From 81910a9e87336abe3e0e30f5f724f99344270885 Mon Sep 17 00:00:00 2001 From: zouinkhim Date: Wed, 28 Jan 2026 08:52:41 -0500 Subject: [PATCH 01/15] support inner model process --- cellmap_flow/blockwise/blockwise_processor.py | 49 +++++++++++++++---- cellmap_flow/utils/config_utils.py | 9 ++++ cellmap_flow/utils/generate_neuroglancer.py | 39 +++++++++++++++ example/model_spec.py | 1 + example/model_spec_run07.py | 3 +- example/model_spec_run07_7hk.py | 2 +- neuroglancer_utils.py | 2 +- pyproject.toml | 3 +- 8 files changed, 94 insertions(+), 14 deletions(-) create mode 100644 cellmap_flow/utils/generate_neuroglancer.py diff --git a/cellmap_flow/blockwise/blockwise_processor.py b/cellmap_flow/blockwise/blockwise_processor.py index 32e10b9..0de5a38 100644 --- a/cellmap_flow/blockwise/blockwise_processor.py +++ b/cellmap_flow/blockwise/blockwise_processor.py @@ -109,6 +109,20 @@ def __init__(self, yaml_config: str, create=False): f"Using {len(models)} models with merge mode: {self.model_mode}" ) + # Support cross-channel processing + self.process_only = self.config.get("process_only", None) + self.cross_channels = self.config.get("cross_channels", None) + + if self.cross_channels and self.cross_channels not in ["AND", "OR", "SUM"]: + raise Exception( + f"Invalid cross_channels: {self.cross_channels}. Must be one of: AND, OR, SUM" + ) + + if self.process_only: + logger.info( + f"Processing only channels: {self.process_only} with merge mode: {self.cross_channels}" + ) + self.model_config = models[0] # this is zyx @@ -133,6 +147,9 @@ def __init__(self, yaml_config: str, create=False): self.output_channel_names = self.channels self.output_channel_indices = None + if not isinstance(self.output_channels, list): + self.output_channels = [self.output_channels] + if json_data: g.input_norms, g.postprocess = get_process_dataset(json_data) @@ -161,6 +178,12 @@ def __init__(self, yaml_config: str, create=False): # Ensure we have output channels to iterate over channels_to_create = self.output_channels if self.output_channels else [] + if not isinstance(channels_to_create, list): + channels_to_create = [channels_to_create] + + # check if there is two channels_to_create with same name + if len(channels_to_create) != len(set(channels_to_create)): + raise Exception(f"output_channels has duplicated channel names. channels: {channels_to_create}") for channel in channels_to_create: if create: @@ -302,6 +325,11 @@ def process_fn(self, block): model_outputs = [] for inferencer in self.inferencers: output = inferencer.process_chunk(self.idi_raw, block.write_roi) + if self.process_only and self.cross_channels: + # Extract only the specified channels + channel_outputs = [output[ch_idx] for ch_idx in self.process_only] + # Merge the extracted channels based on cross_channels mode + output = self._merge_model_outputs(channel_outputs, mode=self.cross_channels) model_outputs.append(output) # Merge outputs based on model_mode @@ -372,37 +400,40 @@ def process_fn(self, block): continue array[array_write_roi] = predictions.to_ndarray(array_write_roi) - def _merge_model_outputs(self, model_outputs): + def _merge_model_outputs(self, model_outputs, mode=None): """ - Merge outputs from multiple models based on the configured model_mode. + Merge outputs from multiple models or channels based on the configured mode. Args: - model_outputs: List of numpy arrays from different models + model_outputs: List of numpy arrays from different models or channels + mode: Merge mode (AND, OR, SUM). If None, uses self.model_mode Returns: Merged numpy array """ - if self.model_mode == "AND": + merge_mode = mode if mode else self.model_mode + + if merge_mode == "AND": # Element-wise minimum (logical AND for binary, minimum for continuous) merged = model_outputs[0] for output in model_outputs[1:]: merged = np.minimum(merged, output) return merged - elif self.model_mode == "OR": + elif merge_mode == "OR": # Element-wise maximum (logical OR for binary, maximum for continuous) merged = model_outputs[0] for output in model_outputs[1:]: merged = np.maximum(merged, output) return merged - elif self.model_mode == "SUM": - # Sum all outputs and normalize by number of models + elif merge_mode == "SUM": + # Sum all outputs and normalize by number of models/channels merged = np.sum(model_outputs, axis=0) / len(model_outputs) return merged else: - raise ValueError(f"Unknown model_mode: {self.model_mode}") + raise ValueError(f"Unknown merge mode: {merge_mode}") def client(self): client = daisy.Client() @@ -484,7 +515,7 @@ def run_worker(): f"prediction_logs/out.out", "-e", f"prediction_logs/out.err", - "cellmap_flow_blockwise_processor", + "cellmap_flow_blockwise", f"{yaml_config}", "--client", ] diff --git a/cellmap_flow/utils/config_utils.py b/cellmap_flow/utils/config_utils.py index 2e71442..cc250f2 100644 --- a/cellmap_flow/utils/config_utils.py +++ b/cellmap_flow/utils/config_utils.py @@ -220,6 +220,15 @@ def build_models(model_entries: Dict[str, Dict[str, Any]]) -> List[ModelConfig]: """ models = [] + if isinstance(model_entries, list): + entries = {} + for entry in model_entries: + if "name" not in entry: + raise ValueError("Each model entry in the list must have a 'name' field.") + entries[entry["name"]] = entry + model_entries = entries + + for model_name, entry in model_entries.items(): model = build_model_from_entry(entry, model_name=model_name) models.append(model) diff --git a/cellmap_flow/utils/generate_neuroglancer.py b/cellmap_flow/utils/generate_neuroglancer.py new file mode 100644 index 0000000..fbd621a --- /dev/null +++ b/cellmap_flow/utils/generate_neuroglancer.py @@ -0,0 +1,39 @@ +import os +import zarr +import yaml +import neuroglancer +from cellmap_flow.utils.scale_pyramid import get_raw_layer + +neuroglancer.set_server_bind_address("0.0.0.0") + +def generate_neuroglancer(yaml_path): + with open(yaml_path, "r") as f: + config = yaml.safe_load(f) + predictions = [f for f in os.listdir(config["output_path"]) if os.path.isdir(os.path.join(config["output_path"], f))] + output_path = os.path.join(config["output_path"]) + raw_path = config["data_path"] + + viewer = neuroglancer.Viewer() + with viewer.txn() as s: + s.layers["raw"] = get_raw_layer(raw_path) + for pred in predictions: + s.layers[pred] = get_raw_layer(os.path.join(output_path, pred)) + + viewer_url = str(viewer) + print("viewer", viewer_url) + input("Press Enter to continue...") + return viewer_url + + +def fix_all(yaml_path): + with open(yaml_path, "r") as f: + config = yaml.safe_load(f) + predictions = [os.path.join(config["output_path"], f) for f in os.listdir(config["output_path"]) if os.path.isdir(os.path.join(config["output_path"], f))] + for pred_path in predictions: + c_path = pred_path + while ".zarr" in c_path: + print("Fixing ", c_path) + zarr.open(c_path, mode="a") + c_path = os.path.dirname(c_path) + + \ No newline at end of file diff --git a/example/model_spec.py b/example/model_spec.py index 0a1633a..1078e99 100644 --- a/example/model_spec.py +++ b/example/model_spec.py @@ -25,6 +25,7 @@ def load_eval_model(num_labels, checkpoint_path): model.eval() return model +# classes = ["mito","er","nuc"," pm"," ves","ld"] CHECKPOINT_PATH = "/nrs/saalfeld/heinrichl/fly_organelles/run08/model_checkpoint_438000" output_channels = 8 # 0:all_mem,1:organelle,2:mito,3:er,4:nucleus,5:pm,6:vs,7:ld model = load_eval_model(output_channels, CHECKPOINT_PATH) diff --git a/example/model_spec_run07.py b/example/model_spec_run07.py index 756d809..2728289 100644 --- a/example/model_spec_run07.py +++ b/example/model_spec_run07.py @@ -25,8 +25,7 @@ def load_eval_model(num_labels, checkpoint_path): model.eval() return model - -classes = ["mito","er","nuc"," pm"," ves","ld"] +classes = ["all_mem","organelle","mito","er","nuc","pm","vesicle","ld"] CHECKPOINT_PATH = "/nrs/saalfeld/heinrichl/fly_organelles/run07/model_checkpoint_432000" # output_channels = len(classes) output_channels = 8 diff --git a/example/model_spec_run07_7hk.py b/example/model_spec_run07_7hk.py index 6e29478..8404b77 100644 --- a/example/model_spec_run07_7hk.py +++ b/example/model_spec_run07_7hk.py @@ -26,7 +26,7 @@ def load_eval_model(num_labels, checkpoint_path): return model -classes = ["mito","er","nuc"," pm"," ves","ld"] +classes = ["all_mem","organelle","mito","er","nuc","pm","vesicle","ld"] CHECKPOINT_PATH = "/nrs/saalfeld/heinrichl/fly_organelles/run07/model_checkpoint_700000" # output_channels = len(classes) output_channels = 8 diff --git a/neuroglancer_utils.py b/neuroglancer_utils.py index 9d27822..ad9e052 100644 --- a/neuroglancer_utils.py +++ b/neuroglancer_utils.py @@ -70,7 +70,7 @@ def generate_neuroglancer_url(dataset_path, extras=[]): host = job.host color = next(color_cycle) s.layers[model] = neuroglancer.ImageLayer( - source=f"n5://{host}/{model}{ARGS_KEY}{st_data}{ARGS_KEY}", + source=f"zarr://{host}/{model}{ARGS_KEY}{st_data}{ARGS_KEY}", shader="""#uicontrol int rChan slider(min=0, max=7, step=1, default=0); #uicontrol int gChan slider(min=0, max=7, step=1, default=1); #uicontrol int bChan slider(min=0, max=7, step=1, default=2); diff --git a/pyproject.toml b/pyproject.toml index 3a8539d..728ac79 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,8 @@ dependencies = [ "flasgger", "numcodecs==0.15.0", "zarr==2.18.4", - "xarray==2024.7.0", + # "xarray==2024.7.0", + "xarray==2023.12.0", "h5py", "s3fs", "torch", From 300c2eb71509dd5a1d7094af7256bfec877ac7e9 Mon Sep 17 00:00:00 2001 From: zouinkhim Date: Wed, 28 Jan 2026 11:20:13 -0500 Subject: [PATCH 02/15] pipeline builder --- cellmap_flow/dashboard/PIPELINE_BUILDER.md | 222 +++++ cellmap_flow/dashboard/app.py | 128 +++ cellmap_flow/dashboard/package.json | 21 + .../dashboard/static/js/bundle-pipeline.js | 324 +++++++ .../dashboard/static/js/index-pipeline.js | 20 + .../dashboard/static/js/pipeline-builder.js | 170 ++++ .../dashboard/static/js/pipeline-exporter.js | 279 ++++++ .../dashboard/static/js/pipeline-nodes.js | 36 + .../dashboard/static/js/pipeline-toolbar.js | 53 ++ .../static/js/react-dom.production.min.js | 267 ++++++ .../static/js/react-dom.production.min.js.1 | 267 ++++++ .../static/js/react.production.min.js | 31 + .../static/js/react.production.min.js.1 | 31 + cellmap_flow/dashboard/templates/index.html | 1 + .../dashboard/templates/pipeline_builder.html | 882 ++++++++++++++++++ cellmap_flow/globals.py | 14 +- check_app.sh | 2 + example/example_pipeline.yaml | 26 + jrc_mus-salivary-1_mito.yaml | 20 + pyproject.toml | 4 +- 20 files changed, 2789 insertions(+), 9 deletions(-) create mode 100644 cellmap_flow/dashboard/PIPELINE_BUILDER.md create mode 100644 cellmap_flow/dashboard/package.json create mode 100644 cellmap_flow/dashboard/static/js/bundle-pipeline.js create mode 100644 cellmap_flow/dashboard/static/js/index-pipeline.js create mode 100644 cellmap_flow/dashboard/static/js/pipeline-builder.js create mode 100644 cellmap_flow/dashboard/static/js/pipeline-exporter.js create mode 100644 cellmap_flow/dashboard/static/js/pipeline-nodes.js create mode 100644 cellmap_flow/dashboard/static/js/pipeline-toolbar.js create mode 100644 cellmap_flow/dashboard/static/js/react-dom.production.min.js create mode 100644 cellmap_flow/dashboard/static/js/react-dom.production.min.js.1 create mode 100644 cellmap_flow/dashboard/static/js/react.production.min.js create mode 100644 cellmap_flow/dashboard/static/js/react.production.min.js.1 create mode 100644 cellmap_flow/dashboard/templates/pipeline_builder.html create mode 100644 check_app.sh create mode 100644 example/example_pipeline.yaml create mode 100644 jrc_mus-salivary-1_mito.yaml diff --git a/cellmap_flow/dashboard/PIPELINE_BUILDER.md b/cellmap_flow/dashboard/PIPELINE_BUILDER.md new file mode 100644 index 0000000..553d193 --- /dev/null +++ b/cellmap_flow/dashboard/PIPELINE_BUILDER.md @@ -0,0 +1,222 @@ +# CellMapFlow Pipeline Builder + +A drag-and-drop visual pipeline builder for creating and managing CellMapFlow inference workflows. + +## Features + +- **Visual Pipeline Editor**: Drag-and-drop interface to connect normalization models and postprocessors +- **Node-based Workflow**: Connect input → normalizers → postprocessors → output +- **Parameter Configuration**: Set parameters for each processing step +- **Export/Import**: Save pipelines as YAML or JSON and reload them later +- **Real-time Validation**: Validate pipelines before applying them + +## Architecture + +### Frontend (React Flow) + +The pipeline builder uses **React Flow** for the visual node editor: + +**Components:** +- [pipeline-builder.js](static/js/pipeline-builder.js) - Main React component +- [pipeline-nodes.js](static/js/pipeline-nodes.js) - Node styling and layout +- [pipeline-toolbar.js](static/js/pipeline-toolbar.js) - Toolbar for adding nodes +- [pipeline-exporter.js](static/js/pipeline-exporter.js) - Export/Import functionality +- [pipeline_builder.html](templates/pipeline_builder.html) - HTML template + +### Backend (Flask) + +Flask routes handle validation and application of pipelines: + +**Endpoints:** +- `GET /pipeline-builder` - Render the pipeline builder UI +- `POST /api/pipeline/validate` - Validate pipeline configuration +- `POST /api/pipeline/apply` - Apply pipeline to inference + +## File Structure + +``` +cellmap_flow/dashboard/ +├── app.py # Flask app with pipeline routes +├── package.json # npm dependencies +├── static/ +│ └── js/ +│ ├── pipeline-builder.js # Main React component +│ ├── pipeline-nodes.js # Node components +│ ├── pipeline-toolbar.js # Toolbar component +│ ├── pipeline-exporter.js # Export/import logic +│ └── index-pipeline.js # Entry point +└── templates/ + └── pipeline_builder.html # HTML template +``` + +## Usage + +### Access the Pipeline Builder + +Navigate to: `http://localhost:PORT/pipeline-builder` + +### Creating a Pipeline + +1. **Add Normalizers**: Select from dropdown in toolbar to add normalization steps +2. **Add Postprocessors**: Select from dropdown to add post-processing steps +3. **Connect Nodes**: Drag from output handle to input handle to connect steps +4. **Configure Parameters**: Click a node and enter JSON parameters in the panel +5. **Apply**: Use the "Apply Pipeline" button to use the configuration + +### Exporting a Pipeline + +**As YAML:** +```yaml +input_normalizers: + - name: StandardNormalizer + params: + mean: 0.5 + std: 0.1 +postprocessors: + - name: InstanceSegmentation + params: + threshold: 0.5 +``` + +**As JSON:** +```json +{ + "nodes": [...], + "edges": [...], + "timestamp": "2024-01-28T..." +} +``` + +### Importing a Pipeline + +Click "Import Pipeline" and select a previously saved `.yaml` or `.json` file. + +## Configuration + +### Node Types + +- **Input** (green): Data entry point +- **Normalizer** (blue): Input normalization steps +- **Postprocessor** (pink): Post-processing operations +- **Output** (gold): Final output + +### Parameter Format + +Parameters are specified as JSON. Examples: + +```json +{ + "clip_min": -1.0, + "clip_max": 1.0, + "bias": 1.0, + "multiplier": 127.5 +} +``` + +## Integration with CellMapFlow + +The pipeline builder integrates with your existing CellMapFlow infrastructure: + +1. **Normalizers**: Uses `get_input_normalizers()` from [input_normalize.py](../norm/input_normalize.py) +2. **Postprocessors**: Uses `get_postprocessors_list()` from [postprocessors.py](../post/postprocessors.py) +3. **Validation**: Validates against available models before applying +4. **Application**: Applies to global state (`g.input_norms`, `g.postprocess`) + +## API Reference + +### GET /pipeline-builder + +Returns the pipeline builder UI with available normalizers and postprocessors. + +**Response:** +- HTML page with React Flow editor + +### POST /api/pipeline/validate + +Validates a pipeline configuration. + +**Request:** +```json +{ + "input_normalizers": [ + { "name": "StandardNormalizer", "params": {...} } + ], + "postprocessors": [ + { "name": "InstanceSegmentation", "params": {...} } + ] +} +``` + +**Response:** +```json +{ + "valid": true, + "message": "Pipeline is valid" +} +``` + +### POST /api/pipeline/apply + +Applies a validated pipeline to the current inference. + +**Request:** Same as validate + +**Response:** +```json +{ + "message": "Pipeline applied successfully", + "normalizers_applied": 2, + "postprocessors_applied": 1 +} +``` + +## Development + +### Setup + +```bash +cd cellmap_flow/dashboard +npm install +npm run build # Build for production +npm run dev # Watch mode for development +``` + +### Building the Frontend + +React Flow components are bundled using webpack. Run `npm run build` to generate the bundled JavaScript. + +## Dependencies + +### Frontend +- **React** 18.2+ - UI library +- **React DOM** 18.2+ - DOM rendering +- **React Flow** 11.10+ - Node-based visual editor + +### Backend +- **Flask** - Web framework (already in CellMapFlow) +- **Pydantic** - Configuration validation (already in CellMapFlow) + +## Future Enhancements + +- [ ] Real-time pipeline preview/simulation +- [ ] Custom node templates for complex operations +- [ ] Pipeline library/templates for common workflows +- [ ] Performance profiling for pipeline execution +- [ ] Undo/Redo functionality +- [ ] Keyboard shortcuts for faster node creation +- [ ] Search/filter for large model lists + +## Troubleshooting + +### Pipeline not applying +- Check browser console for errors +- Verify all node names match available normalizers/postprocessors +- Ensure JSON parameter format is valid + +### Import fails +- Ensure file is valid YAML or JSON +- Check that all referenced models exist in your installation + +### Styling issues +- Clear browser cache +- Rebuild with `npm run build` diff --git a/cellmap_flow/dashboard/app.py b/cellmap_flow/dashboard/app.py index 901a1e9..8450fb3 100644 --- a/cellmap_flow/dashboard/app.py +++ b/cellmap_flow/dashboard/app.py @@ -68,6 +68,46 @@ def index(): ) +@app.route("/pipeline-builder") +def pipeline_builder(): + """Render the drag-and-drop pipeline builder interface with current state from globals""" + input_norms = get_input_normalizers() + output_postprocessors = get_postprocessors_list() + + # Convert current pipeline state from globals to dict format with unique IDs + current_normalizers = [] + for idx, norm in enumerate(g.input_norms): + norm_dict = norm.to_dict() if hasattr(norm, 'to_dict') else {'name': str(norm)} + norm_name = norm_dict.get('name', str(norm)) + # Extract params: all dict items except 'name' + params = {k: v for k, v in norm_dict.items() if k != 'name'} + current_normalizers.append({ + 'id': f'norm-{idx}-{int(time.time()*1000)}', + 'name': norm_name, + 'params': params + }) + + current_postprocessors = [] + for idx, post in enumerate(g.postprocess): + post_dict = post.to_dict() if hasattr(post, 'to_dict') else {'name': str(post)} + post_name = post_dict.get('name', str(post)) + # Extract params: all dict items except 'name' + params = {k: v for k, v in post_dict.items() if k != 'name'} + current_postprocessors.append({ + 'id': f'post-{idx}-{int(time.time()*1000)}', + 'name': post_name, + 'params': params + }) + + return render_template( + "pipeline_builder.html", + input_normalizers=input_norms or {}, + output_postprocessors=output_postprocessors or {}, + current_normalizers=current_normalizers, + current_postprocessors=current_postprocessors, + ) + + def is_output_segmentation(): if len(g.postprocess) == 0: return False @@ -177,6 +217,94 @@ def process(): ) +@app.route("/api/pipeline/validate", methods=["POST"]) +def validate_pipeline(): + """Validate a pipeline configuration""" + try: + data = request.get_json() + + # Validate normalizers + normalizer_names = [n.get("name") for n in data.get("input_normalizers", [])] + available_norms = get_input_normalizers() + for norm_name in normalizer_names: + if norm_name not in available_norms: + return jsonify( + {"valid": False, "error": f"Unknown normalizer: {norm_name}"} + ), 400 + + # Validate postprocessors + processor_names = [p.get("name") for p in data.get("postprocessors", [])] + available_procs = get_postprocessors_list() + for proc_name in processor_names: + if proc_name not in available_procs: + return jsonify( + {"valid": False, "error": f"Unknown postprocessor: {proc_name}"} + ), 400 + + return jsonify({"valid": True, "message": "Pipeline is valid"}) + + except Exception as e: + logger.error(f"Error validating pipeline: {e}") + return jsonify({"valid": False, "error": str(e)}), 500 + + +@app.route("/api/pipeline/apply", methods=["POST"]) +def apply_pipeline(): + """Apply a pipeline configuration to the current inference""" + try: + data = request.get_json() + + # Validate first + validation = validate_pipeline_config(data) + if not validation["valid"]: + return jsonify(validation), 400 + + # Apply normalizers + input_norms_config = { + n["name"]: n.get("params", {}) for n in data.get("input_normalizers", []) + } + g.input_norms = get_normalizations(input_norms_config) + + # Apply postprocessors + postprocs_config = { + p["name"]: p.get("params", {}) for p in data.get("postprocessors", []) + } + g.postprocess = get_postprocessors(postprocs_config) + + logger.warning(f"Pipeline applied: {len(g.input_norms)} normalizers, {len(g.postprocess)} postprocessors") + + return jsonify({ + "message": "Pipeline applied successfully", + "normalizers_applied": len(g.input_norms), + "postprocessors_applied": len(g.postprocess), + }) + + except Exception as e: + logger.error(f"Error applying pipeline: {e}") + return jsonify({"error": str(e)}), 500 + + +def validate_pipeline_config(config): + """Helper function to validate pipeline configuration""" + try: + normalizer_names = [n.get("name") for n in config.get("input_normalizers", [])] + available_norms = get_input_normalizers() + for norm_name in normalizer_names: + if norm_name not in available_norms: + return {"valid": False, "error": f"Unknown normalizer: {norm_name}"} + + processor_names = [p.get("name") for p in config.get("postprocessors", [])] + available_procs = get_postprocessors_list() + for proc_name in processor_names: + if proc_name not in available_procs: + return {"valid": False, "error": f"Unknown postprocessor: {proc_name}"} + + return {"valid": True} + + except Exception as e: + return {"valid": False, "error": str(e)} + + def create_and_run_app(neuroglancer_url=None, inference_servers=None): global NEUROGLANCER_URL, INFERENCE_SERVER NEUROGLANCER_URL = neuroglancer_url diff --git a/cellmap_flow/dashboard/package.json b/cellmap_flow/dashboard/package.json new file mode 100644 index 0000000..168dba6 --- /dev/null +++ b/cellmap_flow/dashboard/package.json @@ -0,0 +1,21 @@ +{ + "name": "cellmap-flow-pipeline-builder", + "version": "0.1.0", + "description": "Drag-and-drop pipeline builder for CellMapFlow", + "dependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0", + "reactflow": "^11.10.0" + }, + "devDependencies": { + "@babel/preset-react": "^7.22.0", + "@babel/preset-env": "^7.22.0", + "webpack": "^5.88.0", + "webpack-cli": "^5.1.0", + "babel-loader": "^9.1.3" + }, + "scripts": { + "build": "webpack --mode production", + "dev": "webpack --mode development --watch" + } +} diff --git a/cellmap_flow/dashboard/static/js/bundle-pipeline.js b/cellmap_flow/dashboard/static/js/bundle-pipeline.js new file mode 100644 index 0000000..381a974 --- /dev/null +++ b/cellmap_flow/dashboard/static/js/bundle-pipeline.js @@ -0,0 +1,324 @@ +// In-browser bundle for the pipeline builder — uses UMD React and ReactFlow globals +// This file is transpiled by Babel in the browser (development convenience). + +// Defer accessing ReactFlow UMD globals until runtime to avoid errors +let ReactFlowProvider, ReactFlow, addEdge, useNodesState, useEdgesState, Controls, Background, useReactFlow, Handle, Position; +const { useCallback, useState } = React; + +const nodeTypes = { + normalizer: PipelineNode, + postprocessor: PipelineNode, + input: PipelineNode, + output: PipelineNode, +}; + +function PipelineNode({ data, selected, id }) { + const nodeColors = { + input: '#90EE90', + normalizer: '#87CEEB', + postprocessor: '#FFB6C1', + output: '#FFD700', + }; + const bgColor = nodeColors[data.type] || '#fff'; + + return ( + React.createElement('div', { + style: { + padding: '10px', + border: `2px ${selected ? 'blue' : '#ddd'} solid`, + borderRadius: '8px', + background: bgColor, + minWidth: '120px', + textAlign: 'center', + fontWeight: 'bold', + cursor: 'pointer', + }, + }, + data.type !== 'input' && React.createElement(Handle, { type: 'target', position: Position.Top }), + React.createElement('div', null, data.label), + data.type !== 'output' && React.createElement(Handle, { type: 'source', position: Position.Bottom }) + ) + ); +} + +function PipelineToolbar({ + availableNormalizers, + availablePostprocessors, + onAddNormalizer, + onAddPostprocessor, +}) { + return ( + React.createElement('div', { style: { + padding: '10px', + background: '#f5f5f5', + borderBottom: '1px solid #ddd', + display: 'flex', + gap: '10px', + alignItems: 'center', + } }, + React.createElement('label', { style: { fontWeight: 'bold' } }, 'Add Normalizer:'), + React.createElement('select', { + onChange: (e) => { + if (e.target.value) { + onAddNormalizer(e.target.value); + e.target.value = ''; + } + } + }, + React.createElement('option', { value: '' }, 'Select a normalizer...'), + availableNormalizers && Object.keys(availableNormalizers).map((norm) => React.createElement('option', { key: norm, value: norm }, norm)) + ), + + React.createElement('label', { style: { fontWeight: 'bold', marginLeft: '20px' } }, 'Add Postprocessor:'), + React.createElement('select', { + onChange: (e) => { + if (e.target.value) { + onAddPostprocessor(e.target.value); + e.target.value = ''; + } + } + }, + React.createElement('option', { value: '' }, 'Select a postprocessor...'), + availablePostprocessors && Object.keys(availablePostprocessors).map((post) => React.createElement('option', { key: post, value: post }, post)) + ) + ) + ); +} + +function PipelineExporter({ + selectedNode, + nodes, + edges, + onExport, + onImport, + onUpdateParams, + onDeleteNode, +}) { + const [paramInputs, setParamInputs] = useState({}); + const selectedNodeData = nodes.find((n) => n.id === selectedNode); + + const applyParams = () => { + if (selectedNode && selectedNodeData) { + try { + const params = {}; + Object.keys(paramInputs).forEach((key) => { + try { + params[key] = JSON.parse(paramInputs[key]); + } catch { + params[key] = paramInputs[key]; + } + }); + onUpdateParams(selectedNode, params); + setParamInputs({}); + } catch (error) { + alert('Invalid parameter format: ' + error.message); + } + } + }; + + const exportToYAML = () => { + const workflow = { + input_normalizers: [], + postprocessors: [], + }; + nodes.forEach((node) => { + if (node.type === 'normalizer') { + workflow.input_normalizers.push({ name: node.data.name, params: node.data.params || {} }); + } else if (node.type === 'postprocessor') { + workflow.postprocessors.push({ name: node.data.name, params: node.data.params || {} }); + } + }); + const yamlContent = generateYAML(workflow); + downloadFile(yamlContent, 'pipeline.yaml', 'text/yaml'); + }; + + const exportToJSON = () => { + const pipelineData = onExport(); + const jsonContent = JSON.stringify(pipelineData, null, 2); + downloadFile(jsonContent, 'pipeline.json', 'application/json'); + }; + + const importFromFile = (event) => { + const file = event.target.files[0]; + if (file) { + const reader = new FileReader(); + reader.onload = (e) => { + try { + const content = e.target.result; + const data = file.name.endsWith('.yaml') ? parseYAML(content) : JSON.parse(content); + onImport(data); + } catch (error) { + alert('Error importing file: ' + error.message); + } + }; + reader.readAsText(file); + } + }; + + return ( + React.createElement('div', { style: { + width: '300px', + padding: '15px', + background: '#fff', + borderLeft: '1px solid #ddd', + overflowY: 'auto', + display: 'flex', + flexDirection: 'column', + } }, + React.createElement('h3', null, 'Pipeline Controls'), + selectedNodeData && selectedNodeData.type !== 'input' && selectedNodeData.type !== 'output' && ( + React.createElement('div', { style: { marginBottom: '20px', padding: '10px', background: '#f9f9f9', borderRadius: '5px' } }, + React.createElement('h4', { style: { marginTop: 0 } }, selectedNodeData.data.label), + React.createElement('p', { style: { fontSize: '12px', color: '#666' } }, `Node ID: ${selectedNode}`), + React.createElement('label', { style: { fontWeight: 'bold', fontSize: '12px' } }, 'Parameters:'), + React.createElement('div', { style: { marginTop: '8px', marginBottom: '10px' } }, + React.createElement('input', { + type: 'text', + placeholder: '{"param1": 0.5}', + onChange: (e) => setParamInputs({ param_input: e.target.value }), + style: { width: '100%', padding: '5px', fontSize: '11px' }, + }) + ), + React.createElement('button', { onClick: applyParams, style: { width: '100%', padding: '6px', background: '#4CAF50', color: 'white', border: 'none', borderRadius: '4px', cursor: 'pointer', fontSize: '12px', marginBottom: '8px' } }, 'Apply Parameters'), + React.createElement('button', { onClick: () => onDeleteNode(selectedNode), style: { width: '100%', padding: '6px', background: '#f44336', color: 'white', border: 'none', borderRadius: '4px', cursor: 'pointer', fontSize: '12px' } }, 'Delete Node') + ) + ), + React.createElement('div', { style: { marginTop: 'auto' } }, + React.createElement('h4', null, 'Export Pipeline'), + React.createElement('button', { onClick: exportToYAML, style: { width: '100%', padding: '8px', background: '#2196F3', color: 'white', border: 'none', borderRadius: '4px', cursor: 'pointer', marginBottom: '8px' } }, 'Export as YAML'), + React.createElement('button', { onClick: exportToJSON, style: { width: '100%', padding: '8px', background: '#2196F3', color: 'white', border: 'none', borderRadius: '4px', cursor: 'pointer', marginBottom: '8px' } }, 'Export as JSON'), + React.createElement('h4', { style: { marginTop: '15px' } }, 'Import Pipeline'), + React.createElement('input', { type: 'file', accept: '.yaml,.json', onChange: importFromFile, style: { width: '100%', fontSize: '12px' } }) + ) + ) + ); +} + +function PipelineBuilder({ availableNormalizers, availablePostprocessors }) { + const [nodes, setNodes, onNodesChange] = useNodesState([ + { id: 'input', data: { label: 'Input Data', type: 'input' }, position: { x: 0, y: 0 }, type: 'input' }, + { id: 'output', data: { label: 'Output', type: 'output' }, position: { x: 400, y: 300 }, type: 'output' }, + ]); + const [edges, setEdges, onEdgesChange] = useEdgesState([]); + const [selectedNode, setSelectedNode] = useState(null); + const reactFlowInstance = useReactFlow(); + + const onConnectCb = useCallback((connection) => setEdges((eds) => addEdge(connection, eds)), [setEdges]); + + const addNormalizer = useCallback((normalizerName) => { + const id = `norm-${Date.now()}`; + const newNode = { id, type: 'normalizer', data: { label: normalizerName, type: 'normalizer', name: normalizerName, params: {} }, position: { x: 100, y: 150 } }; + setNodes((nds) => [...nds, newNode]); + }, [setNodes]); + + const addPostprocessor = useCallback((processorName) => { + const id = `post-${Date.now()}`; + const newNode = { id, type: 'postprocessor', data: { label: processorName, type: 'postprocessor', name: processorName, params: {} }, position: { x: 250, y: 150 } }; + setNodes((nds) => [...nds, newNode]); + }, [setNodes]); + + const updateNodeParams = useCallback((nodeId, params) => setNodes((nds) => nds.map((node) => node.id === nodeId ? { ...node, data: { ...node.data, params } } : node)), [setNodes]); + + const deleteNode = useCallback((nodeId) => { setNodes((nds) => nds.filter((node) => node.id !== nodeId)); setEdges((eds) => eds.filter((edge) => edge.source !== nodeId && edge.target !== nodeId)); }, [setNodes, setEdges]); + + const exportPipeline = useCallback(() => ({ nodes, edges, timestamp: new Date().toISOString() }), [nodes, edges]); + const importPipeline = useCallback((pipelineData) => { if (pipelineData.nodes) setNodes(pipelineData.nodes); if (pipelineData.edges) setEdges(pipelineData.edges); }, [setNodes, setEdges]); + + return ( + React.createElement('div', { style: { width: '100%', height: '100vh', display: 'flex', flexDirection: 'column' } }, + React.createElement(PipelineToolbar, { availableNormalizers, availablePostprocessors, onAddNormalizer: addNormalizer, onAddPostprocessor: addPostprocessor }), + React.createElement('div', { style: { flex: 1, display: 'flex' } }, + React.createElement('div', { style: { flex: 1 } }, + React.createElement(ReactFlow, { nodes, edges, onNodesChange, onEdgesChange, onConnect: onConnectCb, nodeTypes, onNodeClick: (event, node) => setSelectedNode(node.id) }, + React.createElement(Background, null), + React.createElement(Controls, null) + ) + ), + React.createElement(PipelineExporter, { selectedNode, nodes, edges, onExport: exportPipeline, onImport: importPipeline, onUpdateParams: updateNodeParams, onDeleteNode: deleteNode }) + ) + ) + ); +} + +// Helpers +function generateYAML(data) { + let yaml = ''; + yaml += 'input_normalizers:\n'; + data.input_normalizers.forEach((norm) => { + yaml += ` - name: ${norm.name}\n`; + yaml += ' params:\n'; + Object.keys(norm.params).forEach((key) => { yaml += ` ${key}: ${JSON.stringify(norm.params[key])}\n`; }); + }); + yaml += 'postprocessors:\n'; + data.postprocessors.forEach((post) => { + yaml += ` - name: ${post.name}\n`; + yaml += ' params:\n'; + Object.keys(post.params).forEach((key) => { yaml += ` ${key}: ${JSON.stringify(post.params[key])}\n`; }); + }); + return yaml; +} + +function parseYAML(yaml) { + const lines = yaml.split('\n'); + const result = { input_normalizers: [], postprocessors: [] }; + let currentSection = null; + let currentItem = null; + lines.forEach((line) => { + const trimmed = line.trim(); + if (trimmed.startsWith('input_normalizers:')) currentSection = 'input_normalizers'; + else if (trimmed.startsWith('postprocessors:')) currentSection = 'postprocessors'; + else if (trimmed.startsWith('- name:')) { currentItem = { name: trimmed.replace('- name: ', ''), params: {} }; } + else if (trimmed.startsWith('name:') && !trimmed.startsWith('- name:')) { if (currentItem) currentItem.name = trimmed.replace('name: ', ''); } + else if (trimmed.startsWith('params:')) {} + else if (trimmed && currentItem && !trimmed.startsWith('-')) { + const [key, value] = trimmed.split(':').map((s) => s.trim()); + if (key && value) { + try { currentItem.params[key] = JSON.parse(value); } catch { currentItem.params[key] = value; } + } + } + if ((trimmed.startsWith('- name:') || trimmed.startsWith('name:')) && currentItem && Object.keys(currentItem.params).length > 0) { if (currentSection) result[currentSection].push(currentItem); currentItem = null; } + }); + if (currentItem && currentSection) result[currentSection].push(currentItem); + return result; +} + +function downloadFile(content, filename, mimeType) { + const element = document.createElement('a'); + element.setAttribute('href', 'data:' + mimeType + ';charset=utf-8,' + encodeURIComponent(content)); + element.setAttribute('download', filename); + element.style.display = 'none'; + document.body.appendChild(element); + element.click(); + document.body.removeChild(element); +} + +// Mount app +document.addEventListener('DOMContentLoaded', function () { + const APP_CONFIG = window.APP_CONFIG || { availableNormalizers: {}, availablePostprocessors: {} }; + + // Ensure ReactFlow UMD global is available + const RF = window.ReactFlow; + if (!RF) { + console.error( + 'ReactFlow UMD not found on window.ReactFlow. Ensure ReactFlow script is loaded before bundle.' + ); + return; + } + + // Extract needed ReactFlow exports at runtime + ReactFlowProvider = RF.ReactFlowProvider; + ReactFlow = RF.ReactFlow; + addEdge = RF.addEdge; + useNodesState = RF.useNodesState; + useEdgesState = RF.useEdgesState; + Controls = RF.Controls; + Background = RF.Background; + useReactFlow = RF.useReactFlow; + Handle = RF.Handle; + Position = RF.Position; + + ReactDOM.render( + React.createElement(ReactFlowProvider, null, React.createElement(PipelineBuilder, { availableNormalizers: APP_CONFIG.availableNormalizers, availablePostprocessors: APP_CONFIG.availablePostprocessors })), + document.getElementById('pipeline-root') + ); +}); diff --git a/cellmap_flow/dashboard/static/js/index-pipeline.js b/cellmap_flow/dashboard/static/js/index-pipeline.js new file mode 100644 index 0000000..c0d3d35 --- /dev/null +++ b/cellmap_flow/dashboard/static/js/index-pipeline.js @@ -0,0 +1,20 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { ReactFlowProvider } from 'reactflow'; +import { PipelineBuilder } from './pipeline-builder'; + +// This will be injected by Flask +const APP_CONFIG = window.APP_CONFIG || { + availableNormalizers: {}, + availablePostprocessors: {}, +}; + +ReactDOM.render( + + + , + document.getElementById('pipeline-root') +); diff --git a/cellmap_flow/dashboard/static/js/pipeline-builder.js b/cellmap_flow/dashboard/static/js/pipeline-builder.js new file mode 100644 index 0000000..5c500af --- /dev/null +++ b/cellmap_flow/dashboard/static/js/pipeline-builder.js @@ -0,0 +1,170 @@ +import React, { useCallback, useState, useRef } from 'react'; +import ReactFlow, { + Node, + Edge, + addEdge, + useNodesState, + useEdgesState, + Controls, + Background, + useReactFlow, + NodeTypes, +} from 'reactflow'; +import 'reactflow/dist/style.css'; +import { PipelineNode } from './pipeline-nodes'; +import { PipelineToolbar } from './pipeline-toolbar'; +import { PipelineExporter } from './pipeline-exporter'; + +const nodeTypes = { + normalizer: PipelineNode, + postprocessor: PipelineNode, + input: PipelineNode, + output: PipelineNode, +}; + +export const PipelineBuilder = ({ availableNormalizers, availablePostprocessors }) => { + const [nodes, setNodes, onNodesChange] = useNodesState([ + { + id: 'input', + data: { label: 'Input Data', type: 'input' }, + position: { x: 0, y: 0 }, + type: 'input', + }, + { + id: 'output', + data: { label: 'Output', type: 'output' }, + position: { x: 400, y: 300 }, + type: 'output', + }, + ]); + + const [edges, setEdges, onEdgesChange] = useEdgesState([]); + const [selectedNode, setSelectedNode] = useState(null); + const reactFlowInstance = useReactFlow(); + + const onConnect = useCallback( + (connection) => { + setEdges((eds) => addEdge(connection, eds)); + }, + [setEdges] + ); + + const addNormalizer = useCallback( + (normalizerName) => { + const id = `norm-${Date.now()}`; + const newNode = { + id, + type: 'normalizer', + data: { + label: normalizerName, + type: 'normalizer', + name: normalizerName, + params: {}, + }, + position: { x: 100, y: 150 }, + }; + setNodes((nds) => [...nds, newNode]); + }, + [setNodes] + ); + + const addPostprocessor = useCallback( + (processorName) => { + const id = `post-${Date.now()}`; + const newNode = { + id, + type: 'postprocessor', + data: { + label: processorName, + type: 'postprocessor', + name: processorName, + params: {}, + }, + position: { x: 250, y: 150 }, + }; + setNodes((nds) => [...nds, newNode]); + }, + [setNodes] + ); + + const updateNodeParams = useCallback( + (nodeId, params) => { + setNodes((nds) => + nds.map((node) => + node.id === nodeId + ? { ...node, data: { ...node.data, params } } + : node + ) + ); + }, + [setNodes] + ); + + const deleteNode = useCallback( + (nodeId) => { + setNodes((nds) => nds.filter((node) => node.id !== nodeId)); + setEdges((eds) => + eds.filter((edge) => edge.source !== nodeId && edge.target !== nodeId) + ); + }, + [setNodes, setEdges] + ); + + const exportPipeline = useCallback(() => { + return { + nodes, + edges, + timestamp: new Date().toISOString(), + }; + }, [nodes, edges]); + + const importPipeline = useCallback( + (pipelineData) => { + if (pipelineData.nodes) { + setNodes(pipelineData.nodes); + } + if (pipelineData.edges) { + setEdges(pipelineData.edges); + } + }, + [setNodes, setEdges] + ); + + return ( +
+ + +
+
+ setSelectedNode(node.id)} + > + + + +
+ + +
+
+ ); +}; diff --git a/cellmap_flow/dashboard/static/js/pipeline-exporter.js b/cellmap_flow/dashboard/static/js/pipeline-exporter.js new file mode 100644 index 0000000..ff12b5d --- /dev/null +++ b/cellmap_flow/dashboard/static/js/pipeline-exporter.js @@ -0,0 +1,279 @@ +import React, { useState } from 'react'; + +export const PipelineExporter = ({ + selectedNode, + nodes, + edges, + onExport, + onImport, + onUpdateParams, + onDeleteNode, +}) => { + const [paramInputs, setParamInputs] = useState({}); + + const selectedNodeData = nodes.find((n) => n.id === selectedNode); + + const handleParamChange = (key, value) => { + setParamInputs({ ...paramInputs, [key]: value }); + }; + + const applyParams = () => { + if (selectedNode && selectedNodeData) { + try { + const params = {}; + Object.keys(paramInputs).forEach((key) => { + try { + params[key] = JSON.parse(paramInputs[key]); + } catch { + params[key] = paramInputs[key]; + } + }); + onUpdateParams(selectedNode, params); + setParamInputs({}); + } catch (error) { + alert('Invalid parameter format: ' + error.message); + } + } + }; + + const exportToYAML = () => { + const pipelineData = onExport(); + + // Extract workflow structure + const workflow = { + input_normalizers: [], + postprocessors: [], + }; + + // Order nodes by edges + nodes.forEach((node) => { + if (node.type === 'normalizer') { + workflow.input_normalizers.push({ + name: node.data.name, + params: node.data.params || {}, + }); + } else if (node.type === 'postprocessor') { + workflow.postprocessors.push({ + name: node.data.name, + params: node.data.params || {}, + }); + } + }); + + const yamlContent = generateYAML(workflow); + downloadFile(yamlContent, 'pipeline.yaml', 'text/yaml'); + }; + + const exportToJSON = () => { + const pipelineData = onExport(); + const jsonContent = JSON.stringify(pipelineData, null, 2); + downloadFile(jsonContent, 'pipeline.json', 'application/json'); + }; + + const importFromFile = (event) => { + const file = event.target.files[0]; + if (file) { + const reader = new FileReader(); + reader.onload = (e) => { + try { + const content = e.target.result; + const data = file.name.endsWith('.yaml') + ? parseYAML(content) + : JSON.parse(content); + onImport(data); + } catch (error) { + alert('Error importing file: ' + error.message); + } + }; + reader.readAsText(file); + } + }; + + return ( +
+

Pipeline Controls

+ + {/* Node Inspector */} + {selectedNodeData && selectedNodeData.type !== 'input' && selectedNodeData.type !== 'output' && ( +
+

{selectedNodeData.data.label}

+

Node ID: {selectedNode}

+ + +
+ setParamInputs({ param_input: e.target.value })} + style={{ width: '100%', padding: '5px', fontSize: '11px' }} + /> +
+ + + + +
+ )} + + {/* Export/Import */} +
+

Export Pipeline

+ + + +

Import Pipeline

+ +
+
+ ); +}; + +// Helper functions +const generateYAML = (data) => { + let yaml = ''; + + yaml += 'input_normalizers:\n'; + data.input_normalizers.forEach((norm) => { + yaml += ` - name: ${norm.name}\n`; + yaml += ' params:\n'; + Object.keys(norm.params).forEach((key) => { + yaml += ` ${key}: ${JSON.stringify(norm.params[key])}\n`; + }); + }); + + yaml += 'postprocessors:\n'; + data.postprocessors.forEach((post) => { + yaml += ` - name: ${post.name}\n`; + yaml += ' params:\n'; + Object.keys(post.params).forEach((key) => { + yaml += ` ${key}: ${JSON.stringify(post.params[key])}\n`; + }); + }); + + return yaml; +}; + +const parseYAML = (yaml) => { + // Simple YAML parser for our use case + const lines = yaml.split('\n'); + const result = { + input_normalizers: [], + postprocessors: [], + }; + + let currentSection = null; + let currentItem = null; + + lines.forEach((line) => { + const trimmed = line.trim(); + if (trimmed.startsWith('input_normalizers:')) currentSection = 'input_normalizers'; + else if (trimmed.startsWith('postprocessors:')) currentSection = 'postprocessors'; + else if (trimmed.startsWith('- name:')) { + currentItem = { name: trimmed.replace('- name: ', ''), params: {} }; + } else if (trimmed.startsWith('name:') && !trimmed.startsWith('- name:')) { + if (currentItem) currentItem.name = trimmed.replace('name: ', ''); + } else if (trimmed.startsWith('params:')) { + // params section + } else if (trimmed && currentItem && !trimmed.startsWith('-')) { + const [key, value] = trimmed.split(':').map((s) => s.trim()); + if (key && value) { + try { + currentItem.params[key] = JSON.parse(value); + } catch { + currentItem.params[key] = value; + } + } + } + + // Save item when moving to next + if ((trimmed.startsWith('- name:') || trimmed.startsWith('name:')) && currentItem && Object.keys(currentItem.params).length > 0) { + if (currentSection) result[currentSection].push(currentItem); + currentItem = null; + } + }); + + // Push last item + if (currentItem && currentSection) result[currentSection].push(currentItem); + + return result; +}; + +const downloadFile = (content, filename, mimeType) => { + const element = document.createElement('a'); + element.setAttribute('href', 'data:' + mimeType + ';charset=utf-8,' + encodeURIComponent(content)); + element.setAttribute('download', filename); + element.style.display = 'none'; + document.body.appendChild(element); + element.click(); + document.body.removeChild(element); +}; diff --git a/cellmap_flow/dashboard/static/js/pipeline-nodes.js b/cellmap_flow/dashboard/static/js/pipeline-nodes.js new file mode 100644 index 0000000..2a649cc --- /dev/null +++ b/cellmap_flow/dashboard/static/js/pipeline-nodes.js @@ -0,0 +1,36 @@ +import React from 'react'; +import { Handle, Position } from 'reactflow'; + +const nodeColors = { + input: '#90EE90', + normalizer: '#87CEEB', + postprocessor: '#FFB6C1', + output: '#FFD700', +}; + +export const PipelineNode = ({ data, selected, id }) => { + const bgColor = nodeColors[data.type] || '#fff'; + + return ( +
+ {data.type !== 'input' && ( + + )} +
{data.label}
+ {data.type !== 'output' && ( + + )} +
+ ); +}; diff --git a/cellmap_flow/dashboard/static/js/pipeline-toolbar.js b/cellmap_flow/dashboard/static/js/pipeline-toolbar.js new file mode 100644 index 0000000..fbf6e3e --- /dev/null +++ b/cellmap_flow/dashboard/static/js/pipeline-toolbar.js @@ -0,0 +1,53 @@ +import React from 'react'; + +export const PipelineToolbar = ({ + availableNormalizers, + availablePostprocessors, + onAddNormalizer, + onAddPostprocessor, +}) => { + return ( +
+ + + + + +
+ ); +}; diff --git a/cellmap_flow/dashboard/static/js/react-dom.production.min.js b/cellmap_flow/dashboard/static/js/react-dom.production.min.js new file mode 100644 index 0000000..fb4e099 --- /dev/null +++ b/cellmap_flow/dashboard/static/js/react-dom.production.min.js @@ -0,0 +1,267 @@ +/** + * @license React + * react-dom.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +(function(){/* + Modernizr 3.0.0pre (Custom Build) | MIT +*/ +'use strict';(function(Q,zb){"object"===typeof exports&&"undefined"!==typeof module?zb(exports,require("react")):"function"===typeof define&&define.amd?define(["exports","react"],zb):(Q=Q||self,zb(Q.ReactDOM={},Q.React))})(this,function(Q,zb){function m(a){for(var b="https://reactjs.org/docs/error-decoder.html?invariant="+a,c=1;cb}return!1}function Y(a,b,c,d,e,f,g){this.acceptsBooleans=2===b||3===b||4===b;this.attributeName=d;this.attributeNamespace=e;this.mustUseProperty=c;this.propertyName=a;this.type=b;this.sanitizeURL=f;this.removeEmptyString=g}function $d(a,b,c,d){var e=R.hasOwnProperty(b)?R[b]:null;if(null!==e?0!==e.type:d||!(2h||e[g]!==f[h]){var k="\n"+e[g].replace(" at new "," at ");a.displayName&&k.includes("")&&(k=k.replace("",a.displayName));return k}while(1<=g&&0<=h)}break}}}finally{ce=!1,Error.prepareStackTrace=c}return(a=a?a.displayName||a.name:"")?bc(a): +""}function fj(a){switch(a.tag){case 5:return bc(a.type);case 16:return bc("Lazy");case 13:return bc("Suspense");case 19:return bc("SuspenseList");case 0:case 2:case 15:return a=be(a.type,!1),a;case 11:return a=be(a.type.render,!1),a;case 1:return a=be(a.type,!0),a;default:return""}}function de(a){if(null==a)return null;if("function"===typeof a)return a.displayName||a.name||null;if("string"===typeof a)return a;switch(a){case Bb:return"Fragment";case Cb:return"Portal";case ee:return"Profiler";case fe:return"StrictMode"; +case ge:return"Suspense";case he:return"SuspenseList"}if("object"===typeof a)switch(a.$$typeof){case gg:return(a.displayName||"Context")+".Consumer";case hg:return(a._context.displayName||"Context")+".Provider";case ie:var b=a.render;a=a.displayName;a||(a=b.displayName||b.name||"",a=""!==a?"ForwardRef("+a+")":"ForwardRef");return a;case je:return b=a.displayName||null,null!==b?b:de(a.type)||"Memo";case Ta:b=a._payload;a=a._init;try{return de(a(b))}catch(c){}}return null}function gj(a){var b=a.type; +switch(a.tag){case 24:return"Cache";case 9:return(b.displayName||"Context")+".Consumer";case 10:return(b._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return a=b.render,a=a.displayName||a.name||"",b.displayName||(""!==a?"ForwardRef("+a+")":"ForwardRef");case 7:return"Fragment";case 5:return b;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return de(b);case 8:return b===fe?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler"; +case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if("function"===typeof b)return b.displayName||b.name||null;if("string"===typeof b)return b}return null}function Ua(a){switch(typeof a){case "boolean":case "number":case "string":case "undefined":return a;case "object":return a;default:return""}}function ig(a){var b=a.type;return(a=a.nodeName)&&"input"===a.toLowerCase()&&("checkbox"===b||"radio"=== +b)}function hj(a){var b=ig(a)?"checked":"value",c=Object.getOwnPropertyDescriptor(a.constructor.prototype,b),d=""+a[b];if(!a.hasOwnProperty(b)&&"undefined"!==typeof c&&"function"===typeof c.get&&"function"===typeof c.set){var e=c.get,f=c.set;Object.defineProperty(a,b,{configurable:!0,get:function(){return e.call(this)},set:function(a){d=""+a;f.call(this,a)}});Object.defineProperty(a,b,{enumerable:c.enumerable});return{getValue:function(){return d},setValue:function(a){d=""+a},stopTracking:function(){a._valueTracker= +null;delete a[b]}}}}function Pc(a){a._valueTracker||(a._valueTracker=hj(a))}function jg(a){if(!a)return!1;var b=a._valueTracker;if(!b)return!0;var c=b.getValue();var d="";a&&(d=ig(a)?a.checked?"true":"false":a.value);a=d;return a!==c?(b.setValue(a),!0):!1}function Qc(a){a=a||("undefined"!==typeof document?document:void 0);if("undefined"===typeof a)return null;try{return a.activeElement||a.body}catch(b){return a.body}}function ke(a,b){var c=b.checked;return E({},b,{defaultChecked:void 0,defaultValue:void 0, +value:void 0,checked:null!=c?c:a._wrapperState.initialChecked})}function kg(a,b){var c=null==b.defaultValue?"":b.defaultValue,d=null!=b.checked?b.checked:b.defaultChecked;c=Ua(null!=b.value?b.value:c);a._wrapperState={initialChecked:d,initialValue:c,controlled:"checkbox"===b.type||"radio"===b.type?null!=b.checked:null!=b.value}}function lg(a,b){b=b.checked;null!=b&&$d(a,"checked",b,!1)}function le(a,b){lg(a,b);var c=Ua(b.value),d=b.type;if(null!=c)if("number"===d){if(0===c&&""===a.value||a.value!= +c)a.value=""+c}else a.value!==""+c&&(a.value=""+c);else if("submit"===d||"reset"===d){a.removeAttribute("value");return}b.hasOwnProperty("value")?me(a,b.type,c):b.hasOwnProperty("defaultValue")&&me(a,b.type,Ua(b.defaultValue));null==b.checked&&null!=b.defaultChecked&&(a.defaultChecked=!!b.defaultChecked)}function mg(a,b,c){if(b.hasOwnProperty("value")||b.hasOwnProperty("defaultValue")){var d=b.type;if(!("submit"!==d&&"reset"!==d||void 0!==b.value&&null!==b.value))return;b=""+a._wrapperState.initialValue; +c||b===a.value||(a.value=b);a.defaultValue=b}c=a.name;""!==c&&(a.name="");a.defaultChecked=!!a._wrapperState.initialChecked;""!==c&&(a.name=c)}function me(a,b,c){if("number"!==b||Qc(a.ownerDocument)!==a)null==c?a.defaultValue=""+a._wrapperState.initialValue:a.defaultValue!==""+c&&(a.defaultValue=""+c)}function Db(a,b,c,d){a=a.options;if(b){b={};for(var e=0;e>>=0;return 0===a?32:31-(qj(a)/rj|0)|0}function hc(a){switch(a&-a){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return a& +4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return a&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return a}}function Vc(a,b){var c=a.pendingLanes;if(0===c)return 0;var d=0,e=a.suspendedLanes,f=a.pingedLanes,g=c&268435455;if(0!==g){var h=g&~e;0!==h?d=hc(h):(f&=g,0!==f&&(d=hc(f)))}else g=c&~e,0!==g?d=hc(g):0!==f&&(d=hc(f));if(0===d)return 0;if(0!==b&&b!==d&&0===(b&e)&& +(e=d&-d,f=b&-b,e>=f||16===e&&0!==(f&4194240)))return b;0!==(d&4)&&(d|=c&16);b=a.entangledLanes;if(0!==b)for(a=a.entanglements,b&=d;0c;c++)b.push(a); +return b}function ic(a,b,c){a.pendingLanes|=b;536870912!==b&&(a.suspendedLanes=0,a.pingedLanes=0);a=a.eventTimes;b=31-ta(b);a[b]=c}function uj(a,b){var c=a.pendingLanes&~b;a.pendingLanes=b;a.suspendedLanes=0;a.pingedLanes=0;a.expiredLanes&=b;a.mutableReadLanes&=b;a.entangledLanes&=b;b=a.entanglements;var d=a.eventTimes;for(a=a.expirationTimes;0=b)return{node:c,offset:b-a};a=d}a:{for(;c;){if(c.nextSibling){c=c.nextSibling;break a}c=c.parentNode}c=void 0}c=$g(c)}}function bh(a,b){return a&&b?a===b?!0:a&&3===a.nodeType?!1:b&&3===b.nodeType?bh(a,b.parentNode):"contains"in a?a.contains(b):a.compareDocumentPosition?!!(a.compareDocumentPosition(b)&16):!1:!1}function ch(){for(var a=window,b=Qc();b instanceof a.HTMLIFrameElement;){try{var c="string"===typeof b.contentWindow.location.href}catch(d){c=!1}if(c)a=b.contentWindow;else break; +b=Qc(a.document)}return b}function Ie(a){var b=a&&a.nodeName&&a.nodeName.toLowerCase();return b&&("input"===b&&("text"===a.type||"search"===a.type||"tel"===a.type||"url"===a.type||"password"===a.type)||"textarea"===b||"true"===a.contentEditable)}function Tj(a){var b=ch(),c=a.focusedElem,d=a.selectionRange;if(b!==c&&c&&c.ownerDocument&&bh(c.ownerDocument.documentElement,c)){if(null!==d&&Ie(c))if(b=d.start,a=d.end,void 0===a&&(a=b),"selectionStart"in c)c.selectionStart=b,c.selectionEnd=Math.min(a,c.value.length); +else if(a=(b=c.ownerDocument||document)&&b.defaultView||window,a.getSelection){a=a.getSelection();var e=c.textContent.length,f=Math.min(d.start,e);d=void 0===d.end?f:Math.min(d.end,e);!a.extend&&f>d&&(e=d,d=f,f=e);e=ah(c,f);var g=ah(c,d);e&&g&&(1!==a.rangeCount||a.anchorNode!==e.node||a.anchorOffset!==e.offset||a.focusNode!==g.node||a.focusOffset!==g.offset)&&(b=b.createRange(),b.setStart(e.node,e.offset),a.removeAllRanges(),f>d?(a.addRange(b),a.extend(g.node,g.offset)):(b.setEnd(g.node,g.offset), +a.addRange(b)))}b=[];for(a=c;a=a.parentNode;)1===a.nodeType&&b.push({element:a,left:a.scrollLeft,top:a.scrollTop});"function"===typeof c.focus&&c.focus();for(c=0;cMb||(a.current=Se[Mb],Se[Mb]=null,Mb--)} +function y(a,b,c){Mb++;Se[Mb]=a.current;a.current=b}function Nb(a,b){var c=a.type.contextTypes;if(!c)return cb;var d=a.stateNode;if(d&&d.__reactInternalMemoizedUnmaskedChildContext===b)return d.__reactInternalMemoizedMaskedChildContext;var e={},f;for(f in c)e[f]=b[f];d&&(a=a.stateNode,a.__reactInternalMemoizedUnmaskedChildContext=b,a.__reactInternalMemoizedMaskedChildContext=e);return e}function ea(a){a=a.childContextTypes;return null!==a&&void 0!==a}function th(a,b,c){if(J.current!==cb)throw Error(m(168)); +y(J,b);y(S,c)}function uh(a,b,c){var d=a.stateNode;b=b.childContextTypes;if("function"!==typeof d.getChildContext)return c;d=d.getChildContext();for(var e in d)if(!(e in b))throw Error(m(108,gj(a)||"Unknown",e));return E({},c,d)}function ld(a){a=(a=a.stateNode)&&a.__reactInternalMemoizedMergedChildContext||cb;pb=J.current;y(J,a);y(S,S.current);return!0}function vh(a,b,c){var d=a.stateNode;if(!d)throw Error(m(169));c?(a=uh(a,b,pb),d.__reactInternalMemoizedMergedChildContext=a,v(S),v(J),y(J,a)):v(S); +y(S,c)}function wh(a){null===La?La=[a]:La.push(a)}function jk(a){md=!0;wh(a)}function db(){if(!Te&&null!==La){Te=!0;var a=0,b=z;try{var c=La;for(z=1;a>=g;e-=g;Ma=1<<32-ta(b)+e|c<t?(q=l,l=null):q=l.sibling;var A=r(e,l,h[t],k);if(null===A){null===l&&(l=q);break}a&&l&&null===A.alternate&&b(e,l);g=f(A,g,t);null===m?n=A:m.sibling=A;m=A;l=q}if(t===h.length)return c(e,l),D&&qb(e,t),n;if(null===l){for(;t< +h.length;t++)l=u(e,h[t],k),null!==l&&(g=f(l,g,t),null===m?n=l:m.sibling=l,m=l);D&&qb(e,t);return n}for(l=d(e,l);tt?(A=q,q=null):A=q.sibling;var x=r(e,q,w.value,k);if(null===x){null===q&&(q=A);break}a&&q&&null===x.alternate&&b(e,q);g=f(x,g,t);null===l?n=x:l.sibling=x;l=x;q=A}if(w.done)return c(e,q),D&&qb(e,t),n;if(null===q){for(;!w.done;t++,w=h.next())w=u(e,w.value,k),null!==w&&(g=f(w,g,t),null===l?n=w:l.sibling=w,l=w);D&&qb(e,t);return n}for(q=d(e,q);!w.done;t++,w=h.next())w=p(q,e,t,w.value,k),null!==w&&(a&&null!==w.alternate&&q.delete(null===w.key?t:w.key),g=f(w,g,t),null===l?n=w:l.sibling= +w,l=w);a&&q.forEach(function(a){return b(e,a)});D&&qb(e,t);return n}function v(a,d,f,h){"object"===typeof f&&null!==f&&f.type===Bb&&null===f.key&&(f=f.props.children);if("object"===typeof f&&null!==f){switch(f.$$typeof){case sd:a:{for(var k=f.key,n=d;null!==n;){if(n.key===k){k=f.type;if(k===Bb){if(7===n.tag){c(a,n.sibling);d=e(n,f.props.children);d.return=a;a=d;break a}}else if(n.elementType===k||"object"===typeof k&&null!==k&&k.$$typeof===Ta&&Ch(k)===n.type){c(a,n.sibling);d=e(n,f.props);d.ref=vc(a, +n,f);d.return=a;a=d;break a}c(a,n);break}else b(a,n);n=n.sibling}f.type===Bb?(d=sb(f.props.children,a.mode,h,f.key),d.return=a,a=d):(h=rd(f.type,f.key,f.props,null,a.mode,h),h.ref=vc(a,d,f),h.return=a,a=h)}return g(a);case Cb:a:{for(n=f.key;null!==d;){if(d.key===n)if(4===d.tag&&d.stateNode.containerInfo===f.containerInfo&&d.stateNode.implementation===f.implementation){c(a,d.sibling);d=e(d,f.children||[]);d.return=a;a=d;break a}else{c(a,d);break}else b(a,d);d=d.sibling}d=$e(f,a.mode,h);d.return=a; +a=d}return g(a);case Ta:return n=f._init,v(a,d,n(f._payload),h)}if(cc(f))return x(a,d,f,h);if(ac(f))return I(a,d,f,h);qd(a,f)}return"string"===typeof f&&""!==f||"number"===typeof f?(f=""+f,null!==d&&6===d.tag?(c(a,d.sibling),d=e(d,f),d.return=a,a=d):(c(a,d),d=Ze(f,a.mode,h),d.return=a,a=d),g(a)):c(a,d)}return v}function af(){bf=Rb=td=null}function cf(a,b){b=ud.current;v(ud);a._currentValue=b}function df(a,b,c){for(;null!==a;){var d=a.alternate;(a.childLanes&b)!==b?(a.childLanes|=b,null!==d&&(d.childLanes|= +b)):null!==d&&(d.childLanes&b)!==b&&(d.childLanes|=b);if(a===c)break;a=a.return}}function Sb(a,b){td=a;bf=Rb=null;a=a.dependencies;null!==a&&null!==a.firstContext&&(0!==(a.lanes&b)&&(ha=!0),a.firstContext=null)}function qa(a){var b=a._currentValue;if(bf!==a)if(a={context:a,memoizedValue:b,next:null},null===Rb){if(null===td)throw Error(m(308));Rb=a;td.dependencies={lanes:0,firstContext:a}}else Rb=Rb.next=a;return b}function ef(a){null===tb?tb=[a]:tb.push(a)}function Eh(a,b,c,d){var e=b.interleaved; +null===e?(c.next=c,ef(b)):(c.next=e.next,e.next=c);b.interleaved=c;return Oa(a,d)}function Oa(a,b){a.lanes|=b;var c=a.alternate;null!==c&&(c.lanes|=b);c=a;for(a=a.return;null!==a;)a.childLanes|=b,c=a.alternate,null!==c&&(c.childLanes|=b),c=a,a=a.return;return 3===c.tag?c.stateNode:null}function ff(a){a.updateQueue={baseState:a.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function Fh(a,b){a=a.updateQueue;b.updateQueue===a&&(b.updateQueue= +{baseState:a.baseState,firstBaseUpdate:a.firstBaseUpdate,lastBaseUpdate:a.lastBaseUpdate,shared:a.shared,effects:a.effects})}function Pa(a,b){return{eventTime:a,lane:b,tag:0,payload:null,callback:null,next:null}}function fb(a,b,c){var d=a.updateQueue;if(null===d)return null;d=d.shared;if(0!==(p&2)){var e=d.pending;null===e?b.next=b:(b.next=e.next,e.next=b);d.pending=b;return kk(a,c)}e=d.interleaved;null===e?(b.next=b,ef(d)):(b.next=e.next,e.next=b);d.interleaved=b;return Oa(a,c)}function vd(a,b,c){b= +b.updateQueue;if(null!==b&&(b=b.shared,0!==(c&4194240))){var d=b.lanes;d&=a.pendingLanes;c|=d;b.lanes=c;xe(a,c)}}function Gh(a,b){var c=a.updateQueue,d=a.alternate;if(null!==d&&(d=d.updateQueue,c===d)){var e=null,f=null;c=c.firstBaseUpdate;if(null!==c){do{var g={eventTime:c.eventTime,lane:c.lane,tag:c.tag,payload:c.payload,callback:c.callback,next:null};null===f?e=f=g:f=f.next=g;c=c.next}while(null!==c);null===f?e=f=b:f=f.next=b}else e=f=b;c={baseState:d.baseState,firstBaseUpdate:e,lastBaseUpdate:f, +shared:d.shared,effects:d.effects};a.updateQueue=c;return}a=c.lastBaseUpdate;null===a?c.firstBaseUpdate=b:a.next=b;c.lastBaseUpdate=b}function wd(a,b,c,d){var e=a.updateQueue;gb=!1;var f=e.firstBaseUpdate,g=e.lastBaseUpdate,h=e.shared.pending;if(null!==h){e.shared.pending=null;var k=h,n=k.next;k.next=null;null===g?f=n:g.next=n;g=k;var l=a.alternate;null!==l&&(l=l.updateQueue,h=l.lastBaseUpdate,h!==g&&(null===h?l.firstBaseUpdate=n:h.next=n,l.lastBaseUpdate=k))}if(null!==f){var m=e.baseState;g=0;l= +n=k=null;h=f;do{var r=h.lane,p=h.eventTime;if((d&r)===r){null!==l&&(l=l.next={eventTime:p,lane:0,tag:h.tag,payload:h.payload,callback:h.callback,next:null});a:{var x=a,v=h;r=b;p=c;switch(v.tag){case 1:x=v.payload;if("function"===typeof x){m=x.call(p,m,r);break a}m=x;break a;case 3:x.flags=x.flags&-65537|128;case 0:x=v.payload;r="function"===typeof x?x.call(p,m,r):x;if(null===r||void 0===r)break a;m=E({},m,r);break a;case 2:gb=!0}}null!==h.callback&&0!==h.lane&&(a.flags|=64,r=e.effects,null===r?e.effects= +[h]:r.push(h))}else p={eventTime:p,lane:r,tag:h.tag,payload:h.payload,callback:h.callback,next:null},null===l?(n=l=p,k=m):l=l.next=p,g|=r;h=h.next;if(null===h)if(h=e.shared.pending,null===h)break;else r=h,h=r.next,r.next=null,e.lastBaseUpdate=r,e.shared.pending=null}while(1);null===l&&(k=m);e.baseState=k;e.firstBaseUpdate=n;e.lastBaseUpdate=l;b=e.shared.interleaved;if(null!==b){e=b;do g|=e.lane,e=e.next;while(e!==b)}else null===f&&(e.shared.lanes=0);ra|=g;a.lanes=g;a.memoizedState=m}}function Hh(a, +b,c){a=b.effects;b.effects=null;if(null!==a)for(b=0;bc?c:4;a(!0);var d=sf.transition;sf.transition= +{};try{a(!1),b()}finally{z=c,sf.transition=d}}function $h(){return sa().memoizedState}function qk(a,b,c){var d=hb(a);c={lane:d,action:c,hasEagerState:!1,eagerState:null,next:null};if(ai(a))bi(b,c);else if(c=Eh(a,b,c,d),null!==c){var e=Z();xa(c,a,d,e);ci(c,b,d)}}function ok(a,b,c){var d=hb(a),e={lane:d,action:c,hasEagerState:!1,eagerState:null,next:null};if(ai(a))bi(b,e);else{var f=a.alternate;if(0===a.lanes&&(null===f||0===f.lanes)&&(f=b.lastRenderedReducer,null!==f))try{var g=b.lastRenderedState, +h=f(g,c);e.hasEagerState=!0;e.eagerState=h;if(ua(h,g)){var k=b.interleaved;null===k?(e.next=e,ef(b)):(e.next=k.next,k.next=e);b.interleaved=e;return}}catch(n){}finally{}c=Eh(a,b,e,d);null!==c&&(e=Z(),xa(c,a,d,e),ci(c,b,d))}}function ai(a){var b=a.alternate;return a===C||null!==b&&b===C}function bi(a,b){zc=Ad=!0;var c=a.pending;null===c?b.next=b:(b.next=c.next,c.next=b);a.pending=b}function ci(a,b,c){if(0!==(c&4194240)){var d=b.lanes;d&=a.pendingLanes;c|=d;b.lanes=c;xe(a,c)}}function ya(a,b){if(a&& +a.defaultProps){b=E({},b);a=a.defaultProps;for(var c in a)void 0===b[c]&&(b[c]=a[c]);return b}return b}function tf(a,b,c,d){b=a.memoizedState;c=c(d,b);c=null===c||void 0===c?b:E({},b,c);a.memoizedState=c;0===a.lanes&&(a.updateQueue.baseState=c)}function di(a,b,c,d,e,f,g){a=a.stateNode;return"function"===typeof a.shouldComponentUpdate?a.shouldComponentUpdate(d,f,g):b.prototype&&b.prototype.isPureReactComponent?!qc(c,d)||!qc(e,f):!0}function ei(a,b,c){var d=!1,e=cb;var f=b.contextType;"object"===typeof f&& +null!==f?f=qa(f):(e=ea(b)?pb:J.current,d=b.contextTypes,f=(d=null!==d&&void 0!==d)?Nb(a,e):cb);b=new b(c,f);a.memoizedState=null!==b.state&&void 0!==b.state?b.state:null;b.updater=Dd;a.stateNode=b;b._reactInternals=a;d&&(a=a.stateNode,a.__reactInternalMemoizedUnmaskedChildContext=e,a.__reactInternalMemoizedMaskedChildContext=f);return b}function fi(a,b,c,d){a=b.state;"function"===typeof b.componentWillReceiveProps&&b.componentWillReceiveProps(c,d);"function"===typeof b.UNSAFE_componentWillReceiveProps&& +b.UNSAFE_componentWillReceiveProps(c,d);b.state!==a&&Dd.enqueueReplaceState(b,b.state,null)}function uf(a,b,c,d){var e=a.stateNode;e.props=c;e.state=a.memoizedState;e.refs={};ff(a);var f=b.contextType;"object"===typeof f&&null!==f?e.context=qa(f):(f=ea(b)?pb:J.current,e.context=Nb(a,f));e.state=a.memoizedState;f=b.getDerivedStateFromProps;"function"===typeof f&&(tf(a,b,f,c),e.state=a.memoizedState);"function"===typeof b.getDerivedStateFromProps||"function"===typeof e.getSnapshotBeforeUpdate||"function"!== +typeof e.UNSAFE_componentWillMount&&"function"!==typeof e.componentWillMount||(b=e.state,"function"===typeof e.componentWillMount&&e.componentWillMount(),"function"===typeof e.UNSAFE_componentWillMount&&e.UNSAFE_componentWillMount(),b!==e.state&&Dd.enqueueReplaceState(e,e.state,null),wd(a,c,e,d),e.state=a.memoizedState);"function"===typeof e.componentDidMount&&(a.flags|=4194308)}function Ub(a,b){try{var c="",d=b;do c+=fj(d),d=d.return;while(d);var e=c}catch(f){e="\nError generating stack: "+f.message+ +"\n"+f.stack}return{value:a,source:b,stack:e,digest:null}}function vf(a,b,c){return{value:a,source:null,stack:null!=c?c:null,digest:null!=b?b:null}}function wf(a,b){try{console.error(b.value)}catch(c){setTimeout(function(){throw c;})}}function gi(a,b,c){c=Pa(-1,c);c.tag=3;c.payload={element:null};var d=b.value;c.callback=function(){Ed||(Ed=!0,xf=d);wf(a,b)};return c}function hi(a,b,c){c=Pa(-1,c);c.tag=3;var d=a.type.getDerivedStateFromError;if("function"===typeof d){var e=b.value;c.payload=function(){return d(e)}; +c.callback=function(){wf(a,b)}}var f=a.stateNode;null!==f&&"function"===typeof f.componentDidCatch&&(c.callback=function(){wf(a,b);"function"!==typeof d&&(null===ib?ib=new Set([this]):ib.add(this));var c=b.stack;this.componentDidCatch(b.value,{componentStack:null!==c?c:""})});return c}function ii(a,b,c){var d=a.pingCache;if(null===d){d=a.pingCache=new rk;var e=new Set;d.set(b,e)}else e=d.get(b),void 0===e&&(e=new Set,d.set(b,e));e.has(c)||(e.add(c),a=sk.bind(null,a,b,c),b.then(a,a))}function ji(a){do{var b; +if(b=13===a.tag)b=a.memoizedState,b=null!==b?null!==b.dehydrated?!0:!1:!0;if(b)return a;a=a.return}while(null!==a);return null}function ki(a,b,c,d,e){if(0===(a.mode&1))return a===b?a.flags|=65536:(a.flags|=128,c.flags|=131072,c.flags&=-52805,1===c.tag&&(null===c.alternate?c.tag=17:(b=Pa(-1,1),b.tag=2,fb(c,b,1))),c.lanes|=1),a;a.flags|=65536;a.lanes=e;return a}function aa(a,b,c,d){b.child=null===a?li(b,null,c,d):Vb(b,a.child,c,d)}function mi(a,b,c,d,e){c=c.render;var f=b.ref;Sb(b,e);d=mf(a,b,c,d,f, +e);c=nf();if(null!==a&&!ha)return b.updateQueue=a.updateQueue,b.flags&=-2053,a.lanes&=~e,Qa(a,b,e);D&&c&&Ue(b);b.flags|=1;aa(a,b,d,e);return b.child}function ni(a,b,c,d,e){if(null===a){var f=c.type;if("function"===typeof f&&!yf(f)&&void 0===f.defaultProps&&null===c.compare&&void 0===c.defaultProps)return b.tag=15,b.type=f,oi(a,b,f,d,e);a=rd(c.type,null,d,b,b.mode,e);a.ref=b.ref;a.return=b;return b.child=a}f=a.child;if(0===(a.lanes&e)){var g=f.memoizedProps;c=c.compare;c=null!==c?c:qc;if(c(g,d)&&a.ref=== +b.ref)return Qa(a,b,e)}b.flags|=1;a=eb(f,d);a.ref=b.ref;a.return=b;return b.child=a}function oi(a,b,c,d,e){if(null!==a){var f=a.memoizedProps;if(qc(f,d)&&a.ref===b.ref)if(ha=!1,b.pendingProps=d=f,0!==(a.lanes&e))0!==(a.flags&131072)&&(ha=!0);else return b.lanes=a.lanes,Qa(a,b,e)}return zf(a,b,c,d,e)}function pi(a,b,c){var d=b.pendingProps,e=d.children,f=null!==a?a.memoizedState:null;if("hidden"===d.mode)if(0===(b.mode&1))b.memoizedState={baseLanes:0,cachePool:null,transitions:null},y(Ga,ba),ba|=c; +else{if(0===(c&1073741824))return a=null!==f?f.baseLanes|c:c,b.lanes=b.childLanes=1073741824,b.memoizedState={baseLanes:a,cachePool:null,transitions:null},b.updateQueue=null,y(Ga,ba),ba|=a,null;b.memoizedState={baseLanes:0,cachePool:null,transitions:null};d=null!==f?f.baseLanes:c;y(Ga,ba);ba|=d}else null!==f?(d=f.baseLanes|c,b.memoizedState=null):d=c,y(Ga,ba),ba|=d;aa(a,b,e,c);return b.child}function qi(a,b){var c=b.ref;if(null===a&&null!==c||null!==a&&a.ref!==c)b.flags|=512,b.flags|=2097152}function zf(a, +b,c,d,e){var f=ea(c)?pb:J.current;f=Nb(b,f);Sb(b,e);c=mf(a,b,c,d,f,e);d=nf();if(null!==a&&!ha)return b.updateQueue=a.updateQueue,b.flags&=-2053,a.lanes&=~e,Qa(a,b,e);D&&d&&Ue(b);b.flags|=1;aa(a,b,c,e);return b.child}function ri(a,b,c,d,e){if(ea(c)){var f=!0;ld(b)}else f=!1;Sb(b,e);if(null===b.stateNode)Fd(a,b),ei(b,c,d),uf(b,c,d,e),d=!0;else if(null===a){var g=b.stateNode,h=b.memoizedProps;g.props=h;var k=g.context,n=c.contextType;"object"===typeof n&&null!==n?n=qa(n):(n=ea(c)?pb:J.current,n=Nb(b, +n));var l=c.getDerivedStateFromProps,m="function"===typeof l||"function"===typeof g.getSnapshotBeforeUpdate;m||"function"!==typeof g.UNSAFE_componentWillReceiveProps&&"function"!==typeof g.componentWillReceiveProps||(h!==d||k!==n)&&fi(b,g,d,n);gb=!1;var r=b.memoizedState;g.state=r;wd(b,d,g,e);k=b.memoizedState;h!==d||r!==k||S.current||gb?("function"===typeof l&&(tf(b,c,l,d),k=b.memoizedState),(h=gb||di(b,c,h,d,r,k,n))?(m||"function"!==typeof g.UNSAFE_componentWillMount&&"function"!==typeof g.componentWillMount|| +("function"===typeof g.componentWillMount&&g.componentWillMount(),"function"===typeof g.UNSAFE_componentWillMount&&g.UNSAFE_componentWillMount()),"function"===typeof g.componentDidMount&&(b.flags|=4194308)):("function"===typeof g.componentDidMount&&(b.flags|=4194308),b.memoizedProps=d,b.memoizedState=k),g.props=d,g.state=k,g.context=n,d=h):("function"===typeof g.componentDidMount&&(b.flags|=4194308),d=!1)}else{g=b.stateNode;Fh(a,b);h=b.memoizedProps;n=b.type===b.elementType?h:ya(b.type,h);g.props= +n;m=b.pendingProps;r=g.context;k=c.contextType;"object"===typeof k&&null!==k?k=qa(k):(k=ea(c)?pb:J.current,k=Nb(b,k));var p=c.getDerivedStateFromProps;(l="function"===typeof p||"function"===typeof g.getSnapshotBeforeUpdate)||"function"!==typeof g.UNSAFE_componentWillReceiveProps&&"function"!==typeof g.componentWillReceiveProps||(h!==m||r!==k)&&fi(b,g,d,k);gb=!1;r=b.memoizedState;g.state=r;wd(b,d,g,e);var x=b.memoizedState;h!==m||r!==x||S.current||gb?("function"===typeof p&&(tf(b,c,p,d),x=b.memoizedState), +(n=gb||di(b,c,n,d,r,x,k)||!1)?(l||"function"!==typeof g.UNSAFE_componentWillUpdate&&"function"!==typeof g.componentWillUpdate||("function"===typeof g.componentWillUpdate&&g.componentWillUpdate(d,x,k),"function"===typeof g.UNSAFE_componentWillUpdate&&g.UNSAFE_componentWillUpdate(d,x,k)),"function"===typeof g.componentDidUpdate&&(b.flags|=4),"function"===typeof g.getSnapshotBeforeUpdate&&(b.flags|=1024)):("function"!==typeof g.componentDidUpdate||h===a.memoizedProps&&r===a.memoizedState||(b.flags|= +4),"function"!==typeof g.getSnapshotBeforeUpdate||h===a.memoizedProps&&r===a.memoizedState||(b.flags|=1024),b.memoizedProps=d,b.memoizedState=x),g.props=d,g.state=x,g.context=k,d=n):("function"!==typeof g.componentDidUpdate||h===a.memoizedProps&&r===a.memoizedState||(b.flags|=4),"function"!==typeof g.getSnapshotBeforeUpdate||h===a.memoizedProps&&r===a.memoizedState||(b.flags|=1024),d=!1)}return Af(a,b,c,d,f,e)}function Af(a,b,c,d,e,f){qi(a,b);var g=0!==(b.flags&128);if(!d&&!g)return e&&vh(b,c,!1), +Qa(a,b,f);d=b.stateNode;tk.current=b;var h=g&&"function"!==typeof c.getDerivedStateFromError?null:d.render();b.flags|=1;null!==a&&g?(b.child=Vb(b,a.child,null,f),b.child=Vb(b,null,h,f)):aa(a,b,h,f);b.memoizedState=d.state;e&&vh(b,c,!0);return b.child}function si(a){var b=a.stateNode;b.pendingContext?th(a,b.pendingContext,b.pendingContext!==b.context):b.context&&th(a,b.context,!1);gf(a,b.containerInfo)}function ti(a,b,c,d,e){Qb();Ye(e);b.flags|=256;aa(a,b,c,d);return b.child}function Bf(a){return{baseLanes:a, +cachePool:null,transitions:null}}function ui(a,b,c){var d=b.pendingProps,e=F.current,f=!1,g=0!==(b.flags&128),h;(h=g)||(h=null!==a&&null===a.memoizedState?!1:0!==(e&2));if(h)f=!0,b.flags&=-129;else if(null===a||null!==a.memoizedState)e|=1;y(F,e&1);if(null===a){Xe(b);a=b.memoizedState;if(null!==a&&(a=a.dehydrated,null!==a))return 0===(b.mode&1)?b.lanes=1:"$!"===a.data?b.lanes=8:b.lanes=1073741824,null;g=d.children;a=d.fallback;return f?(d=b.mode,f=b.child,g={mode:"hidden",children:g},0===(d&1)&&null!== +f?(f.childLanes=0,f.pendingProps=g):f=Gd(g,d,0,null),a=sb(a,d,c,null),f.return=b,a.return=b,f.sibling=a,b.child=f,b.child.memoizedState=Bf(c),b.memoizedState=Cf,a):Df(b,g)}e=a.memoizedState;if(null!==e&&(h=e.dehydrated,null!==h))return uk(a,b,g,d,h,e,c);if(f){f=d.fallback;g=b.mode;e=a.child;h=e.sibling;var k={mode:"hidden",children:d.children};0===(g&1)&&b.child!==e?(d=b.child,d.childLanes=0,d.pendingProps=k,b.deletions=null):(d=eb(e,k),d.subtreeFlags=e.subtreeFlags&14680064);null!==h?f=eb(h,f):(f= +sb(f,g,c,null),f.flags|=2);f.return=b;d.return=b;d.sibling=f;b.child=d;d=f;f=b.child;g=a.child.memoizedState;g=null===g?Bf(c):{baseLanes:g.baseLanes|c,cachePool:null,transitions:g.transitions};f.memoizedState=g;f.childLanes=a.childLanes&~c;b.memoizedState=Cf;return d}f=a.child;a=f.sibling;d=eb(f,{mode:"visible",children:d.children});0===(b.mode&1)&&(d.lanes=c);d.return=b;d.sibling=null;null!==a&&(c=b.deletions,null===c?(b.deletions=[a],b.flags|=16):c.push(a));b.child=d;b.memoizedState=null;return d} +function Df(a,b,c){b=Gd({mode:"visible",children:b},a.mode,0,null);b.return=a;return a.child=b}function Hd(a,b,c,d){null!==d&&Ye(d);Vb(b,a.child,null,c);a=Df(b,b.pendingProps.children);a.flags|=2;b.memoizedState=null;return a}function uk(a,b,c,d,e,f,g){if(c){if(b.flags&256)return b.flags&=-257,d=vf(Error(m(422))),Hd(a,b,g,d);if(null!==b.memoizedState)return b.child=a.child,b.flags|=128,null;f=d.fallback;e=b.mode;d=Gd({mode:"visible",children:d.children},e,0,null);f=sb(f,e,g,null);f.flags|=2;d.return= +b;f.return=b;d.sibling=f;b.child=d;0!==(b.mode&1)&&Vb(b,a.child,null,g);b.child.memoizedState=Bf(g);b.memoizedState=Cf;return f}if(0===(b.mode&1))return Hd(a,b,g,null);if("$!"===e.data){d=e.nextSibling&&e.nextSibling.dataset;if(d)var h=d.dgst;d=h;f=Error(m(419));d=vf(f,d,void 0);return Hd(a,b,g,d)}h=0!==(g&a.childLanes);if(ha||h){d=O;if(null!==d){switch(g&-g){case 4:e=2;break;case 16:e=8;break;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:e= +32;break;case 536870912:e=268435456;break;default:e=0}e=0!==(e&(d.suspendedLanes|g))?0:e;0!==e&&e!==f.retryLane&&(f.retryLane=e,Oa(a,e),xa(d,a,e,-1))}Ef();d=vf(Error(m(421)));return Hd(a,b,g,d)}if("$?"===e.data)return b.flags|=128,b.child=a.child,b=vk.bind(null,a),e._reactRetry=b,null;a=f.treeContext;fa=Ka(e.nextSibling);la=b;D=!0;wa=null;null!==a&&(na[oa++]=Ma,na[oa++]=Na,na[oa++]=rb,Ma=a.id,Na=a.overflow,rb=b);b=Df(b,d.children);b.flags|=4096;return b}function vi(a,b,c){a.lanes|=b;var d=a.alternate; +null!==d&&(d.lanes|=b);df(a.return,b,c)}function Ff(a,b,c,d,e){var f=a.memoizedState;null===f?a.memoizedState={isBackwards:b,rendering:null,renderingStartTime:0,last:d,tail:c,tailMode:e}:(f.isBackwards=b,f.rendering=null,f.renderingStartTime=0,f.last=d,f.tail=c,f.tailMode=e)}function wi(a,b,c){var d=b.pendingProps,e=d.revealOrder,f=d.tail;aa(a,b,d.children,c);d=F.current;if(0!==(d&2))d=d&1|2,b.flags|=128;else{if(null!==a&&0!==(a.flags&128))a:for(a=b.child;null!==a;){if(13===a.tag)null!==a.memoizedState&& +vi(a,c,b);else if(19===a.tag)vi(a,c,b);else if(null!==a.child){a.child.return=a;a=a.child;continue}if(a===b)break a;for(;null===a.sibling;){if(null===a.return||a.return===b)break a;a=a.return}a.sibling.return=a.return;a=a.sibling}d&=1}y(F,d);if(0===(b.mode&1))b.memoizedState=null;else switch(e){case "forwards":c=b.child;for(e=null;null!==c;)a=c.alternate,null!==a&&null===xd(a)&&(e=c),c=c.sibling;c=e;null===c?(e=b.child,b.child=null):(e=c.sibling,c.sibling=null);Ff(b,!1,e,c,f);break;case "backwards":c= +null;e=b.child;for(b.child=null;null!==e;){a=e.alternate;if(null!==a&&null===xd(a)){b.child=e;break}a=e.sibling;e.sibling=c;c=e;e=a}Ff(b,!0,c,null,f);break;case "together":Ff(b,!1,null,null,void 0);break;default:b.memoizedState=null}return b.child}function Fd(a,b){0===(b.mode&1)&&null!==a&&(a.alternate=null,b.alternate=null,b.flags|=2)}function Qa(a,b,c){null!==a&&(b.dependencies=a.dependencies);ra|=b.lanes;if(0===(c&b.childLanes))return null;if(null!==a&&b.child!==a.child)throw Error(m(153));if(null!== +b.child){a=b.child;c=eb(a,a.pendingProps);b.child=c;for(c.return=b;null!==a.sibling;)a=a.sibling,c=c.sibling=eb(a,a.pendingProps),c.return=b;c.sibling=null}return b.child}function wk(a,b,c){switch(b.tag){case 3:si(b);Qb();break;case 5:Ih(b);break;case 1:ea(b.type)&&ld(b);break;case 4:gf(b,b.stateNode.containerInfo);break;case 10:var d=b.type._context,e=b.memoizedProps.value;y(ud,d._currentValue);d._currentValue=e;break;case 13:d=b.memoizedState;if(null!==d){if(null!==d.dehydrated)return y(F,F.current& +1),b.flags|=128,null;if(0!==(c&b.child.childLanes))return ui(a,b,c);y(F,F.current&1);a=Qa(a,b,c);return null!==a?a.sibling:null}y(F,F.current&1);break;case 19:d=0!==(c&b.childLanes);if(0!==(a.flags&128)){if(d)return wi(a,b,c);b.flags|=128}e=b.memoizedState;null!==e&&(e.rendering=null,e.tail=null,e.lastEffect=null);y(F,F.current);if(d)break;else return null;case 22:case 23:return b.lanes=0,pi(a,b,c)}return Qa(a,b,c)}function Dc(a,b){if(!D)switch(a.tailMode){case "hidden":b=a.tail;for(var c=null;null!== +b;)null!==b.alternate&&(c=b),b=b.sibling;null===c?a.tail=null:c.sibling=null;break;case "collapsed":c=a.tail;for(var d=null;null!==c;)null!==c.alternate&&(d=c),c=c.sibling;null===d?b||null===a.tail?a.tail=null:a.tail.sibling=null:d.sibling=null}}function W(a){var b=null!==a.alternate&&a.alternate.child===a.child,c=0,d=0;if(b)for(var e=a.child;null!==e;)c|=e.lanes|e.childLanes,d|=e.subtreeFlags&14680064,d|=e.flags&14680064,e.return=a,e=e.sibling;else for(e=a.child;null!==e;)c|=e.lanes|e.childLanes, +d|=e.subtreeFlags,d|=e.flags,e.return=a,e=e.sibling;a.subtreeFlags|=d;a.childLanes=c;return b}function xk(a,b,c){var d=b.pendingProps;Ve(b);switch(b.tag){case 2:case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return W(b),null;case 1:return ea(b.type)&&(v(S),v(J)),W(b),null;case 3:d=b.stateNode;Tb();v(S);v(J);jf();d.pendingContext&&(d.context=d.pendingContext,d.pendingContext=null);if(null===a||null===a.child)pd(b)?b.flags|=4:null===a||a.memoizedState.isDehydrated&&0===(b.flags& +256)||(b.flags|=1024,null!==wa&&(Gf(wa),wa=null));xi(a,b);W(b);return null;case 5:hf(b);var e=ub(xc.current);c=b.type;if(null!==a&&null!=b.stateNode)yk(a,b,c,d,e),a.ref!==b.ref&&(b.flags|=512,b.flags|=2097152);else{if(!d){if(null===b.stateNode)throw Error(m(166));W(b);return null}a=ub(Ea.current);if(pd(b)){d=b.stateNode;c=b.type;var f=b.memoizedProps;d[Da]=b;d[uc]=f;a=0!==(b.mode&1);switch(c){case "dialog":B("cancel",d);B("close",d);break;case "iframe":case "object":case "embed":B("load",d);break; +case "video":case "audio":for(e=0;e\x3c/script>",a=a.removeChild(a.firstChild)):"string"===typeof d.is?a=g.createElement(c,{is:d.is}):(a=g.createElement(c),"select"===c&&(g=a,d.multiple?g.multiple=!0:d.size&&(g.size=d.size))):a=g.createElementNS(a,c);a[Da]=b;a[uc]=d;zk(a,b,!1,!1);b.stateNode=a;a:{g=qe(c,d);switch(c){case "dialog":B("cancel",a);B("close",a);e=d;break;case "iframe":case "object":case "embed":B("load",a);e=d;break; +case "video":case "audio":for(e=0;eHf&&(b.flags|=128,d=!0,Dc(f,!1),b.lanes=4194304)}else{if(!d)if(a=xd(g),null!==a){if(b.flags|=128,d=!0,c=a.updateQueue,null!==c&&(b.updateQueue=c,b.flags|=4),Dc(f,!0),null===f.tail&&"hidden"===f.tailMode&&!g.alternate&&!D)return W(b),null}else 2*P()-f.renderingStartTime>Hf&&1073741824!==c&&(b.flags|= +128,d=!0,Dc(f,!1),b.lanes=4194304);f.isBackwards?(g.sibling=b.child,b.child=g):(c=f.last,null!==c?c.sibling=g:b.child=g,f.last=g)}if(null!==f.tail)return b=f.tail,f.rendering=b,f.tail=b.sibling,f.renderingStartTime=P(),b.sibling=null,c=F.current,y(F,d?c&1|2:c&1),b;W(b);return null;case 22:case 23:return ba=Ga.current,v(Ga),d=null!==b.memoizedState,null!==a&&null!==a.memoizedState!==d&&(b.flags|=8192),d&&0!==(b.mode&1)?0!==(ba&1073741824)&&(W(b),b.subtreeFlags&6&&(b.flags|=8192)):W(b),null;case 24:return null; +case 25:return null}throw Error(m(156,b.tag));}function Bk(a,b,c){Ve(b);switch(b.tag){case 1:return ea(b.type)&&(v(S),v(J)),a=b.flags,a&65536?(b.flags=a&-65537|128,b):null;case 3:return Tb(),v(S),v(J),jf(),a=b.flags,0!==(a&65536)&&0===(a&128)?(b.flags=a&-65537|128,b):null;case 5:return hf(b),null;case 13:v(F);a=b.memoizedState;if(null!==a&&null!==a.dehydrated){if(null===b.alternate)throw Error(m(340));Qb()}a=b.flags;return a&65536?(b.flags=a&-65537|128,b):null;case 19:return v(F),null;case 4:return Tb(), +null;case 10:return cf(b.type._context),null;case 22:case 23:return ba=Ga.current,v(Ga),null;case 24:return null;default:return null}}function Wb(a,b){var c=a.ref;if(null!==c)if("function"===typeof c)try{c(null)}catch(d){G(a,b,d)}else c.current=null}function If(a,b,c){try{c()}catch(d){G(a,b,d)}}function Ck(a,b){Jf=Zc;a=ch();if(Ie(a)){if("selectionStart"in a)var c={start:a.selectionStart,end:a.selectionEnd};else a:{c=(c=a.ownerDocument)&&c.defaultView||window;var d=c.getSelection&&c.getSelection(); +if(d&&0!==d.rangeCount){c=d.anchorNode;var e=d.anchorOffset,f=d.focusNode;d=d.focusOffset;try{c.nodeType,f.nodeType}catch(M){c=null;break a}var g=0,h=-1,k=-1,n=0,q=0,u=a,r=null;b:for(;;){for(var p;;){u!==c||0!==e&&3!==u.nodeType||(h=g+e);u!==f||0!==d&&3!==u.nodeType||(k=g+d);3===u.nodeType&&(g+=u.nodeValue.length);if(null===(p=u.firstChild))break;r=u;u=p}for(;;){if(u===a)break b;r===c&&++n===e&&(h=g);r===f&&++q===d&&(k=g);if(null!==(p=u.nextSibling))break;u=r;r=u.parentNode}u=p}c=-1===h||-1===k?null: +{start:h,end:k}}else c=null}c=c||{start:0,end:0}}else c=null;Kf={focusedElem:a,selectionRange:c};Zc=!1;for(l=b;null!==l;)if(b=l,a=b.child,0!==(b.subtreeFlags&1028)&&null!==a)a.return=b,l=a;else for(;null!==l;){b=l;try{var x=b.alternate;if(0!==(b.flags&1024))switch(b.tag){case 0:case 11:case 15:break;case 1:if(null!==x){var v=x.memoizedProps,z=x.memoizedState,w=b.stateNode,A=w.getSnapshotBeforeUpdate(b.elementType===b.type?v:ya(b.type,v),z);w.__reactInternalSnapshotBeforeUpdate=A}break;case 3:var t= +b.stateNode.containerInfo;1===t.nodeType?t.textContent="":9===t.nodeType&&t.documentElement&&t.removeChild(t.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(m(163));}}catch(M){G(b,b.return,M)}a=b.sibling;if(null!==a){a.return=b.return;l=a;break}l=b.return}x=zi;zi=!1;return x}function Gc(a,b,c){var d=b.updateQueue;d=null!==d?d.lastEffect:null;if(null!==d){var e=d=d.next;do{if((e.tag&a)===a){var f=e.destroy;e.destroy=void 0;void 0!==f&&If(b,c,f)}e=e.next}while(e!==d)}} +function Id(a,b){b=b.updateQueue;b=null!==b?b.lastEffect:null;if(null!==b){var c=b=b.next;do{if((c.tag&a)===a){var d=c.create;c.destroy=d()}c=c.next}while(c!==b)}}function Lf(a){var b=a.ref;if(null!==b){var c=a.stateNode;switch(a.tag){case 5:a=c;break;default:a=c}"function"===typeof b?b(a):b.current=a}}function Ai(a){var b=a.alternate;null!==b&&(a.alternate=null,Ai(b));a.child=null;a.deletions=null;a.sibling=null;5===a.tag&&(b=a.stateNode,null!==b&&(delete b[Da],delete b[uc],delete b[Me],delete b[Dk], +delete b[Ek]));a.stateNode=null;a.return=null;a.dependencies=null;a.memoizedProps=null;a.memoizedState=null;a.pendingProps=null;a.stateNode=null;a.updateQueue=null}function Bi(a){return 5===a.tag||3===a.tag||4===a.tag}function Ci(a){a:for(;;){for(;null===a.sibling;){if(null===a.return||Bi(a.return))return null;a=a.return}a.sibling.return=a.return;for(a=a.sibling;5!==a.tag&&6!==a.tag&&18!==a.tag;){if(a.flags&2)continue a;if(null===a.child||4===a.tag)continue a;else a.child.return=a,a=a.child}if(!(a.flags& +2))return a.stateNode}}function Mf(a,b,c){var d=a.tag;if(5===d||6===d)a=a.stateNode,b?8===c.nodeType?c.parentNode.insertBefore(a,b):c.insertBefore(a,b):(8===c.nodeType?(b=c.parentNode,b.insertBefore(a,c)):(b=c,b.appendChild(a)),c=c._reactRootContainer,null!==c&&void 0!==c||null!==b.onclick||(b.onclick=kd));else if(4!==d&&(a=a.child,null!==a))for(Mf(a,b,c),a=a.sibling;null!==a;)Mf(a,b,c),a=a.sibling}function Nf(a,b,c){var d=a.tag;if(5===d||6===d)a=a.stateNode,b?c.insertBefore(a,b):c.appendChild(a); +else if(4!==d&&(a=a.child,null!==a))for(Nf(a,b,c),a=a.sibling;null!==a;)Nf(a,b,c),a=a.sibling}function jb(a,b,c){for(c=c.child;null!==c;)Di(a,b,c),c=c.sibling}function Di(a,b,c){if(Ca&&"function"===typeof Ca.onCommitFiberUnmount)try{Ca.onCommitFiberUnmount(Uc,c)}catch(h){}switch(c.tag){case 5:X||Wb(c,b);case 6:var d=T,e=za;T=null;jb(a,b,c);T=d;za=e;null!==T&&(za?(a=T,c=c.stateNode,8===a.nodeType?a.parentNode.removeChild(c):a.removeChild(c)):T.removeChild(c.stateNode));break;case 18:null!==T&&(za? +(a=T,c=c.stateNode,8===a.nodeType?Re(a.parentNode,c):1===a.nodeType&&Re(a,c),nc(a)):Re(T,c.stateNode));break;case 4:d=T;e=za;T=c.stateNode.containerInfo;za=!0;jb(a,b,c);T=d;za=e;break;case 0:case 11:case 14:case 15:if(!X&&(d=c.updateQueue,null!==d&&(d=d.lastEffect,null!==d))){e=d=d.next;do{var f=e,g=f.destroy;f=f.tag;void 0!==g&&(0!==(f&2)?If(c,b,g):0!==(f&4)&&If(c,b,g));e=e.next}while(e!==d)}jb(a,b,c);break;case 1:if(!X&&(Wb(c,b),d=c.stateNode,"function"===typeof d.componentWillUnmount))try{d.props= +c.memoizedProps,d.state=c.memoizedState,d.componentWillUnmount()}catch(h){G(c,b,h)}jb(a,b,c);break;case 21:jb(a,b,c);break;case 22:c.mode&1?(X=(d=X)||null!==c.memoizedState,jb(a,b,c),X=d):jb(a,b,c);break;default:jb(a,b,c)}}function Ei(a){var b=a.updateQueue;if(null!==b){a.updateQueue=null;var c=a.stateNode;null===c&&(c=a.stateNode=new Fk);b.forEach(function(b){var d=Gk.bind(null,a,b);c.has(b)||(c.add(b),b.then(d,d))})}}function Aa(a,b,c){c=b.deletions;if(null!==c)for(var d=0;de&&(e=g);d&=~f}d=e;d=P()-d;d=(120>d?120:480>d?480:1080>d?1080:1920>d?1920:3E3>d?3E3:4320>d?4320:1960*Mk(d/1960))-d;if(10a?16:a;if(null===lb)var d=!1;else{a=lb;lb=null;Qd=0;if(0!==(p&6))throw Error(m(331));var e=p;p|=4;for(l=a.current;null!==l;){var f=l,g=f.child;if(0!==(l.flags&16)){var h=f.deletions;if(null!==h){for(var k=0;kP()-Of?wb(a,0):Sf|=c);ia(a,b)}function Ti(a,b){0===b&&(0===(a.mode&1)?b=1:(b=Rd,Rd<<=1,0===(Rd&130023424)&&(Rd=4194304)));var c=Z();a=Oa(a,b);null!==a&&(ic(a,b,c),ia(a,c))}function vk(a){var b=a.memoizedState,c=0;null!==b&&(c=b.retryLane);Ti(a,c)}function Gk(a,b){var c=0;switch(a.tag){case 13:var d=a.stateNode;var e=a.memoizedState;null!==e&&(c=e.retryLane); +break;case 19:d=a.stateNode;break;default:throw Error(m(314));}null!==d&&d.delete(b);Ti(a,c)}function Mi(a,b){return xh(a,b)}function Tk(a,b,c,d){this.tag=a;this.key=c;this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null;this.index=0;this.ref=null;this.pendingProps=b;this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null;this.mode=d;this.subtreeFlags=this.flags=0;this.deletions=null;this.childLanes=this.lanes=0;this.alternate=null}function yf(a){a= +a.prototype;return!(!a||!a.isReactComponent)}function Uk(a){if("function"===typeof a)return yf(a)?1:0;if(void 0!==a&&null!==a){a=a.$$typeof;if(a===ie)return 11;if(a===je)return 14}return 2}function eb(a,b){var c=a.alternate;null===c?(c=pa(a.tag,b,a.key,a.mode),c.elementType=a.elementType,c.type=a.type,c.stateNode=a.stateNode,c.alternate=a,a.alternate=c):(c.pendingProps=b,c.type=a.type,c.flags=0,c.subtreeFlags=0,c.deletions=null);c.flags=a.flags&14680064;c.childLanes=a.childLanes;c.lanes=a.lanes;c.child= +a.child;c.memoizedProps=a.memoizedProps;c.memoizedState=a.memoizedState;c.updateQueue=a.updateQueue;b=a.dependencies;c.dependencies=null===b?null:{lanes:b.lanes,firstContext:b.firstContext};c.sibling=a.sibling;c.index=a.index;c.ref=a.ref;return c}function rd(a,b,c,d,e,f){var g=2;d=a;if("function"===typeof a)yf(a)&&(g=1);else if("string"===typeof a)g=5;else a:switch(a){case Bb:return sb(c.children,e,f,b);case fe:g=8;e|=8;break;case ee:return a=pa(12,c,b,e|2),a.elementType=ee,a.lanes=f,a;case ge:return a= +pa(13,c,b,e),a.elementType=ge,a.lanes=f,a;case he:return a=pa(19,c,b,e),a.elementType=he,a.lanes=f,a;case Ui:return Gd(c,e,f,b);default:if("object"===typeof a&&null!==a)switch(a.$$typeof){case hg:g=10;break a;case gg:g=9;break a;case ie:g=11;break a;case je:g=14;break a;case Ta:g=16;d=null;break a}throw Error(m(130,null==a?a:typeof a,""));}b=pa(g,c,b,e);b.elementType=a;b.type=d;b.lanes=f;return b}function sb(a,b,c,d){a=pa(7,a,d,b);a.lanes=c;return a}function Gd(a,b,c,d){a=pa(22,a,d,b);a.elementType= +Ui;a.lanes=c;a.stateNode={isHidden:!1};return a}function Ze(a,b,c){a=pa(6,a,null,b);a.lanes=c;return a}function $e(a,b,c){b=pa(4,null!==a.children?a.children:[],a.key,b);b.lanes=c;b.stateNode={containerInfo:a.containerInfo,pendingChildren:null,implementation:a.implementation};return b}function Vk(a,b,c,d,e){this.tag=b;this.containerInfo=a;this.finishedWork=this.pingCache=this.current=this.pendingChildren=null;this.timeoutHandle=-1;this.callbackNode=this.pendingContext=this.context=null;this.callbackPriority= +0;this.eventTimes=we(0);this.expirationTimes=we(-1);this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0;this.entanglements=we(0);this.identifierPrefix=d;this.onRecoverableError=e;this.mutableSourceEagerHydrationData=null}function Vf(a,b,c,d,e,f,g,h,k,l){a=new Vk(a,b,c,h,k);1===b?(b=1,!0===f&&(b|=8)):b=0;f=pa(3,null,null,b);a.current=f;f.stateNode=a;f.memoizedState={element:d,isDehydrated:c,cache:null,transitions:null, +pendingSuspenseBoundaries:null};ff(f);return a}function Wk(a,b,c){var d=3