diff --git a/blocks/flojoy/control/slider.py b/blocks/flojoy/control/slider.py index 0dec95d..c570acb 100644 --- a/blocks/flojoy/control/slider.py +++ b/blocks/flojoy/control/slider.py @@ -2,5 +2,5 @@ @ui_input -def slider(x): +def slider(x: int) -> int: return x diff --git a/blocks/flojoy/control/toggle.py b/blocks/flojoy/control/toggle.py new file mode 100644 index 0000000..be46508 --- /dev/null +++ b/blocks/flojoy/control/toggle.py @@ -0,0 +1,6 @@ +from captain.decorators import ui_input + + +@ui_input +def toggle(x: bool): + return x diff --git a/blocks/flojoy/logic/clock.py b/blocks/flojoy/logic/clock.py new file mode 100644 index 0000000..da39b09 --- /dev/null +++ b/blocks/flojoy/logic/clock.py @@ -0,0 +1,5 @@ +import reactivex as rx + + +def clock(): + return rx.interval(1 / 60) diff --git a/blocks/flojoy/logic/conditional.py b/blocks/flojoy/logic/conditional.py new file mode 100644 index 0000000..47e2f06 --- /dev/null +++ b/blocks/flojoy/logic/conditional.py @@ -0,0 +1,14 @@ +from typing import TypedDict +from captain.types.builtins import Ignore + + +class Output(TypedDict): + true: Ignore | None + false: Ignore | None + + +def conditional(b: bool) -> Output: + return Output( + true=None if b else Ignore(), + false=Ignore() if b else None, + ) diff --git a/blocks/flojoy/logic/false.py b/blocks/flojoy/logic/false.py new file mode 100644 index 0000000..b848a09 --- /dev/null +++ b/blocks/flojoy/logic/false.py @@ -0,0 +1,2 @@ +def false() -> bool: + return False diff --git a/blocks/flojoy/logic/sequence.py b/blocks/flojoy/logic/sequence.py new file mode 100644 index 0000000..1cdc4e8 --- /dev/null +++ b/blocks/flojoy/logic/sequence.py @@ -0,0 +1,5 @@ +import reactivex as rx + + +def sequence(start: int = 0, stop: int | None = None, step: int | None = None): + return rx.range(start=start, stop=stop, step=step) diff --git a/blocks/flojoy/logic/true.py b/blocks/flojoy/logic/true.py new file mode 100644 index 0000000..068df96 --- /dev/null +++ b/blocks/flojoy/logic/true.py @@ -0,0 +1,2 @@ +def true() -> bool: + return True diff --git a/blocks/flojoy/math/arithmetic/add.py b/blocks/flojoy/math/arithmetic/add.py index 463d462..c040ba4 100644 --- a/blocks/flojoy/math/arithmetic/add.py +++ b/blocks/flojoy/math/arithmetic/add.py @@ -1,2 +1,2 @@ -def add(x, y): +def add(x: int, y: int) -> int: return x + y diff --git a/blocks/flojoy/math/arithmetic/subtract.py b/blocks/flojoy/math/arithmetic/subtract.py index 4c59fd8..ca22e3f 100644 --- a/blocks/flojoy/math/arithmetic/subtract.py +++ b/blocks/flojoy/math/arithmetic/subtract.py @@ -1,2 +1,2 @@ -def subtract(x, y): +def subtract(x: int, y: int) -> int: return x - y diff --git a/blocks/flojoy/math/constant.py b/blocks/flojoy/math/constant.py index 3a428cc..91145e1 100644 --- a/blocks/flojoy/math/constant.py +++ b/blocks/flojoy/math/constant.py @@ -1,2 +1,2 @@ -def constant(): - return 2 +def constant(val: int) -> int: + return val diff --git a/blocks/flojoy/math/rand.py b/blocks/flojoy/math/rand.py new file mode 100644 index 0000000..9391eb8 --- /dev/null +++ b/blocks/flojoy/math/rand.py @@ -0,0 +1,6 @@ +import random +from typing import Any + + +def rand(x: Any = None) -> int: + return random.randint(0, 100) diff --git a/captain/controllers/reactive.py b/captain/controllers/reactive.py index f8574cb..7b7d3b3 100644 --- a/captain/controllers/reactive.py +++ b/captain/controllers/reactive.py @@ -1,18 +1,20 @@ -import os from dataclasses import dataclass from functools import partial -from typing import Any, Callable, Mapping, Tuple +from typing import Any, Callable, Mapping, Tuple, is_typeddict import reactivex as rx import reactivex.operators as ops from reactivex import Observable, Subject +from reactivex.abc import DisposableBase, SchedulerBase +from reactivex.scheduler import ThreadPoolScheduler +from captain.lib.block_import import FlojoyBlock, import_blocks from captain.logging import logger +from captain.types.builtins import Ignore from captain.types.events import FlowControlEvent from captain.types.flowchart import BlockID, BlockType, FCBlock, FlowChart -from captain.utils.blocks import import_blocks, is_ui_input -BLOCKS_DIR = os.path.join("blocks") +BLOCKS_DIR = "blocks" @dataclass @@ -25,7 +27,7 @@ class FCBlockIO: block: FCBlock input_subject: Subject - output_observable: Observable + output_observables: Mapping[str, Observable] def find_islands(blocks: dict[str, FCBlock]) -> list[list[FCBlock]]: @@ -63,179 +65,24 @@ def dfs(block: FCBlock, island: list[FCBlock]): return islands -def wire_flowchart( - flowchart: FlowChart, - on_publish: Callable[[BlockID, Any], None], - starter: Observable, - control_subjects: Mapping[BlockID, Observable], - block_funcs: Mapping[BlockType, Callable], -): - """Connects all of block functions in a flow chart using RxPY. +class Flow: + """Represents a currently running flow. + + The flowchart is immediately started upon creation. To run the flow chart, push an item to the start observable. - Parameters - ---------- + Fields + ------ flowchart - The flow chart to connect together. + The flow chart to run. on_publish A function that will be called every time a block function is run. starter An observable that blocks with no inputs will be subscribed to. The flowchart will be run from the start every time this observable emits an item. - control_subjects - Observables for each of the UI inputs in the flow chart. - block_funcs - Preimported functions for each block type. - """ - - blocks: dict[BlockID, FCBlock] = {b.id: b for b in flowchart.blocks} - islands = find_islands(blocks) - block_ios: dict[BlockID, FCBlockIO] = {} - - for island in islands: - for block in island: - # input_subject is the subject that will be used to push values to the block - # output_observable is the observable that will be used to get values from the block - - input_subject = Subject() - - # TODO: should probably have a debug toggle in settings - # to turn on/off debugging and we can guard debug statements with that - input_subject.subscribe( - partial( - lambda blk, x: logger.debug(f"Input got {x} for {blk.id}"), - block, - ) - ) # for logging purpose only - - # A lot of the lambdas being passed created are partially applied, - # this is because Python has weird late binding with lambdas in a loop. - # Instead of using captured values, we need to pass it as a parameter - # so that it binds to the value and not the name. - - def run_block(blk: FCBlock, kwargs: dict[str, Any]): - fn = block_funcs[blk.block_type] - logger.debug(f"Running block {blk.id}") - return fn(**kwargs) - - def make_block_fn_props( - blk: FCBlock, inputs: list[Tuple[str, Any]] - ) -> dict[str, Any]: - logger.debug(f"Making params for block {blk.id} with {inputs}") - return dict(inputs) - - output_observable = input_subject.pipe( - ops.map(partial(make_block_fn_props, block)), - ops.map(partial(run_block, block)), - ops.publish(), # Makes it so values are not emitted on each subscribe - ) - - # TODO: check comment above, also can be guarded - output_observable.subscribe( - partial( - lambda blk, x: logger.debug( - f"Got {x} for {blk.id} after transform" - ), - block, - ) - ) # for logging purpose only - - output_observable.subscribe( - on_next=partial(lambda blk, x: on_publish(blk.id, x), block), - on_error=lambda e: logger.debug(e), - on_completed=lambda: logger.debug("completed"), - ) # this is to stream data back to the client - - # Start emitting values for outputs - output_observable.connect() - - if block.id in control_subjects: - logger.debug( - f"Connecting {block.id} to ui input {control_subjects[block.id]}" - ) - control_subjects[block.id].subscribe( - on_next=input_subject.on_next, - on_error=input_subject.on_error, - on_completed=input_subject.on_completed, - ) - control_subjects[block.id].subscribe( - on_next=lambda x: logger.debug(f"Got {x} from the UI input subject") - ) - - block_ios[block.id] = FCBlockIO( - block=block, - input_subject=input_subject, - output_observable=output_observable, - ) - - visited_blocks: set[BlockID] = set() - - def rec_connect_blocks(io: FCBlockIO): - """Combines each input edge of a block into a single observable, until a block with no inputs is reached. - - Blocks with no inputs are subscribed to the start observable. - - """ - logger.info( - f"Recursively connecting {io.block.block_type}({io.block.id}) to its inputs" - ) - - if not io.block.ins and io.block.id not in control_subjects: - logger.info( - f"Connected {io.block.id} to start observable with ui inputs {control_subjects.keys()}" - ) - logger.debug(f"CREATED REACTIVE EDGE {io.block.id} -> {starter}") - starter.subscribe( - on_next=io.input_subject.on_next, - on_error=io.input_subject.on_error, - on_completed=io.input_subject.on_completed, - ) - return - - if not io.block.ins and io.block.id in control_subjects: - return - - logger.debug(f"Connecting {io.block.id}") - - # combines every input for a block into a single observable. - in_combined = rx.combine_latest( - *( - block_ios[conn.source].output_observable.pipe( - ops.map(partial(lambda param, v: (param, v), conn.targetParam)) - ) - for conn in io.block.ins - ) - ) - - for conn in io.block.ins: - logger.debug( - f"CREATED REACTIVE EDGE {conn.source} -> {io.block.id} thru 'combine_latest' via {conn.targetParam}" - ) - - in_combined.subscribe( - on_next=io.input_subject.on_next, - on_error=io.input_subject.on_error, - on_completed=io.input_subject.on_completed, - ) - for conn in io.block.ins: - logger.debug(conn) - if conn.source in visited_blocks: - continue - visited_blocks.add(conn.source) - rec_connect_blocks(block_ios[conn.source]) - - # Connect the graph backwards starting from the terminal nodes - terminals = filter(lambda b: not b.outs, blocks.values()) - for block in terminals: - visited_blocks.add(block.id) - rec_connect_blocks(block_ios[block.id]) - - -class Flow: - """Represents a currently running flow. - - The flowchart is immediately started upon creation. + publish_scheduler + The RxPY scheduler to use for the publish function """ flowchart: FlowChart @@ -246,26 +93,25 @@ def __init__( flowchart: FlowChart, publish_fn: Callable[[BlockID, Any], None], start_obs: Observable, + publish_scheduler: SchedulerBase | None = None, + publish_sample_time: float = 1 / 30, ) -> None: self.flowchart = flowchart self.control_subjects = {} + self.on_publish = publish_fn + self.start_obs = start_obs + self.publish_scheduler = publish_scheduler funcs = import_blocks(BLOCKS_DIR) for block in flowchart.blocks: - if is_ui_input(funcs[block.block_type]): + if funcs[block.block_type].is_ui_input: logger.info( f"Creating a Subject for {block.block_type}({block.id}) to react to changes." ) self.control_subjects[block.id] = Subject() - wire_flowchart( - flowchart=self.flowchart, - on_publish=publish_fn, - starter=start_obs, - control_subjects=self.control_subjects, - block_funcs=funcs, - ) + self.subscriptions = self.connect(funcs, publish_sample_time) @staticmethod def from_json(data: str, publish_fn: Callable, start_obs: Observable): @@ -274,3 +120,202 @@ def from_json(data: str, publish_fn: Callable, start_obs: Observable): def process_ui_event(self, event: FlowControlEvent): self.control_subjects[event.block_id].on_next([("x", event.value)]) + + def destroy(self): + for sub in self.subscriptions: + sub.dispose() + + def connect( + self, block_funcs: Mapping[BlockType, FlojoyBlock], publish_sample_time: float + ): + """Connects all of block functions in a flow chart using RxPY. + + To run the flow chart, push an item to the start observable. + + Parameters + ---------- + block_funcs + Preimported FlojoyBlocks for each block type. + """ + blocks: dict[str, FCBlock] = {b.id: b for b in self.flowchart.blocks} + islands = find_islands(blocks) + block_ios: dict[str, FCBlockIO] = {} + + subscriptions: list[DisposableBase] = [] + + for island in islands: + for block in island: + # input_subject is the subject that will be used to push values to the block + # output_observable is the observable that will be used to get values from the block + input_subject = Subject() + + # TODO: should probably have a debug toggle in settings + # to turn on/off debugging and we can guard debug statements with that + input_subject.subscribe( + partial( + lambda blk, x: logger.info( + f"Input got {x} for {blk.block_type} ({blk.id}) regardless of zip" + ), + block, + ), + on_error=lambda e: logger.error(e), + ) + + fj_block = block_funcs[block.block_type] + + # A lot of the lambdas being passed created are partially applied, + # this is because Python has weird late binding with lambdas in a loop. + # Instead of using captured values, we need to pass it as a parameter + # so that it binds to the value and not the name. + # + def run_block(blk: FCBlock, kwargs: dict[str, Any]): + fn = block_funcs[blk.block_type] + logger.info(f"Running block {blk.block_type} ({blk.id})") + res = fn(**kwargs) + match res: + case Observable(): + return res + case _: + return rx.just(res) + + def make_block_fn_props( + blk: FCBlock, inputs: list[Tuple[str, Any]] + ) -> dict[str, Any]: + logger.info( + f"Making params for block {blk.block_type} ({blk.id}) with {inputs}" + ) + return dict(inputs) | blk.intrinsic_parameters + + output_observable = input_subject.pipe( + # run concurrently so the loop doesn't block everything + ops.map(partial(make_block_fn_props, block)), + ops.flat_map(partial(run_block, block)), + ops.filter(lambda x: not isinstance(x, Ignore)), + ops.observe_on(ThreadPoolScheduler()), + # Makes it so values are not emitted on each subscribe + ops.publish(), + ) + + disposable = output_observable.subscribe( + partial( + lambda blk, x: logger.info( + f"Got {x} for {blk.block_type} ({blk.id}) after zip and transform" + ), + block, + ), + on_error=lambda e: logger.error(e), + ) # for logging purpose only + subscriptions.append(disposable) + + # Split blocks that return multiple things into individual observables + if is_typeddict(fj_block.output): + output_observables = { + name: output_observable.pipe( + ops.pluck(name), + ops.filter(lambda x: not isinstance(x, Ignore)), + ) + for name in fj_block.output.__annotations__ + } + else: + output_observables = {"value": output_observable} + + # TODO: Figure out what to publish when a block returns multiple values + # Current solution is just to publish the first value only + debounced = next(iter(output_observables.values())).pipe( + ops.sample(publish_sample_time, self.publish_scheduler), + ) + + disposable = debounced.subscribe( + partial(lambda blk, x: self.on_publish(blk.id, x), block), + on_error=lambda e: logger.error(e), + ) # this is to stream data back to the client + subscriptions.append(disposable) + + # Start emitting values for outputs + output_observable.connect() + + if block.id in self.control_subjects: + logger.debug( + f"Connecting {block.block_type} ({block.id}) to ui input {self.control_subjects[block.id]}" + ) + disposable = self.control_subjects[block.id].subscribe( + input_subject.on_next, + input_subject.on_error, + input_subject.on_completed, + ) + subscriptions.append(disposable) + disposable = self.control_subjects[block.id].subscribe( + on_next=lambda x: logger.debug( + f"Got {x} from the UI input subject" + ) + ) + subscriptions.append(disposable) + + block_ios[block.id] = FCBlockIO( + block=block, + input_subject=input_subject, + output_observables=output_observables, + ) + + visited_blocks = set() + + def rec_connect_blocks(io: FCBlockIO): + logger.info( + f"Recursively connecting {io.block.block_type} ({io.block.id}) to its inputs" + ) + + if not io.block.ins and io.block.id not in self.control_subjects: + logger.debug( + f"Connected {io.block.id} to start observable with ui inputs {list(self.control_subjects.keys())}" + ) + logger.debug(f"CREATED REACTIVE EDGE {io.block.id} -> {self.start_obs}") + disposable = self.start_obs.subscribe( + io.input_subject.on_next, + io.input_subject.on_error, + io.input_subject.on_completed, + ) + subscriptions.append(disposable) + return + + if not io.block.ins and io.block.id in self.control_subjects: + return + + logger.debug(f"Connecting {io.block.id}") + + # combines every input for a block into a single observable. + in_combined = rx.combine_latest( + *( + block_ios[conn.source] + .output_observables[conn.sourceParam] + .pipe( + ops.map(partial(lambda param, v: (param, v), conn.targetParam)) + ) + for conn in io.block.ins + ) + ) + + for conn in io.block.ins: + logger.debug( + f"CREATED REACTIVE EDGE {conn.source} -> {io.block.id} via {conn.targetParam}" + ) + + disposable = in_combined.subscribe( + io.input_subject.on_next, + io.input_subject.on_error, + io.input_subject.on_completed, + ) + subscriptions.append(disposable) + for conn in io.block.ins: + logger.debug(conn) + if conn.source in visited_blocks: + continue + visited_blocks.add(conn.source) + rec_connect_blocks(block_ios[conn.source]) + + # Connect the graph backwards starting from the terminal nodes + terminals = filter(lambda b: not b.outs, blocks.values()) + for block in terminals: + visited_blocks.add(block.id) + rec_connect_blocks(block_ios[block.id]) + + return subscriptions diff --git a/captain/lib/block_import.py b/captain/lib/block_import.py new file mode 100644 index 0000000..890c529 --- /dev/null +++ b/captain/lib/block_import.py @@ -0,0 +1,65 @@ +import inspect +import importlib.util +import os +from typing import Callable, Mapping, Type, TypeAlias +from dataclasses import dataclass + +from captain.types.flowchart import BlockType + +Name: TypeAlias = str + + +@dataclass +class FlojoyBlock: + func: Callable + inputs: Mapping[Name, Type] + output: Type + + def __call__(self, *args, **kwargs): + return self.func(*args, **kwargs) + + @property + def is_ui_input(self) -> bool: + return getattr(self.func, "ui_input", False) + + +def import_blocks(blocks_dir: str) -> Mapping[BlockType, FlojoyBlock]: + folder_path = os.path.join(blocks_dir, "flojoy") + if not os.path.exists(folder_path): + raise ValueError( + f"{blocks_dir} does not seem to be a valid blocks directory since the 'flojoy' folder (which contains the Flojoy Standard Blocks Library) is missing here." + ) + + # example key: "flojoy.math.arithmetic.add" + functions = {} + + for root, _, files in os.walk(blocks_dir): + for file in files: + if not file.endswith(".py"): + continue + + block_name = file.rstrip(".py") + block_path = os.path.join(root, file) + block_type = os.path.relpath( + os.path.splitext(block_path)[0], blocks_dir + ).replace(os.path.sep, ".") + + spec = importlib.util.spec_from_file_location(block_name, block_path) + + if not spec: + raise ValueError(f"Invalid block spec from {block_path}") + + module = importlib.util.module_from_spec(spec) + if not spec.loader: + raise ValueError(f"Could not get loader from {block_path}") + + spec.loader.exec_module(module) + func = getattr(module, block_name) + + sig = inspect.signature(func) + inputs = {name: param.annotation for name, param in sig.parameters.items()} + outputs = sig.return_annotation + + functions[block_type] = FlojoyBlock(func, inputs, outputs) + + return functions diff --git a/captain/routers/blocks.py b/captain/routers/blocks.py index 42787ba..845daa6 100644 --- a/captain/routers/blocks.py +++ b/captain/routers/blocks.py @@ -1,14 +1,9 @@ -# import asyncio -# from asyncio import Future -# from typing import Any - -# import reactivex.operators as ops - from typing import Any from fastapi import APIRouter, WebSocket from pydantic import ValidationError from reactivex import Subject +from reactivex.scheduler.eventloop import AsyncIOThreadSafeScheduler from captain.controllers.reactive import Flow from captain.logging import logger @@ -22,9 +17,7 @@ ) from captain.types.flowchart import BlockID, FlowChart from captain.utils.ws import send_message_factory - -# from reactivex import create -# from reactivex.subject import BehaviorSubject +import asyncio router = APIRouter(tags=["blocks"], prefix="/blocks") @@ -48,7 +41,6 @@ def publish_fn(id: BlockID, x: Any): while True: data = await websocket.receive_text() - logger.info(f"Got message {data}") try: message = FlowSocketMessage.model_validate_json(data) except ValidationError as e: @@ -56,14 +48,22 @@ def publish_fn(id: BlockID, x: Any): continue match message.event: - case FlowStartEvent(rf=rf): + case FlowStartEvent(rf=rf, function_definitions=function_definitions): if flow is None: - fc = FlowChart.from_react_flow(rf) + fc = FlowChart.from_react_flow(rf, function_definitions) + logger.info("Creating flow from react flow instance") - flow = Flow(fc, publish_fn, start_obs) + loop = asyncio.get_event_loop() + scheduler = AsyncIOThreadSafeScheduler(loop) + flow = Flow(fc, publish_fn, start_obs, scheduler) + send_msg(FlowReadyEvent().model_dump_json()) logger.info("flow ready") + + start_obs.on_next({}) case FlowCancelEvent(): + if flow is not None: + flow.destroy() flow = None logger.info("Cancelling flow") case FlowControlEvent(): diff --git a/captain/tests/test_functions.py b/captain/tests/test_functions.py new file mode 100644 index 0000000..f165b7e --- /dev/null +++ b/captain/tests/test_functions.py @@ -0,0 +1,138 @@ +import time + +import pytest +from reactivex import Subject + +from captain.controllers.reactive import Flow +from captain.types.flowchart import ( + INTERNAL_PREFIX, + FlowChart, + FunctionDefinition, + ReactFlow, + RFBlockData, + RFBuiltinBlockData, + RFEdge, + RFFunctionInstanceData, + RFNode, + convert_rf_nodes_edges, + inline_function_instances, + join_function_edges, +) + + +@pytest.fixture +def basic_function_flow(): + defn = FunctionDefinition( + block=RFNode[RFBuiltinBlockData]( + id="add_two_def", + data=RFBuiltinBlockData(block_type="flojoy.intrinsics.function"), + ), + nodes=[ + RFNode[RFBlockData]( + id="const_two", + data=RFBuiltinBlockData( + block_type="flojoy.math.constant", + intrinsic_parameters={"val": 2}, + ), + ), + RFNode[RFBlockData]( + id="add", + data=RFBuiltinBlockData( + block_type="flojoy.math.arithmetic.add", + ), + ), + ], + edges=[ + RFEdge( + source="add_two_def", + target="add", + sourceHandle=f"{INTERNAL_PREFIX}inp", + targetHandle="x", + ), + RFEdge( + source="const_two", + target="add", + sourceHandle="value", + targetHandle="y", + ), + RFEdge( + source="add", + target="add_two_def", + sourceHandle="value", + targetHandle=f"{INTERNAL_PREFIX}out", + ), + ], + ) + blocks = [ + RFNode[RFBlockData]( + id="add_two_instance", + data=RFFunctionInstanceData( + block_type="function_instance", definition_block_id="add_two_def" + ), + ), + RFNode[RFBlockData]( + id="const_three", + data=RFBuiltinBlockData( + block_type="flojoy.math.constant", + intrinsic_parameters={"val": 3}, + ), + ), + RFNode[RFBlockData]( + id="big_num", + data=RFBuiltinBlockData(block_type="flojoy.visualization.big_num"), + ), + ] + + edges = [ + RFEdge( + source="const_three", + target="add_two_instance", + sourceHandle="value", + targetHandle="inp", + ), + RFEdge( + source="add_two_instance", + target="big_num", + sourceHandle="out", + targetHandle="x", + ), + ] + return (blocks, edges, {"add_two_def": defn}) + + +def test_basic_function_instance_inlining(basic_function_flow): + nodes, edges, definitions = basic_function_flow + + nodes, edges = inline_function_instances(nodes, edges, definitions, set()) + assert len(nodes) == 4 + assert len(edges) == 5 + assert "add_two_instance" not in [node.id for node in nodes] + + _, cons = convert_rf_nodes_edges(nodes, edges) + cons = join_function_edges(cons, {"add_two_def", "add_two_instance"}) + assert all( + INTERNAL_PREFIX not in e.targetParam and INTERNAL_PREFIX not in e.sourceParam + for e in cons + ) + + assert len(cons) == 3 + + +def test_basic_function_instance_running(basic_function_flow): + nodes, edges, definitions = basic_function_flow + fc = FlowChart.from_react_flow( + ReactFlow(nodes=nodes, edges=edges), function_definitions=definitions + ) + outputs = {} + start_obs = Subject() + + def pub(id, x): + print(id, x) + outputs[id] = x + + _ = Flow(fc, pub, start_obs, publish_sample_time=0) + start_obs.on_next({}) + + time.sleep(0.5) + + assert outputs["big_num"] == 5 diff --git a/captain/tests/test_reactive.py b/captain/tests/test_reactive.py index 16f8de7..97cbc90 100644 --- a/captain/tests/test_reactive.py +++ b/captain/tests/test_reactive.py @@ -2,12 +2,21 @@ from captain.controllers.reactive import Flow from captain.types.flowchart import FCConnection, FlowChart, _Block +import time def test_add(): blocks = [ - _Block(id="constant1", block_type="flojoy.math.constant"), - _Block(id="constant2", block_type="flojoy.math.constant"), + _Block( + id="constant1", + block_type="flojoy.math.constant", + intrinsic_parameters={"val": 2}, + ), + _Block( + id="constant2", + block_type="flojoy.math.constant", + intrinsic_parameters={"val": 2}, + ), _Block(id="add", block_type="flojoy.math.arithmetic.add"), _Block(id="big_num", block_type="flojoy.visualization.big_num"), ] @@ -24,18 +33,21 @@ def test_add(): ), ] - fc = FlowChart.from_blocks_edges(blocks, edges) + fc = FlowChart.from_blocks_edges(blocks, edges, set()) outputs = {} start_obs = Subject() - def pub(x, id): + def pub(id, x): outputs[id] = x # TODO: Maybe we should do something like flow.wire() to trigger the # wire process instead of doing it upon construction? - _ = Flow(fc, pub, start_obs) + _ = Flow(fc, pub, start_obs, publish_sample_time=0) start_obs.on_next({}) + time.sleep(0.5) + print(outputs) + assert outputs["constant1"] == 2 assert outputs["constant2"] == 2 assert outputs["add"] == 4 diff --git a/captain/types/builtins.py b/captain/types/builtins.py new file mode 100644 index 0000000..ca06207 --- /dev/null +++ b/captain/types/builtins.py @@ -0,0 +1,2 @@ +class Ignore: + pass diff --git a/captain/types/events.py b/captain/types/events.py index aba72d6..ad38a8a 100644 --- a/captain/types/events.py +++ b/captain/types/events.py @@ -2,12 +2,13 @@ from pydantic import BaseModel, Field -from captain.types.flowchart import BlockID, ReactFlow +from captain.types.flowchart import BlockID, FunctionDefinition, ReactFlow class FlowStartEvent(BaseModel): event_type: Literal["start"] rf: ReactFlow + function_definitions: dict[str, FunctionDefinition] class FlowCancelEvent(BaseModel): diff --git a/captain/types/flowchart.py b/captain/types/flowchart.py index 5711914..1c689cb 100644 --- a/captain/types/flowchart.py +++ b/captain/types/flowchart.py @@ -1,15 +1,27 @@ -from typing import Literal, TypeAlias +from pprint import pformat +from typing import Annotated, Literal, Tuple, TypeAlias, TypeVar, Union, Generic, cast -from pydantic import BaseModel +from pydantic import BaseModel, Field + +from captain.logging import logger # TODO: This is hardcoded for now BlockType = Literal[ + "flojoy.control.toggle", "flojoy.control.slider", "flojoy.visualization.big_num", "flojoy.visualization.progress_bar", "flojoy.math.arithmetic.add", "flojoy.math.arithmetic.subtract", "flojoy.math.constant", + "flojoy.math.rand", + "flojoy.logic.sequence", + "flojoy.logic.clock", + "flojoy.logic.conditional", + "flojoy.logic.true", + "flojoy.logic.false", + "flojoy.visualization.bignum", + "flojoy.intrinsics.function", ] BlockID: TypeAlias = str @@ -25,14 +37,38 @@ # input param name and output param name. RFHandleID: TypeAlias = str +IntrinsicParameterValue: TypeAlias = str | int + -class RFNodeData(BaseModel): +class RFBuiltinBlockData(BaseModel): + label: str = "" block_type: BlockType + intrinsic_parameters: dict[str, IntrinsicParameterValue] = {} + + +class RFFunctionInstanceData(BaseModel): + block_type: Literal["function_instance"] + definition_block_id: BlockID + + +RFBlockData = Annotated[ + Union[RFBuiltinBlockData, RFFunctionInstanceData], + Field(discriminator="block_type"), +] + +BD = TypeVar("BD", bound=RFBlockData) -class RFNode(BaseModel): +# TODO: Replace this with a better way +# Using generics causes some issues with Pydantic's type inference, +# which makes it so you have to explicitly specify the type when +# instantiating this manually (when writing tests), which is kinda annoying +class RFNode(BaseModel, Generic[BD]): id: BlockID - data: RFNodeData + data: BD + + def __repr__(self): + return f"RFNode(id={self.id}, data={self.data})" class RFEdge(BaseModel): @@ -43,9 +79,24 @@ class RFEdge(BaseModel): class ReactFlow(BaseModel): - nodes: list[RFNode] + nodes: list[RFNode[RFBlockData]] + edges: list[RFEdge] + + +class FunctionDefinition(BaseModel): + block: RFNode[RFBuiltinBlockData] + nodes: list[RFNode[RFBlockData]] edges: list[RFEdge] + @staticmethod + def from_nodes_edges( + block: RFNode[RFBuiltinBlockData], + nodes_edges: Tuple[list[RFNode[RFBlockData]], list[RFEdge]], + ): + return FunctionDefinition( + block=block, nodes=nodes_edges[0], edges=nodes_edges[1] + ) + """ Flojoy specific flowchart types @@ -70,6 +121,9 @@ class _Block(BaseModel): id: BlockID block_type: BlockType + intrinsic_parameters: dict[str, IntrinsicParameterValue] = {} + inputs: dict[str, str] = {} + outputs: dict[str, str] = {} class FCBlock(_Block): @@ -78,20 +132,34 @@ class FCBlock(_Block): @staticmethod def from_block(block: _Block): - return FCBlock(id=block.id, block_type=block.block_type, ins=[], outs=[]) + return FCBlock( + id=block.id, + block_type=block.block_type, + ins=[], + outs=[], + intrinsic_parameters=block.intrinsic_parameters, + ) class FlowChart(BaseModel): blocks: list[FCBlock] @staticmethod - def from_blocks_edges(blocks: list[_Block], edges: list[FCConnection]): + def from_blocks_edges( + blocks: list[_Block], edges: list[FCConnection], function_blocks: set[BlockID] + ): block_lookup = {block.id: block for block in blocks} fc_blocks: dict[BlockID, FCBlock] = {} # TODO: This func can raise KeyError, needs more error handling logic # and throw a better error message. + logger.info(f"Function blocks: \n{pformat(function_blocks)}") + edges = join_function_edges(edges, function_blocks) + + logger.info(f"Blocks after join: \n{pformat(blocks)}") + logger.info(f"Edges after join: \n{pformat(edges)}") + for edge in edges: if edge.target not in fc_blocks: fc_blocks[edge.target] = FCBlock.from_block(block_lookup[edge.target]) @@ -101,21 +169,192 @@ def from_blocks_edges(blocks: list[_Block], edges: list[FCConnection]): fc_blocks[edge.source].outs.append(edge) for block_id, block in block_lookup.items(): - if block_id not in fc_blocks: + # Erase functions at runtime + if ( + block_id not in fc_blocks + and block.block_type != "flojoy.intrinsics.function" + ): fc_blocks[block_id] = FCBlock.from_block(block) return FlowChart(blocks=list(fc_blocks.values())) @staticmethod - def from_react_flow(rf: ReactFlow): - blocks = [_Block(id=n.id, block_type=n.data.block_type) for n in rf.nodes] - edges = [ - FCConnection( - target=e.target, - source=e.source, - sourceParam=e.sourceHandle, - targetParam=e.targetHandle, - ) - for e in rf.edges - ] - return FlowChart.from_blocks_edges(blocks, edges) + def from_react_flow( + rf: ReactFlow, function_definitions: dict[str, FunctionDefinition] | None = None + ): + nodes = rf.nodes + edges = rf.edges + logger.info(f"Function definitions: \n{pformat(function_definitions)}") + logger.info(f"Nodes ({len(nodes)}): \n{pformat(nodes)}") + logger.info(f"Edges ({len(edges)}): \n{pformat(edges)}") + + function_blocks = set() + + nodes, edges = inline_function_instances( + nodes, edges, function_definitions, function_blocks + ) + blocks, cons = convert_rf_nodes_edges(nodes, edges) + + return FlowChart.from_blocks_edges(blocks, cons, function_blocks) + + +def convert_rf_nodes_edges( + nodes: list[RFNode[RFBuiltinBlockData]], edges: list[RFEdge] +): + blocks = [ + _Block( + id=n.id, + block_type=n.data.block_type, + intrinsic_parameters=n.data.intrinsic_parameters, + ) + for n in nodes + ] + cons = [ + FCConnection( + target=e.target, + source=e.source, + sourceParam=e.sourceHandle, + targetParam=e.targetHandle, + ) + for e in edges + ] + + return blocks, cons + + +def inline_function_instances( + nodes: list[RFNode[RFBlockData]], + edges: list[RFEdge], + function_definitions: dict[str, FunctionDefinition] | None, + function_blocks: set[str], +) -> Tuple[list[RFNode[RFBuiltinBlockData]], list[RFEdge]]: + # TODO: Make things not blow up when you try to use recursion + # This may require rethinking how we deal with functions + if not function_definitions: + return cast(list[RFNode[RFBuiltinBlockData]], nodes), edges + + next_nodes: list[RFNode] = [] + next_edges: list[RFEdge] = edges[:] + + done_inlining = True + for node in nodes: + match node.data: + case RFBuiltinBlockData(): + if node.data.block_type == "flojoy.intrinsics.function": + function_blocks.add(node.id) + next_nodes.append(node) + case RFFunctionInstanceData(definition_block_id=defn_id): + function_blocks.add(node.id) + + logger.info(f"Inlining {pformat(node)}") + done_inlining = False + + defn = function_definitions[defn_id] + inlined_nodes = [ + RFNode(id=f"{node.id}-{body_node.id}", data=body_node.data) + for body_node in defn.nodes + ] + inlined_edges = [ + RFEdge( + # If the body edge is connected to the internal 'out', then we want to make the target + # the function instance's id instead so it can be joined later + target=f"{node.id}-{body_edge.target}" + if body_edge.target != defn.block.id + else node.id, + # Likewise, if the body edge is connected to the internal 'in', then we want to make the source + # the function instance's id instead + source=f"{node.id}-{body_edge.source}" + if body_edge.source != defn.block.id + else node.id, + targetHandle=body_edge.targetHandle, + sourceHandle=body_edge.sourceHandle, + ) + for body_edge in defn.edges + ] + + logger.info(f"Nodes added from inline: \n{pformat(inlined_nodes)}") + logger.info(f"Edges added from inline: \n{pformat(inlined_edges)}") + next_nodes.extend(inlined_nodes) + next_edges.extend(inlined_edges) + + if not done_inlining: + return inline_function_instances( + next_nodes, next_edges, function_definitions, function_blocks + ) + + return next_nodes, next_edges + + +def join_edges(e1: FCConnection, e2: FCConnection) -> FCConnection: + logger.info(f"Joining {e1.sourceParam} to {e2.targetParam}") + return FCConnection( + source=e1.source, + target=e2.target, + sourceParam=e1.sourceParam, + targetParam=e2.targetParam, + ) + + +INTERNAL_PREFIX = "FUNC-INTERNAL_" + + +# TODO: Come up with a better algorithm for this +def join_function_edges( + edges: list[FCConnection], function_blocks: set[BlockID] +) -> list[FCConnection]: + joined_edges = [] + while edges: + e1 = edges.pop() + src_func = e1.source in function_blocks + dst_func = e1.target in function_blocks + + logger.info(f"Processing edge {e1}") + logger.info(f"src_func: {src_func}") + logger.info(f"dst_func: {dst_func}") + + if not dst_func and not src_func: + joined_edges.append(e1) + continue + + # join A -> in and FUNC-INTERNAL_in -> B into A -> B + if dst_func and not e1.targetParam.startswith(INTERNAL_PREFIX): + joinable = [ + e2 + for e2 in edges + if e2.source == e1.target + and e2.sourceParam == f"{INTERNAL_PREFIX}{e1.targetParam}" + ] + for e2 in joinable: + edges.append(join_edges(e1, e2)) + # join FUNC-INTERNAL_in -> B and A -> in into A -> B + elif src_func and e1.sourceParam.startswith(INTERNAL_PREFIX): + param_name = e1.sourceParam.removeprefix(INTERNAL_PREFIX) + joinable = [ + e2 + for e2 in edges + if e2.target == e1.source and e2.targetParam == param_name + ] + for e2 in joinable: + edges.append(join_edges(e2, e1)) + # join C -> FUNC-INTERNAL_out and out -> D into C -> D + elif dst_func and e1.targetParam.startswith(INTERNAL_PREFIX): + param_name = e1.targetParam.removeprefix(INTERNAL_PREFIX) + joinable = [ + e2 + for e2 in edges + if e2.source == e1.target and e2.sourceParam == param_name + ] + for e2 in joinable: + edges.append(join_edges(e1, e2)) + # join out -> D and and C -> FUNC-INTERNAL_out into C -> D + elif src_func and not e1.sourceParam.startswith(INTERNAL_PREFIX): + joinable = [ + e2 + for e2 in edges + if e2.target == e1.source + and e2.targetParam == f"{INTERNAL_PREFIX}{e1.sourceParam}" + ] + for e2 in joinable: + edges.append(join_edges(e2, e1)) + + return joined_edges diff --git a/captain/utils/__init__.py b/captain/utils/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/captain/utils/list_utils.py b/captain/utils/list_utils.py new file mode 100644 index 0000000..31dee4a --- /dev/null +++ b/captain/utils/list_utils.py @@ -0,0 +1,15 @@ +from typing import Tuple, TypeVar, Callable + +T = TypeVar("T") + + +def partition_by(pred: Callable[[T], bool], xs: list[T]) -> Tuple[list[T], list[T]]: + matched, not_matched = [], [] + + for x in xs: + if pred(x): + matched.append(x) + else: + not_matched.append(x) + + return matched, not_matched diff --git a/package.json b/package.json index 3341d30..5ae43b6 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "@radix-ui/react-separator": "^1.0.3", "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-switch": "^1.0.3", + "@radix-ui/react-tabs": "^1.0.4", "@radix-ui/react-tooltip": "^1.0.7", "@tanstack/react-query": "^5.12.2", "@tanstack/react-query-devtools": "^5.13.3", @@ -53,7 +54,9 @@ "electron-updater": "^6.1.7", "fix-path": "^3.0.0", "gamepad.js": "^2.1.0", + "immer": "^10.0.3", "localforage": "^1.10.0", + "lodash": "^4.17.21", "lucide-react": "^0.294.0", "match-sorter": "^6.3.1", "react-router-dom": "^6.20.1", @@ -73,6 +76,7 @@ "@electron-toolkit/eslint-config-prettier": "^1.0.1", "@electron-toolkit/eslint-config-ts": "^1.0.0", "@electron-toolkit/tsconfig": "^1.0.1", + "@types/lodash": "^4.14.202", "@types/node": "^20.10.4", "@types/react": "^18.2.42", "@types/react-dom": "^18.2.17", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 93f406e..61ce7ac 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,7 +7,7 @@ settings: dependencies: '@codemirror/lang-python': specifier: ^6.1.3 - version: 6.1.3(@codemirror/state@6.3.3)(@codemirror/view@6.22.2)(@lezer/common@1.1.2) + version: 6.1.3(@codemirror/state@6.3.2)(@codemirror/view@6.22.1)(@lezer/common@1.1.1) '@electron-toolkit/preload': specifier: ^2.0.0 version: 2.0.0(electron@27.1.3) @@ -16,52 +16,55 @@ dependencies: version: 2.0.1(electron@27.1.3) '@radix-ui/react-alert-dialog': specifier: ^1.0.5 - version: 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + version: 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-checkbox': specifier: ^1.0.4 - version: 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + version: 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-dialog': specifier: ^1.0.5 - version: 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + version: 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-dropdown-menu': specifier: ^2.0.6 - version: 2.0.6(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + version: 2.0.6(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-icons': specifier: ^1.3.0 version: 1.3.0(react@18.2.0) '@radix-ui/react-label': specifier: ^2.0.2 - version: 2.0.2(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + version: 2.0.2(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-popover': specifier: ^1.0.7 - version: 1.0.7(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + version: 1.0.7(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-progress': specifier: ^1.0.3 - version: 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + version: 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-scroll-area': specifier: ^1.0.5 - version: 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + version: 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-select': specifier: ^2.0.0 - version: 2.0.0(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + version: 2.0.0(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-separator': specifier: ^1.0.3 - version: 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + version: 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-slot': specifier: ^1.0.2 - version: 1.0.2(@types/react@18.2.42)(react@18.2.0) + version: 1.0.2(@types/react@18.2.43)(react@18.2.0) '@radix-ui/react-switch': specifier: ^1.0.3 - version: 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + version: 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-tabs': + specifier: ^1.0.4 + version: 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-tooltip': specifier: ^1.0.7 - version: 1.0.7(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + version: 1.0.7(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) '@tanstack/react-query': specifier: ^5.12.2 version: 5.12.2(react@18.2.0) '@tanstack/react-query-devtools': specifier: ^5.13.3 - version: 5.13.3(@tanstack/react-query@5.12.2)(react@18.2.0) + version: 5.13.5(@tanstack/react-query@5.12.2)(react@18.2.0) '@tisoap/react-flow-smart-edge': specifier: ^3.0.0 version: 3.0.0(react-dom@18.2.0)(react@18.2.0)(reactflow@11.10.1)(typescript@5.3.3) @@ -73,7 +76,7 @@ dependencies: version: 10.44.1 '@uiw/react-codemirror': specifier: ^4.21.21 - version: 4.21.21(@babel/runtime@7.23.5)(@codemirror/autocomplete@6.11.1)(@codemirror/language@6.9.3)(@codemirror/lint@6.4.2)(@codemirror/search@6.5.5)(@codemirror/state@6.3.3)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.22.2)(codemirror@6.0.1)(react-dom@18.2.0)(react@18.2.0) + version: 4.21.21(@babel/runtime@7.23.5)(@codemirror/autocomplete@6.11.1)(@codemirror/language@6.9.3)(@codemirror/lint@6.4.2)(@codemirror/search@6.5.5)(@codemirror/state@6.3.2)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.22.1)(codemirror@6.0.1)(react-dom@18.2.0)(react@18.2.0) axios: specifier: ^1.6.2 version: 1.6.2 @@ -85,7 +88,7 @@ dependencies: version: 2.0.0 cmdk: specifier: ^0.2.0 - version: 0.2.0(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + version: 0.2.0(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) electron-log: specifier: ^5.0.1 version: 5.0.1 @@ -101,9 +104,15 @@ dependencies: gamepad.js: specifier: ^2.1.0 version: 2.1.0 + immer: + specifier: ^10.0.3 + version: 10.0.3 localforage: specifier: ^1.10.0 version: 1.10.0 + lodash: + specifier: ^4.17.21 + version: 4.17.21 lucide-react: specifier: ^0.294.0 version: 0.294.0(react@18.2.0) @@ -118,7 +127,7 @@ dependencies: version: 4.5.0(react-dom@18.2.0)(react@18.2.0) reactflow: specifier: ^11.10.1 - version: 11.10.1(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + version: 11.10.1(@types/react@18.2.43)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0) sonner: specifier: ^1.2.4 version: 1.2.4(react-dom@18.2.0)(react@18.2.0) @@ -133,7 +142,7 @@ dependencies: version: 1.0.7(tailwindcss@3.3.6) use-broadcast-ts: specifier: ^1.4.0 - version: 1.4.0(@types/react@18.2.42)(react@18.2.0) + version: 1.4.0(@types/react@18.2.43)(immer@10.0.3)(react@18.2.0) uuid: specifier: ^9.0.1 version: 9.0.1 @@ -145,7 +154,7 @@ dependencies: version: 3.22.4 zustand: specifier: ^4.4.7 - version: 4.4.7(@types/react@18.2.42)(react@18.2.0) + version: 4.4.7(@types/react@18.2.43)(immer@10.0.3)(react@18.2.0) devDependencies: '@electron-toolkit/eslint-config-prettier': @@ -157,12 +166,15 @@ devDependencies: '@electron-toolkit/tsconfig': specifier: ^1.0.1 version: 1.0.1(@types/node@20.10.4) + '@types/lodash': + specifier: ^4.14.202 + version: 4.14.202 '@types/node': specifier: ^20.10.4 version: 20.10.4 '@types/react': specifier: ^18.2.42 - version: 18.2.42 + version: 18.2.43 '@types/react-dom': specifier: ^18.2.17 version: 18.2.17 @@ -299,7 +311,7 @@ packages: dependencies: '@babel/compat-data': 7.23.5 '@babel/helper-validator-option': 7.23.5 - browserslist: 4.22.2 + browserslist: 4.22.1 lru-cache: 5.1.1 semver: 6.3.1 dev: true @@ -479,7 +491,7 @@ packages: to-fast-properties: 2.0.0 dev: true - /@codemirror/autocomplete@6.11.1(@codemirror/language@6.9.3)(@codemirror/state@6.3.3)(@codemirror/view@6.22.2)(@lezer/common@1.1.2): + /@codemirror/autocomplete@6.11.1(@codemirror/language@6.9.3)(@codemirror/state@6.3.2)(@codemirror/view@6.22.1)(@lezer/common@1.1.1): resolution: {integrity: sha512-L5UInv8Ffd6BPw0P3EF7JLYAMeEbclY7+6Q11REt8vhih8RuLreKtPy/xk8wPxs4EQgYqzI7cdgpiYwWlbS/ow==} peerDependencies: '@codemirror/language': ^6.0.0 @@ -488,24 +500,24 @@ packages: '@lezer/common': ^1.0.0 dependencies: '@codemirror/language': 6.9.3 - '@codemirror/state': 6.3.3 - '@codemirror/view': 6.22.2 - '@lezer/common': 1.1.2 + '@codemirror/state': 6.3.2 + '@codemirror/view': 6.22.1 + '@lezer/common': 1.1.1 dev: false /@codemirror/commands@6.3.2: resolution: {integrity: sha512-tjoi4MCWDNxgIpoLZ7+tezdS9OEB6pkiDKhfKx9ReJ/XBcs2G2RXIu+/FxXBlWsPTsz6C9q/r4gjzrsxpcnqCQ==} dependencies: '@codemirror/language': 6.9.3 - '@codemirror/state': 6.3.3 - '@codemirror/view': 6.22.2 - '@lezer/common': 1.1.2 + '@codemirror/state': 6.3.2 + '@codemirror/view': 6.22.1 + '@lezer/common': 1.1.1 dev: false - /@codemirror/lang-python@6.1.3(@codemirror/state@6.3.3)(@codemirror/view@6.22.2)(@lezer/common@1.1.2): + /@codemirror/lang-python@6.1.3(@codemirror/state@6.3.2)(@codemirror/view@6.22.1)(@lezer/common@1.1.1): resolution: {integrity: sha512-S9w2Jl74hFlD5nqtUMIaXAq9t5WlM0acCkyuQWUUSvZclk1sV+UfnpFiZzuZSG+hfEaOmxKR5UxY/Uxswn7EhQ==} dependencies: - '@codemirror/autocomplete': 6.11.1(@codemirror/language@6.9.3)(@codemirror/state@6.3.3)(@codemirror/view@6.22.2)(@lezer/common@1.1.2) + '@codemirror/autocomplete': 6.11.1(@codemirror/language@6.9.3)(@codemirror/state@6.3.2)(@codemirror/view@6.22.1)(@lezer/common@1.1.1) '@codemirror/language': 6.9.3 '@lezer/python': 1.1.9 transitivePeerDependencies: @@ -517,9 +529,9 @@ packages: /@codemirror/language@6.9.3: resolution: {integrity: sha512-qq48pYzoi6ldYWV/52+Z9Ou6QouVI+8YwvxFbUypI33NbjG2UeRHKENRyhwljTTiOqjQ33FjyZj6EREQ9apAOQ==} dependencies: - '@codemirror/state': 6.3.3 - '@codemirror/view': 6.22.2 - '@lezer/common': 1.1.2 + '@codemirror/state': 6.3.2 + '@codemirror/view': 6.22.1 + '@lezer/common': 1.1.1 '@lezer/highlight': 1.2.0 '@lezer/lr': 1.3.14 style-mod: 4.1.0 @@ -528,36 +540,36 @@ packages: /@codemirror/lint@6.4.2: resolution: {integrity: sha512-wzRkluWb1ptPKdzlsrbwwjYCPLgzU6N88YBAmlZi8WFyuiEduSd05MnJYNogzyc8rPK7pj6m95ptUApc8sHKVA==} dependencies: - '@codemirror/state': 6.3.3 - '@codemirror/view': 6.22.2 + '@codemirror/state': 6.3.2 + '@codemirror/view': 6.22.1 crelt: 1.0.6 dev: false /@codemirror/search@6.5.5: resolution: {integrity: sha512-PIEN3Ke1buPod2EHbJsoQwlbpkz30qGZKcnmH1eihq9+bPQx8gelauUwLYaY4vBOuBAuEhmpDLii4rj/uO0yMA==} dependencies: - '@codemirror/state': 6.3.3 - '@codemirror/view': 6.22.2 + '@codemirror/state': 6.3.2 + '@codemirror/view': 6.22.1 crelt: 1.0.6 dev: false - /@codemirror/state@6.3.3: - resolution: {integrity: sha512-0wufKcTw2dEwEaADajjHf6hBy1sh3M6V0e+q4JKIhLuiMSe5td5HOWpUdvKth1fT1M9VYOboajoBHpkCd7PG7A==} + /@codemirror/state@6.3.2: + resolution: {integrity: sha512-5jEikOfU0r9y+OTlZn5AEQB15mibu3deLBUp+GnLzVUNezEEuPt/JdSeniQNi+0YviblAvOPO2JQAlgJ3SYYaA==} dev: false /@codemirror/theme-one-dark@6.1.2: resolution: {integrity: sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==} dependencies: '@codemirror/language': 6.9.3 - '@codemirror/state': 6.3.3 - '@codemirror/view': 6.22.2 + '@codemirror/state': 6.3.2 + '@codemirror/view': 6.22.1 '@lezer/highlight': 1.2.0 dev: false - /@codemirror/view@6.22.2: - resolution: {integrity: sha512-cJp64cPXm7QfSBWEXK+76+hsZCGHupUgy8JAbSzMG6Lr0rfK73c1CaWITVW6hZVkOnAFxJTxd0PIuynNbzxYPw==} + /@codemirror/view@6.22.1: + resolution: {integrity: sha512-38BRn1nPqZqiHbmWfI8zri23IbRVbmSpSmh1E/Ysvc+lIGGdBC17K8zlK7ZU6fhfy9x4De9Zyj5JQqScPq5DkA==} dependencies: - '@codemirror/state': 6.3.3 + '@codemirror/state': 6.3.2 style-mod: 4.1.0 w3c-keyname: 2.2.8 dev: false @@ -593,8 +605,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/eslint-plugin': 6.13.2(@typescript-eslint/parser@6.13.2)(eslint@8.55.0)(typescript@5.3.3) - '@typescript-eslint/parser': 6.13.2(eslint@8.55.0)(typescript@5.3.3) + '@typescript-eslint/eslint-plugin': 6.13.1(@typescript-eslint/parser@6.13.1)(eslint@8.55.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.13.1(eslint@8.55.0)(typescript@5.3.3) eslint: 8.55.0 typescript: 5.3.3 transitivePeerDependencies: @@ -927,8 +939,8 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@floating-ui/core@1.5.2: - resolution: {integrity: sha512-Ii3MrfY/GAIN3OhXNzpCKaLxHQfJF9qvwq/kEJYdqDxeIHa01K8sldugal6TmeeXl+WMvhv9cnVzUTaFFJF09A==} + /@floating-ui/core@1.5.0: + resolution: {integrity: sha512-kK1h4m36DQ0UHGj5Ah4db7R0rHemTqqO0QLvUqi1/mUUp3LuAWbWxdxSIf/XsnH9VS6rRVPLJCncjRzUvyCLXg==} dependencies: '@floating-ui/utils': 0.1.6 dev: false @@ -936,7 +948,7 @@ packages: /@floating-ui/dom@1.5.3: resolution: {integrity: sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==} dependencies: - '@floating-ui/core': 1.5.2 + '@floating-ui/core': 1.5.0 '@floating-ui/utils': 0.1.6 dev: false @@ -1000,20 +1012,20 @@ packages: '@jridgewell/resolve-uri': 3.1.1 '@jridgewell/sourcemap-codec': 1.4.15 - /@lezer/common@1.1.2: - resolution: {integrity: sha512-V+GqBsga5+cQJMfM0GdnHmg4DgWvLzgMWjbldBg0+jC3k9Gu6nJNZDLJxXEBT1Xj8KhRN4jmbC5CY7SIL++sVw==} + /@lezer/common@1.1.1: + resolution: {integrity: sha512-aAPB9YbvZHqAW+bIwiuuTDGB4DG0sYNRObGLxud8cW7osw1ZQxfDuTZ8KQiqfZ0QJGcR34CvpTMDXEyo/+Htgg==} dev: false /@lezer/highlight@1.2.0: resolution: {integrity: sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==} dependencies: - '@lezer/common': 1.1.2 + '@lezer/common': 1.1.1 dev: false /@lezer/lr@1.3.14: resolution: {integrity: sha512-z5mY4LStlA3yL7aHT/rqgG614cfcvklS+8oFRFBYrs4YaWLJyKKM4+nN6KopToX0o9Hj6zmH6M5kinOYuy06ug==} dependencies: - '@lezer/common': 1.1.2 + '@lezer/common': 1.1.1 dev: false /@lezer/python@1.1.9: @@ -1090,7 +1102,7 @@ packages: '@babel/runtime': 7.23.5 dev: false - /@radix-ui/react-alert-dialog@1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-alert-dialog@1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-OrVIOcZL0tl6xibeuGt5/+UxoT2N27KCFOPjFyfXMnchxSHZ/OW7cCX2nGlIYJrbHK/fczPcFzAwvNBB6XBNMA==} peerDependencies: '@types/react': '*' @@ -1105,18 +1117,18 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-dialog': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-slot': 1.0.2(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-dialog': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-arrow@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-arrow@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==} peerDependencies: '@types/react': '*' @@ -1130,14 +1142,14 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-checkbox@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-checkbox@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-CBuGQa52aAYnADZVt/KBQzXrwx6TqnlwtcIPGtVt5JkkzQwMOLJjPukimhfKEr4GQNd43C+djUh5Ikopj8pSLg==} peerDependencies: '@types/react': '*' @@ -1152,20 +1164,20 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-use-previous': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-use-size': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-use-previous': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-use-size': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-collection@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-collection@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==} peerDependencies: '@types/react': '*' @@ -1179,11 +1191,11 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-slot': 1.0.2(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -1198,7 +1210,7 @@ packages: react: 18.2.0 dev: false - /@radix-ui/react-compose-refs@1.0.1(@types/react@18.2.42)(react@18.2.0): + /@radix-ui/react-compose-refs@1.0.1(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==} peerDependencies: '@types/react': '*' @@ -1208,7 +1220,7 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@types/react': 18.2.42 + '@types/react': 18.2.43 react: 18.2.0 dev: false @@ -1221,7 +1233,7 @@ packages: react: 18.2.0 dev: false - /@radix-ui/react-context@1.0.1(@types/react@18.2.42)(react@18.2.0): + /@radix-ui/react-context@1.0.1(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==} peerDependencies: '@types/react': '*' @@ -1231,11 +1243,11 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@types/react': 18.2.42 + '@types/react': 18.2.43 react: 18.2.0 dev: false - /@radix-ui/react-dialog@1.0.0(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-dialog@1.0.0(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-Yn9YU+QlHYLWwV1XfKiqnGVpWYWk6MeBVM6x/bcoyPvxgjQGoeT35482viLPctTMWoMw0PoHgqfSox7Ig+957Q==} peerDependencies: react: ^16.8 || ^17.0 || ^18.0 @@ -1257,12 +1269,12 @@ packages: aria-hidden: 1.2.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - react-remove-scroll: 2.5.4(@types/react@18.2.42)(react@18.2.0) + react-remove-scroll: 2.5.4(@types/react@18.2.43)(react@18.2.0) transitivePeerDependencies: - '@types/react' dev: false - /@radix-ui/react-dialog@1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-dialog@1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==} peerDependencies: '@types/react': '*' @@ -1277,26 +1289,26 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-id': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-slot': 1.0.2(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 aria-hidden: 1.2.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - react-remove-scroll: 2.5.5(@types/react@18.2.42)(react@18.2.0) + react-remove-scroll: 2.5.5(@types/react@18.2.43)(react@18.2.0) dev: false - /@radix-ui/react-direction@1.0.1(@types/react@18.2.42)(react@18.2.0): + /@radix-ui/react-direction@1.0.1(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==} peerDependencies: '@types/react': '*' @@ -1306,7 +1318,7 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@types/react': 18.2.42 + '@types/react': 18.2.43 react: 18.2.0 dev: false @@ -1326,7 +1338,7 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-dismissable-layer@1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-dismissable-layer@1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==} peerDependencies: '@types/react': '*' @@ -1341,17 +1353,17 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-dropdown-menu@2.0.6(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-dropdown-menu@2.0.6(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-i6TuFOoWmLWq+M/eCLGd/bQ2HfAX1RJgvrBQ6AQLmzfvsLdefxbWu8G9zczcPFfcSPehz9GcpF6K9QYreFV8hA==} peerDependencies: '@types/react': '*' @@ -1366,13 +1378,13 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-id': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-menu': 2.0.6(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-menu': 2.0.6(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -1387,7 +1399,7 @@ packages: react: 18.2.0 dev: false - /@radix-ui/react-focus-guards@1.0.1(@types/react@18.2.42)(react@18.2.0): + /@radix-ui/react-focus-guards@1.0.1(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==} peerDependencies: '@types/react': '*' @@ -1397,7 +1409,7 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@types/react': 18.2.42 + '@types/react': 18.2.43 react: 18.2.0 dev: false @@ -1415,7 +1427,7 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-focus-scope@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-focus-scope@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==} peerDependencies: '@types/react': '*' @@ -1429,10 +1441,10 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -1456,7 +1468,7 @@ packages: react: 18.2.0 dev: false - /@radix-ui/react-id@1.0.1(@types/react@18.2.42)(react@18.2.0): + /@radix-ui/react-id@1.0.1(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==} peerDependencies: '@types/react': '*' @@ -1466,12 +1478,12 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 react: 18.2.0 dev: false - /@radix-ui/react-label@2.0.2(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-label@2.0.2(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-N5ehvlM7qoTLx7nWPodsPYPgMzA5WM8zZChQg8nyFJKnDO5WHdba1vv5/H6IO5LtJMfD2Q3wh1qHFGNtK0w3bQ==} peerDependencies: '@types/react': '*' @@ -1485,14 +1497,14 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-menu@2.0.6(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-menu@2.0.6(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-BVkFLS+bUC8HcImkRKPSiVumA1VPOOEC5WBMiT+QAVsPzW1FJzI9KnqgGxVDPBcql5xXrHkD3JOVoXWEXD8SYA==} peerDependencies: '@types/react': '*' @@ -1507,30 +1519,30 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-direction': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-id': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-slot': 1.0.2(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 aria-hidden: 1.2.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - react-remove-scroll: 2.5.5(@types/react@18.2.42)(react@18.2.0) + react-remove-scroll: 2.5.5(@types/react@18.2.43)(react@18.2.0) dev: false - /@radix-ui/react-popover@1.0.7(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-popover@1.0.7(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-shtvVnlsxT6faMnK/a7n0wptwBD23xc1Z5mdrtKLwVEfsEMXodS0r5s0/g5P0hX//EKYZS2sxUjqfzlg52ZSnQ==} peerDependencies: '@types/react': '*' @@ -1545,27 +1557,27 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-id': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-slot': 1.0.2(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 aria-hidden: 1.2.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - react-remove-scroll: 2.5.5(@types/react@18.2.42)(react@18.2.0) + react-remove-scroll: 2.5.5(@types/react@18.2.43)(react@18.2.0) dev: false - /@radix-ui/react-popper@1.1.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-popper@1.1.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==} peerDependencies: '@types/react': '*' @@ -1580,16 +1592,16 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@floating-ui/react-dom': 2.0.4(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-use-rect': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-use-size': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-use-rect': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-use-size': 1.0.1(@types/react@18.2.43)(react@18.2.0) '@radix-ui/rect': 1.0.1 - '@types/react': 18.2.42 + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -1607,7 +1619,7 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-portal@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-portal@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==} peerDependencies: '@types/react': '*' @@ -1621,8 +1633,8 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -1641,7 +1653,7 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-presence@1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-presence@1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==} peerDependencies: '@types/react': '*' @@ -1655,9 +1667,9 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -1675,7 +1687,7 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-primitive@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-primitive@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==} peerDependencies: '@types/react': '*' @@ -1689,14 +1701,14 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-slot': 1.0.2(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-progress@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-progress@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-5G6Om/tYSxjSeEdrb1VfKkfZfn/1IlPWd731h2RfPuSbIfNUgfqAwbKfJCg/PP6nuUCTrYzalwHSpSinoWoCag==} peerDependencies: '@types/react': '*' @@ -1710,15 +1722,15 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-context': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-roving-focus@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-roving-focus@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==} peerDependencies: '@types/react': '*' @@ -1733,21 +1745,21 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-direction': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-id': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-scroll-area@1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-scroll-area@1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-b6PAgH4GQf9QEn8zbT2XUHpW5z8BzqEc7Kl11TwDrvuTrxlkcjTD5qa/bxgKr+nmuXKu4L/W5UZ4mlP/VG/5Gw==} peerDependencies: '@types/react': '*' @@ -1763,20 +1775,20 @@ packages: '@babel/runtime': 7.23.5 '@radix-ui/number': 1.0.1 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-direction': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-select@2.0.0(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-select@2.0.0(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-RH5b7af4oHtkcHS7pG6Sgv5rk5Wxa7XI8W5gvB1N/yiuDGZxko1ynvOiVhFM7Cis2A8zxF9bTOUVbRDzPepe6w==} peerDependencies: '@types/react': '*' @@ -1792,32 +1804,32 @@ packages: '@babel/runtime': 7.23.5 '@radix-ui/number': 1.0.1 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-direction': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-id': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-slot': 1.0.2(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-use-previous': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-visually-hidden': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-use-previous': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-visually-hidden': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 aria-hidden: 1.2.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - react-remove-scroll: 2.5.5(@types/react@18.2.42)(react@18.2.0) + react-remove-scroll: 2.5.5(@types/react@18.2.43)(react@18.2.0) dev: false - /@radix-ui/react-separator@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-separator@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-itYmTy/kokS21aiV5+Z56MZB54KrhPgn6eHDKkFeOLR34HMN2s8PaN47qZZAGnvupcjxHaFZnW4pQEh0BvvVuw==} peerDependencies: '@types/react': '*' @@ -1831,8 +1843,8 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -1848,7 +1860,7 @@ packages: react: 18.2.0 dev: false - /@radix-ui/react-slot@1.0.2(@types/react@18.2.42)(react@18.2.0): + /@radix-ui/react-slot@1.0.2(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==} peerDependencies: '@types/react': '*' @@ -1858,12 +1870,12 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 react: 18.2.0 dev: false - /@radix-ui/react-switch@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-switch@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-mxm87F88HyHztsI7N+ZUmEoARGkC22YVW5CaC+Byc+HRpuvCrOBPTAnXgf+tZ/7i0Sg/eOePGdMhUKhPaQEqow==} peerDependencies: '@types/react': '*' @@ -1878,19 +1890,47 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-use-previous': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-use-size': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-use-previous': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-use-size': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 + '@types/react-dom': 18.2.17 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-tabs@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-egZfYY/+wRNCflXNHx+dePvnz9FbmssDTJBtgRfDY7e8SE5oIo3Py2eCB1ckAbh1Q7cQ/6yJZThJ++sgbxibog==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-context': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-tooltip@1.0.7(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-tooltip@1.0.7(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-lPh5iKNFVQ/jav/j6ZrWq3blfDJ0OH9R6FlNUHPMqdLuQ9vwDgFsRxvl8b7Asuy5c8xmoojHUxKHQSOAvMHxyw==} peerDependencies: '@types/react': '*' @@ -1905,18 +1945,18 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-id': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-slot': 1.0.2(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@radix-ui/react-visually-hidden': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@radix-ui/react-visually-hidden': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -1931,7 +1971,7 @@ packages: react: 18.2.0 dev: false - /@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.2.42)(react@18.2.0): + /@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==} peerDependencies: '@types/react': '*' @@ -1941,7 +1981,7 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@types/react': 18.2.42 + '@types/react': 18.2.43 react: 18.2.0 dev: false @@ -1955,7 +1995,7 @@ packages: react: 18.2.0 dev: false - /@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.2.42)(react@18.2.0): + /@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==} peerDependencies: '@types/react': '*' @@ -1965,8 +2005,8 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 react: 18.2.0 dev: false @@ -1980,7 +2020,7 @@ packages: react: 18.2.0 dev: false - /@radix-ui/react-use-escape-keydown@1.0.3(@types/react@18.2.42)(react@18.2.0): + /@radix-ui/react-use-escape-keydown@1.0.3(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==} peerDependencies: '@types/react': '*' @@ -1990,8 +2030,8 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 react: 18.2.0 dev: false @@ -2004,7 +2044,7 @@ packages: react: 18.2.0 dev: false - /@radix-ui/react-use-layout-effect@1.0.1(@types/react@18.2.42)(react@18.2.0): + /@radix-ui/react-use-layout-effect@1.0.1(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==} peerDependencies: '@types/react': '*' @@ -2014,11 +2054,11 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@types/react': 18.2.42 + '@types/react': 18.2.43 react: 18.2.0 dev: false - /@radix-ui/react-use-previous@1.0.1(@types/react@18.2.42)(react@18.2.0): + /@radix-ui/react-use-previous@1.0.1(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw==} peerDependencies: '@types/react': '*' @@ -2028,11 +2068,11 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@types/react': 18.2.42 + '@types/react': 18.2.43 react: 18.2.0 dev: false - /@radix-ui/react-use-rect@1.0.1(@types/react@18.2.42)(react@18.2.0): + /@radix-ui/react-use-rect@1.0.1(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==} peerDependencies: '@types/react': '*' @@ -2043,11 +2083,11 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@radix-ui/rect': 1.0.1 - '@types/react': 18.2.42 + '@types/react': 18.2.43 react: 18.2.0 dev: false - /@radix-ui/react-use-size@1.0.1(@types/react@18.2.42)(react@18.2.0): + /@radix-ui/react-use-size@1.0.1(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==} peerDependencies: '@types/react': '*' @@ -2057,12 +2097,12 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.42)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.43)(react@18.2.0) + '@types/react': 18.2.43 react: 18.2.0 dev: false - /@radix-ui/react-visually-hidden@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-visually-hidden@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==} peerDependencies: '@types/react': '*' @@ -2076,8 +2116,8 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.5 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@types/react': 18.2.42 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.43 '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -2089,39 +2129,39 @@ packages: '@babel/runtime': 7.23.5 dev: false - /@reactflow/background@11.3.6(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@reactflow/background@11.3.6(@types/react@18.2.43)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-06FPlSUOOMALEEs+2PqPAbpqmL7WDjrkbG2UsDr2d6mbcDDhHiV4tu9FYoz44SQvXo7ma9VRotlsaR4OiRcYsg==} peerDependencies: react: '>=17' react-dom: '>=17' dependencies: - '@reactflow/core': 11.10.1(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@reactflow/core': 11.10.1(@types/react@18.2.43)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0) classcat: 5.0.4 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - zustand: 4.4.7(@types/react@18.2.42)(react@18.2.0) + zustand: 4.4.7(@types/react@18.2.43)(immer@10.0.3)(react@18.2.0) transitivePeerDependencies: - '@types/react' - immer dev: false - /@reactflow/controls@11.2.6(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@reactflow/controls@11.2.6(@types/react@18.2.43)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-4QHT92/ACVlZkvV+Hq44bAPV8WbMhkJl+/J0EbXcqQ1+an7cWJsF84eeelJw7R5J76RoaSSpKdsWsL2v7HAVlw==} peerDependencies: react: '>=17' react-dom: '>=17' dependencies: - '@reactflow/core': 11.10.1(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@reactflow/core': 11.10.1(@types/react@18.2.43)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0) classcat: 5.0.4 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - zustand: 4.4.7(@types/react@18.2.42)(react@18.2.0) + zustand: 4.4.7(@types/react@18.2.43)(immer@10.0.3)(react@18.2.0) transitivePeerDependencies: - '@types/react' - immer dev: false - /@reactflow/core@11.10.1(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@reactflow/core@11.10.1(@types/react@18.2.43)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-GIh3usY1W3eVobx//OO9+Cwm+5evQBBdPGxDaeXwm25UqPMWRI240nXQA5F/5gL5Mwpf0DUC7DR2EmrKNQy+Rw==} peerDependencies: react: '>=17' @@ -2137,19 +2177,19 @@ packages: d3-zoom: 3.0.0 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - zustand: 4.4.7(@types/react@18.2.42)(react@18.2.0) + zustand: 4.4.7(@types/react@18.2.43)(immer@10.0.3)(react@18.2.0) transitivePeerDependencies: - '@types/react' - immer dev: false - /@reactflow/minimap@11.7.6(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@reactflow/minimap@11.7.6(@types/react@18.2.43)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-kJEtyeQkTZYViLGebVWHVUJROMAGcvejvT+iX4DqKnFb5yK8E8LWlXQpRx2FrL9gDy80mJJaciy7IxnnQKE1bg==} peerDependencies: react: '>=17' react-dom: '>=17' dependencies: - '@reactflow/core': 11.10.1(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@reactflow/core': 11.10.1(@types/react@18.2.43)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0) '@types/d3-selection': 3.0.10 '@types/d3-zoom': 3.0.8 classcat: 5.0.4 @@ -2157,41 +2197,41 @@ packages: d3-zoom: 3.0.0 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - zustand: 4.4.7(@types/react@18.2.42)(react@18.2.0) + zustand: 4.4.7(@types/react@18.2.43)(immer@10.0.3)(react@18.2.0) transitivePeerDependencies: - '@types/react' - immer dev: false - /@reactflow/node-resizer@2.2.6(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@reactflow/node-resizer@2.2.6(@types/react@18.2.43)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-1Xb6q97uP7hRBLpog9sRCNfnsHdDgFRGEiU+lQqGgPEAeYwl4nRjWa/sXwH6ajniKxBhGEvrdzOgEFn6CRMcpQ==} peerDependencies: react: '>=17' react-dom: '>=17' dependencies: - '@reactflow/core': 11.10.1(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@reactflow/core': 11.10.1(@types/react@18.2.43)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0) classcat: 5.0.4 d3-drag: 3.0.0 d3-selection: 3.0.0 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - zustand: 4.4.7(@types/react@18.2.42)(react@18.2.0) + zustand: 4.4.7(@types/react@18.2.43)(immer@10.0.3)(react@18.2.0) transitivePeerDependencies: - '@types/react' - immer dev: false - /@reactflow/node-toolbar@1.3.6(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /@reactflow/node-toolbar@1.3.6(@types/react@18.2.43)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-JXDEuZ0wKjZ8z7qK2bIst0eZPzNyVEsiHL0e93EyuqT4fA9icoyE0fLq2ryNOOp7MXgId1h7LusnH6ta45F0yQ==} peerDependencies: react: '>=17' react-dom: '>=17' dependencies: - '@reactflow/core': 11.10.1(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@reactflow/core': 11.10.1(@types/react@18.2.43)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0) classcat: 5.0.4 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - zustand: 4.4.7(@types/react@18.2.42)(react@18.2.0) + zustand: 4.4.7(@types/react@18.2.43)(immer@10.0.3)(react@18.2.0) transitivePeerDependencies: - '@types/react' - immer @@ -2216,17 +2256,17 @@ packages: resolution: {integrity: sha512-WbZztNmKq0t6QjdNmHzezbi/uifYo9j6e2GLJkodsYaYUlzMbAp91RDyeHkIZrm7EfO4wa6Sm5sxJZm5SPlh6w==} dev: false - /@tanstack/query-devtools@5.13.3: - resolution: {integrity: sha512-1acztPKZexvM9Ns2T0aq4rMVSDA3VGdB73KF7zT/KNVl6VfnBvs24wuIRVSPZKqyZznZTzT3/DzcpntYqg9hmw==} + /@tanstack/query-devtools@5.13.5: + resolution: {integrity: sha512-effSYz9AWcZ6sNd+c8LCBYFIuDZApoCTXEpRlEYChBZpMz9QUUVMLToThwCyUY49+T5pANL3XxgZf3HV7hwJlg==} dev: false - /@tanstack/react-query-devtools@5.13.3(@tanstack/react-query@5.12.2)(react@18.2.0): - resolution: {integrity: sha512-ct58CMRrcjANRWCQ6cxzSUtme2jlX5au63+ckhMONob8bIk5VRfUEi4R49AWNJFL5haTBKe0InC0AV4bWi75VQ==} + /@tanstack/react-query-devtools@5.13.5(@tanstack/react-query@5.12.2)(react@18.2.0): + resolution: {integrity: sha512-FB17B/yPtwnqg+DAdosAM+rFj3t8Pl121MPLiUGgl6jvG0A+U9XN3n39zVbhurbdSFO5jCMkPBlloW4NH5ojrA==} peerDependencies: - '@tanstack/react-query': ^5.12.2 + '@tanstack/react-query': ^5.13.4 react: ^18.0.0 dependencies: - '@tanstack/query-devtools': 5.13.3 + '@tanstack/query-devtools': 5.13.5 '@tanstack/react-query': 5.12.2(react@18.2.0) react: 18.2.0 dev: false @@ -2252,7 +2292,7 @@ packages: pathfinding: 0.4.18 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - reactflow: 11.10.1(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + reactflow: 11.10.1(@types/react@18.2.43)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0) typescript: 5.3.3 dev: false @@ -2518,12 +2558,16 @@ packages: dependencies: '@types/node': 20.10.4 + /@types/lodash@4.14.202: + resolution: {integrity: sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==} + dev: true + /@types/ms@0.7.34: resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} dev: true - /@types/node@18.19.3: - resolution: {integrity: sha512-k5fggr14DwAytoA/t8rPrIz++lXK7/DqckthCmoZOKNsEbJkId4Z//BqgApXBUGrGddrigYa1oqheo/7YmW4rg==} + /@types/node@18.18.13: + resolution: {integrity: sha512-vXYZGRrSCreZmq1rEjMRLXJhiy8MrIeVasx+PCVlP414N7CJLHnMf+juVvjdprHyH+XRy3zKZLHeNueOpJCn0g==} dependencies: undici-types: 5.26.5 @@ -2547,14 +2591,14 @@ packages: /@types/react-dom@18.2.17: resolution: {integrity: sha512-rvrT/M7Df5eykWFxn6MYt5Pem/Dbyc1N8Y0S9Mrkw2WFCRiqUgw9P7ul2NpwsXCSM1DVdENzdG9J5SreqfAIWg==} dependencies: - '@types/react': 18.2.42 + '@types/react': 18.2.43 - /@types/react@18.2.42: - resolution: {integrity: sha512-c1zEr96MjakLYus/wPnuWDo1/zErfdU9rNsIGmE+NV71nx88FG9Ttgo5dqorXTu/LImX2f63WBP986gJkMPNbA==} + /@types/react@18.2.43: + resolution: {integrity: sha512-nvOV01ZdBdd/KW6FahSbcNplt2jCJfyWdTos61RYHV+FVv5L/g9AOX1bmbVcWcLFL8+KHQfh1zVIQrud6ihyQA==} dependencies: '@types/prop-types': 15.7.11 '@types/scheduler': 0.16.8 - csstype: 3.1.3 + csstype: 3.1.2 /@types/responselike@1.0.3: resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} @@ -2591,8 +2635,8 @@ packages: '@types/node': 20.10.4 optional: true - /@typescript-eslint/eslint-plugin@6.13.2(@typescript-eslint/parser@6.13.2)(eslint@8.55.0)(typescript@5.3.3): - resolution: {integrity: sha512-3+9OGAWHhk4O1LlcwLBONbdXsAhLjyCFogJY/cWy2lxdVJ2JrcTF2pTGMaLl2AE7U1l31n8Py4a8bx5DLf/0dQ==} + /@typescript-eslint/eslint-plugin@6.13.1(@typescript-eslint/parser@6.13.1)(eslint@8.55.0)(typescript@5.3.3): + resolution: {integrity: sha512-5bQDGkXaxD46bPvQt08BUz9YSaO4S0fB1LB5JHQuXTfkGPI3+UUeS387C/e9jRie5GqT8u5kFTrMvAjtX4O5kA==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha @@ -2603,11 +2647,11 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 6.13.2(eslint@8.55.0)(typescript@5.3.3) - '@typescript-eslint/scope-manager': 6.13.2 - '@typescript-eslint/type-utils': 6.13.2(eslint@8.55.0)(typescript@5.3.3) - '@typescript-eslint/utils': 6.13.2(eslint@8.55.0)(typescript@5.3.3) - '@typescript-eslint/visitor-keys': 6.13.2 + '@typescript-eslint/parser': 6.13.1(eslint@8.55.0)(typescript@5.3.3) + '@typescript-eslint/scope-manager': 6.13.1 + '@typescript-eslint/type-utils': 6.13.1(eslint@8.55.0)(typescript@5.3.3) + '@typescript-eslint/utils': 6.13.1(eslint@8.55.0)(typescript@5.3.3) + '@typescript-eslint/visitor-keys': 6.13.1 debug: 4.3.4 eslint: 8.55.0 graphemer: 1.4.0 @@ -2620,8 +2664,8 @@ packages: - supports-color dev: true - /@typescript-eslint/parser@6.13.2(eslint@8.55.0)(typescript@5.3.3): - resolution: {integrity: sha512-MUkcC+7Wt/QOGeVlM8aGGJZy1XV5YKjTpq9jK6r6/iLsGXhBVaGP5N0UYvFsu9BFlSpwY9kMretzdBH01rkRXg==} + /@typescript-eslint/parser@6.13.1(eslint@8.55.0)(typescript@5.3.3): + resolution: {integrity: sha512-fs2XOhWCzRhqMmQf0eicLa/CWSaYss2feXsy7xBD/pLyWke/jCIVc2s1ikEAtSW7ina1HNhv7kONoEfVNEcdDQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -2630,10 +2674,10 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 6.13.2 - '@typescript-eslint/types': 6.13.2 - '@typescript-eslint/typescript-estree': 6.13.2(typescript@5.3.3) - '@typescript-eslint/visitor-keys': 6.13.2 + '@typescript-eslint/scope-manager': 6.13.1 + '@typescript-eslint/types': 6.13.1 + '@typescript-eslint/typescript-estree': 6.13.1(typescript@5.3.3) + '@typescript-eslint/visitor-keys': 6.13.1 debug: 4.3.4 eslint: 8.55.0 typescript: 5.3.3 @@ -2641,16 +2685,16 @@ packages: - supports-color dev: true - /@typescript-eslint/scope-manager@6.13.2: - resolution: {integrity: sha512-CXQA0xo7z6x13FeDYCgBkjWzNqzBn8RXaE3QVQVIUm74fWJLkJkaHmHdKStrxQllGh6Q4eUGyNpMe0b1hMkXFA==} + /@typescript-eslint/scope-manager@6.13.1: + resolution: {integrity: sha512-BW0kJ7ceiKi56GbT2KKzZzN+nDxzQK2DS6x0PiSMPjciPgd/JRQGMibyaN2cPt2cAvuoH0oNvn2fwonHI+4QUQ==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.13.2 - '@typescript-eslint/visitor-keys': 6.13.2 + '@typescript-eslint/types': 6.13.1 + '@typescript-eslint/visitor-keys': 6.13.1 dev: true - /@typescript-eslint/type-utils@6.13.2(eslint@8.55.0)(typescript@5.3.3): - resolution: {integrity: sha512-Qr6ssS1GFongzH2qfnWKkAQmMUyZSyOr0W54nZNU1MDfo+U4Mv3XveeLZzadc/yq8iYhQZHYT+eoXJqnACM1tw==} + /@typescript-eslint/type-utils@6.13.1(eslint@8.55.0)(typescript@5.3.3): + resolution: {integrity: sha512-A2qPlgpxx2v//3meMqQyB1qqTg1h1dJvzca7TugM3Yc2USDY+fsRBiojAEo92HO7f5hW5mjAUF6qobOPzlBCBQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -2659,8 +2703,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 6.13.2(typescript@5.3.3) - '@typescript-eslint/utils': 6.13.2(eslint@8.55.0)(typescript@5.3.3) + '@typescript-eslint/typescript-estree': 6.13.1(typescript@5.3.3) + '@typescript-eslint/utils': 6.13.1(eslint@8.55.0)(typescript@5.3.3) debug: 4.3.4 eslint: 8.55.0 ts-api-utils: 1.0.3(typescript@5.3.3) @@ -2669,13 +2713,13 @@ packages: - supports-color dev: true - /@typescript-eslint/types@6.13.2: - resolution: {integrity: sha512-7sxbQ+EMRubQc3wTfTsycgYpSujyVbI1xw+3UMRUcrhSy+pN09y/lWzeKDbvhoqcRbHdc+APLs/PWYi/cisLPg==} + /@typescript-eslint/types@6.13.1: + resolution: {integrity: sha512-gjeEskSmiEKKFIbnhDXUyiqVma1gRCQNbVZ1C8q7Zjcxh3WZMbzWVfGE9rHfWd1msQtPS0BVD9Jz9jded44eKg==} engines: {node: ^16.0.0 || >=18.0.0} dev: true - /@typescript-eslint/typescript-estree@6.13.2(typescript@5.3.3): - resolution: {integrity: sha512-SuD8YLQv6WHnOEtKv8D6HZUzOub855cfPnPMKvdM/Bh1plv1f7Q/0iFUDLKKlxHcEstQnaUU4QZskgQq74t+3w==} + /@typescript-eslint/typescript-estree@6.13.1(typescript@5.3.3): + resolution: {integrity: sha512-sBLQsvOC0Q7LGcUHO5qpG1HxRgePbT6wwqOiGLpR8uOJvPJbfs0mW3jPA3ujsDvfiVwVlWUDESNXv44KtINkUQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: typescript: '*' @@ -2683,8 +2727,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 6.13.2 - '@typescript-eslint/visitor-keys': 6.13.2 + '@typescript-eslint/types': 6.13.1 + '@typescript-eslint/visitor-keys': 6.13.1 debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 @@ -2695,8 +2739,8 @@ packages: - supports-color dev: true - /@typescript-eslint/utils@6.13.2(eslint@8.55.0)(typescript@5.3.3): - resolution: {integrity: sha512-b9Ptq4eAZUym4idijCRzl61oPCwwREcfDI8xGk751Vhzig5fFZR9CyzDz4Sp/nxSLBYxUPyh4QdIDqWykFhNmQ==} + /@typescript-eslint/utils@6.13.1(eslint@8.55.0)(typescript@5.3.3): + resolution: {integrity: sha512-ouPn/zVoan92JgAegesTXDB/oUp6BP1v8WpfYcqh649ejNc9Qv+B4FF2Ff626kO1xg0wWwwG48lAJ4JuesgdOw==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -2704,9 +2748,9 @@ packages: '@eslint-community/eslint-utils': 4.4.0(eslint@8.55.0) '@types/json-schema': 7.0.15 '@types/semver': 7.5.6 - '@typescript-eslint/scope-manager': 6.13.2 - '@typescript-eslint/types': 6.13.2 - '@typescript-eslint/typescript-estree': 6.13.2(typescript@5.3.3) + '@typescript-eslint/scope-manager': 6.13.1 + '@typescript-eslint/types': 6.13.1 + '@typescript-eslint/typescript-estree': 6.13.1(typescript@5.3.3) eslint: 8.55.0 semver: 7.5.4 transitivePeerDependencies: @@ -2714,15 +2758,15 @@ packages: - typescript dev: true - /@typescript-eslint/visitor-keys@6.13.2: - resolution: {integrity: sha512-OGznFs0eAQXJsp+xSd6k/O1UbFi/K/L7WjqeRoFE7vadjAF9y0uppXhYNQNEqygjou782maGClOoZwPqF0Drlw==} + /@typescript-eslint/visitor-keys@6.13.1: + resolution: {integrity: sha512-NDhQUy2tg6XGNBGDRm1XybOHSia8mcXmlbKWoQP+nm1BIIMxa55shyJfZkHpEBN62KNPLrocSM2PdPcaLgDKMQ==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.13.2 + '@typescript-eslint/types': 6.13.1 eslint-visitor-keys: 3.4.3 dev: true - /@uiw/codemirror-extensions-basic-setup@4.21.21(@codemirror/autocomplete@6.11.1)(@codemirror/commands@6.3.2)(@codemirror/language@6.9.3)(@codemirror/lint@6.4.2)(@codemirror/search@6.5.5)(@codemirror/state@6.3.3)(@codemirror/view@6.22.2): + /@uiw/codemirror-extensions-basic-setup@4.21.21(@codemirror/autocomplete@6.11.1)(@codemirror/commands@6.3.2)(@codemirror/language@6.9.3)(@codemirror/lint@6.4.2)(@codemirror/search@6.5.5)(@codemirror/state@6.3.2)(@codemirror/view@6.22.1): resolution: {integrity: sha512-+0i9dPrRSa8Mf0CvyrMvnAhajnqwsP3IMRRlaHDRgsSGL8igc4z7MhvUPn+7cWFAAqWzQRhMdMSWzo6/TEa3EA==} peerDependencies: '@codemirror/autocomplete': '>=6.0.0' @@ -2733,16 +2777,16 @@ packages: '@codemirror/state': '>=6.0.0' '@codemirror/view': '>=6.0.0' dependencies: - '@codemirror/autocomplete': 6.11.1(@codemirror/language@6.9.3)(@codemirror/state@6.3.3)(@codemirror/view@6.22.2)(@lezer/common@1.1.2) + '@codemirror/autocomplete': 6.11.1(@codemirror/language@6.9.3)(@codemirror/state@6.3.2)(@codemirror/view@6.22.1)(@lezer/common@1.1.1) '@codemirror/commands': 6.3.2 '@codemirror/language': 6.9.3 '@codemirror/lint': 6.4.2 '@codemirror/search': 6.5.5 - '@codemirror/state': 6.3.3 - '@codemirror/view': 6.22.2 + '@codemirror/state': 6.3.2 + '@codemirror/view': 6.22.1 dev: false - /@uiw/react-codemirror@4.21.21(@babel/runtime@7.23.5)(@codemirror/autocomplete@6.11.1)(@codemirror/language@6.9.3)(@codemirror/lint@6.4.2)(@codemirror/search@6.5.5)(@codemirror/state@6.3.3)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.22.2)(codemirror@6.0.1)(react-dom@18.2.0)(react@18.2.0): + /@uiw/react-codemirror@4.21.21(@babel/runtime@7.23.5)(@codemirror/autocomplete@6.11.1)(@codemirror/language@6.9.3)(@codemirror/lint@6.4.2)(@codemirror/search@6.5.5)(@codemirror/state@6.3.2)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.22.1)(codemirror@6.0.1)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-PaxBMarufMWoR0qc5zuvBSt76rJ9POm9qoOaJbqRmnNL2viaF+d+Paf2blPSlm1JSnqn7hlRjio+40nZJ9TKzw==} peerDependencies: '@babel/runtime': '>=7.11.0' @@ -2755,11 +2799,11 @@ packages: dependencies: '@babel/runtime': 7.23.5 '@codemirror/commands': 6.3.2 - '@codemirror/state': 6.3.3 + '@codemirror/state': 6.3.2 '@codemirror/theme-one-dark': 6.1.2 - '@codemirror/view': 6.22.2 - '@uiw/codemirror-extensions-basic-setup': 4.21.21(@codemirror/autocomplete@6.11.1)(@codemirror/commands@6.3.2)(@codemirror/language@6.9.3)(@codemirror/lint@6.4.2)(@codemirror/search@6.5.5)(@codemirror/state@6.3.3)(@codemirror/view@6.22.2) - codemirror: 6.0.1(@lezer/common@1.1.2) + '@codemirror/view': 6.22.1 + '@uiw/codemirror-extensions-basic-setup': 4.21.21(@codemirror/autocomplete@6.11.1)(@codemirror/commands@6.3.2)(@codemirror/language@6.9.3)(@codemirror/lint@6.4.2)(@codemirror/search@6.5.5)(@codemirror/state@6.3.2)(@codemirror/view@6.22.1) + codemirror: 6.0.1(@lezer/common@1.1.1) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) transitivePeerDependencies: @@ -3032,8 +3076,8 @@ packages: peerDependencies: postcss: ^8.1.0 dependencies: - browserslist: 4.22.2 - caniuse-lite: 1.0.30001566 + browserslist: 4.22.1 + caniuse-lite: 1.0.30001565 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.0.0 @@ -3113,15 +3157,15 @@ packages: dependencies: fill-range: 7.0.1 - /browserslist@4.22.2: - resolution: {integrity: sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==} + /browserslist@4.22.1: + resolution: {integrity: sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001566 - electron-to-chromium: 1.4.609 - node-releases: 2.0.14 - update-browserslist-db: 1.0.13(browserslist@4.22.2) + caniuse-lite: 1.0.30001565 + electron-to-chromium: 1.4.596 + node-releases: 2.0.13 + update-browserslist-db: 1.0.13(browserslist@4.22.1) dev: true /buffer-crc32@0.2.13: @@ -3222,8 +3266,8 @@ packages: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} - /caniuse-lite@1.0.30001566: - resolution: {integrity: sha512-ggIhCsTxmITBAMmK8yZjEhCO5/47jKXPu6Dha/wuCS4JePVL+3uiDEBuhu2aIoT+bqTOR8L76Ip1ARL9xYsEJA==} + /caniuse-lite@1.0.30001565: + resolution: {integrity: sha512-xrE//a3O7TP0vaJ8ikzkD2c2NgcVUvsEe2IvFTntV4Yd1Z9FVzh+gW+enX96L0psrbaFMcVcH2l90xNuGDWc8w==} dev: true /chalk@2.4.2: @@ -3310,13 +3354,13 @@ packages: engines: {node: '>=6'} dev: false - /cmdk@0.2.0(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /cmdk@0.2.0(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-JQpKvEOb86SnvMZbYaFKYhvzFntWBeSZdyii0rZPhKJj9uwJBxu4DaVYDrRN7r3mPop56oPhRw+JYWTKs66TYw==} peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 dependencies: - '@radix-ui/react-dialog': 1.0.0(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-dialog': 1.0.0(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) command-score: 0.1.2 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -3324,16 +3368,16 @@ packages: - '@types/react' dev: false - /codemirror@6.0.1(@lezer/common@1.1.2): + /codemirror@6.0.1(@lezer/common@1.1.1): resolution: {integrity: sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==} dependencies: - '@codemirror/autocomplete': 6.11.1(@codemirror/language@6.9.3)(@codemirror/state@6.3.3)(@codemirror/view@6.22.2)(@lezer/common@1.1.2) + '@codemirror/autocomplete': 6.11.1(@codemirror/language@6.9.3)(@codemirror/state@6.3.2)(@codemirror/view@6.22.1)(@lezer/common@1.1.1) '@codemirror/commands': 6.3.2 '@codemirror/language': 6.9.3 '@codemirror/lint': 6.4.2 '@codemirror/search': 6.5.5 - '@codemirror/state': 6.3.3 - '@codemirror/view': 6.22.2 + '@codemirror/state': 6.3.2 + '@codemirror/view': 6.22.1 transitivePeerDependencies: - '@lezer/common' dev: false @@ -3459,8 +3503,8 @@ packages: engines: {node: '>=4'} hasBin: true - /csstype@3.1.3: - resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + /csstype@3.1.2: + resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} /d3-color@3.1.0: resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} @@ -3748,8 +3792,8 @@ packages: - supports-color dev: true - /electron-to-chromium@1.4.609: - resolution: {integrity: sha512-ihiCP7PJmjoGNuLpl7TjNA8pCQWu09vGyjlPYw1Rqww4gvNuCcmvl+44G+2QyJ6S2K4o+wbTS++Xz0YN8Q9ERw==} + /electron-to-chromium@1.4.596: + resolution: {integrity: sha512-zW3zbZ40Icb2BCWjm47nxwcFGYlIgdXkAx85XDO7cyky9J4QQfq8t0W19/TLZqq3JPQXtlv8BPIGmfa9Jb4scg==} dev: true /electron-trpc@0.5.2(@trpc/client@10.44.1)(@trpc/server@10.44.1)(electron@27.1.3): @@ -3811,7 +3855,7 @@ packages: requiresBuild: true dependencies: '@electron/get': 2.0.3 - '@types/node': 18.19.3 + '@types/node': 18.18.13 extract-zip: 2.0.1 transitivePeerDependencies: - supports-color @@ -3864,7 +3908,7 @@ packages: is-weakref: 1.0.2 object-inspect: 1.13.1 object-keys: 1.1.1 - object.assign: 4.1.5 + object.assign: 4.1.4 regexp.prototype.flags: 1.5.1 safe-array-concat: 1.0.1 safe-regex-test: 1.0.0 @@ -3998,7 +4042,7 @@ packages: eslint-config-prettier: 8.10.0(eslint@8.55.0) prettier: 3.1.0 prettier-linter-helpers: 1.0.0 - synckit: 0.8.6 + synckit: 0.8.5 dev: true /eslint-plugin-react@7.33.2(eslint@8.55.0): @@ -4638,6 +4682,10 @@ packages: /immediate@3.0.6: resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + /immer@10.0.3: + resolution: {integrity: sha512-pwupu3eWfouuaowscykeckFmVTpqbzW+rXFCX8rQLkZzM9ftBmU/++Ra+o+L27mz03zJTlyV4UUr+fdKNffo4A==} + dev: false + /import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} @@ -4987,7 +5035,7 @@ packages: dependencies: array-includes: 3.1.7 array.prototype.flat: 1.3.2 - object.assign: 4.1.5 + object.assign: 4.1.4 object.values: 1.1.7 dev: true @@ -5066,7 +5114,6 @@ packages: /lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - dev: true /loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} @@ -5250,8 +5297,8 @@ packages: dev: true optional: true - /node-releases@2.0.14: - resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + /node-releases@2.0.13: + resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==} dev: true /normalize-path@3.0.0: @@ -5309,8 +5356,8 @@ packages: engines: {node: '>=0.8.0'} dev: false - /object.assign@4.1.5: - resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} + /object.assign@4.1.4: + resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.5 @@ -5710,7 +5757,7 @@ packages: engines: {node: '>=0.10.0'} dev: true - /react-remove-scroll-bar@2.3.4(@types/react@18.2.42)(react@18.2.0): + /react-remove-scroll-bar@2.3.4(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==} engines: {node: '>=10'} peerDependencies: @@ -5720,13 +5767,13 @@ packages: '@types/react': optional: true dependencies: - '@types/react': 18.2.42 + '@types/react': 18.2.43 react: 18.2.0 - react-style-singleton: 2.2.1(@types/react@18.2.42)(react@18.2.0) + react-style-singleton: 2.2.1(@types/react@18.2.43)(react@18.2.0) tslib: 2.6.2 dev: false - /react-remove-scroll@2.5.4(@types/react@18.2.42)(react@18.2.0): + /react-remove-scroll@2.5.4(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-xGVKJJr0SJGQVirVFAUZ2k1QLyO6m+2fy0l8Qawbp5Jgrv3DeLalrfMNBFSlmz5kriGGzsVBtGVnf4pTKIhhWA==} engines: {node: '>=10'} peerDependencies: @@ -5736,16 +5783,16 @@ packages: '@types/react': optional: true dependencies: - '@types/react': 18.2.42 + '@types/react': 18.2.43 react: 18.2.0 - react-remove-scroll-bar: 2.3.4(@types/react@18.2.42)(react@18.2.0) - react-style-singleton: 2.2.1(@types/react@18.2.42)(react@18.2.0) + react-remove-scroll-bar: 2.3.4(@types/react@18.2.43)(react@18.2.0) + react-style-singleton: 2.2.1(@types/react@18.2.43)(react@18.2.0) tslib: 2.6.2 - use-callback-ref: 1.3.0(@types/react@18.2.42)(react@18.2.0) - use-sidecar: 1.1.2(@types/react@18.2.42)(react@18.2.0) + use-callback-ref: 1.3.0(@types/react@18.2.43)(react@18.2.0) + use-sidecar: 1.1.2(@types/react@18.2.43)(react@18.2.0) dev: false - /react-remove-scroll@2.5.5(@types/react@18.2.42)(react@18.2.0): + /react-remove-scroll@2.5.5(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==} engines: {node: '>=10'} peerDependencies: @@ -5755,13 +5802,13 @@ packages: '@types/react': optional: true dependencies: - '@types/react': 18.2.42 + '@types/react': 18.2.43 react: 18.2.0 - react-remove-scroll-bar: 2.3.4(@types/react@18.2.42)(react@18.2.0) - react-style-singleton: 2.2.1(@types/react@18.2.42)(react@18.2.0) + react-remove-scroll-bar: 2.3.4(@types/react@18.2.43)(react@18.2.0) + react-style-singleton: 2.2.1(@types/react@18.2.43)(react@18.2.0) tslib: 2.6.2 - use-callback-ref: 1.3.0(@types/react@18.2.42)(react@18.2.0) - use-sidecar: 1.1.2(@types/react@18.2.42)(react@18.2.0) + use-callback-ref: 1.3.0(@types/react@18.2.43)(react@18.2.0) + use-sidecar: 1.1.2(@types/react@18.2.43)(react@18.2.0) dev: false /react-router-dom@6.20.1(react-dom@18.2.0)(react@18.2.0): @@ -5787,7 +5834,7 @@ packages: react: 18.2.0 dev: false - /react-style-singleton@2.2.1(@types/react@18.2.42)(react@18.2.0): + /react-style-singleton@2.2.1(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==} engines: {node: '>=10'} peerDependencies: @@ -5797,7 +5844,7 @@ packages: '@types/react': optional: true dependencies: - '@types/react': 18.2.42 + '@types/react': 18.2.43 get-nonce: 1.0.1 invariant: 2.2.4 react: 18.2.0 @@ -5820,18 +5867,18 @@ packages: dependencies: loose-envify: 1.4.0 - /reactflow@11.10.1(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + /reactflow@11.10.1(@types/react@18.2.43)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-Q616fElAc5/N37tMwjuRkkgm/VgmnLLTNNCj61z5mvJxae+/VXZQMfot1K6a5LLz9G3SVKqU97PMb9Ga1PRXew==} peerDependencies: react: '>=17' react-dom: '>=17' dependencies: - '@reactflow/background': 11.3.6(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@reactflow/controls': 11.2.6(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@reactflow/core': 11.10.1(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@reactflow/minimap': 11.7.6(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@reactflow/node-resizer': 2.2.6(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) - '@reactflow/node-toolbar': 1.3.6(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@reactflow/background': 11.3.6(@types/react@18.2.43)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0) + '@reactflow/controls': 11.2.6(@types/react@18.2.43)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0) + '@reactflow/core': 11.10.1(@types/react@18.2.43)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0) + '@reactflow/minimap': 11.7.6(@types/react@18.2.43)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0) + '@reactflow/node-resizer': 2.2.6(@types/react@18.2.43)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0) + '@reactflow/node-toolbar': 1.3.6(@types/react@18.2.43)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) transitivePeerDependencies: @@ -6336,8 +6383,8 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - /synckit@0.8.6: - resolution: {integrity: sha512-laHF2savN6sMeHCjLRkheIU4wo3Zg9Ln5YOjOo7sZ5dVQW8yF5pPE5SIw1dsPhq3TRp1jisKRCdPhfs/1WMqDA==} + /synckit@0.8.5: + resolution: {integrity: sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==} engines: {node: ^14.18.0 || >=16.0.0} dependencies: '@pkgr/utils': 2.4.2 @@ -6585,13 +6632,13 @@ packages: yaku: 0.16.7 dev: true - /update-browserslist-db@1.0.13(browserslist@4.22.2): + /update-browserslist-db@1.0.13(browserslist@4.22.1): resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' dependencies: - browserslist: 4.22.2 + browserslist: 4.22.1 escalade: 3.1.1 picocolors: 1.0.0 dev: true @@ -6602,20 +6649,20 @@ packages: punycode: 2.3.1 dev: true - /use-broadcast-ts@1.4.0(@types/react@18.2.42)(react@18.2.0): + /use-broadcast-ts@1.4.0(@types/react@18.2.43)(immer@10.0.3)(react@18.2.0): resolution: {integrity: sha512-VvcWEewTbUgL+fqS0VLsRNpK8NYf815BDTd8cj1C1fbacCeGA6aXhSeVo3Zf5coG32/4MHz2gjlm2o8v9tJu1Q==} peerDependencies: react: '>=18.0' dependencies: react: 18.2.0 optionalDependencies: - zustand: 4.4.7(@types/react@18.2.42)(react@18.2.0) + zustand: 4.4.7(@types/react@18.2.43)(immer@10.0.3)(react@18.2.0) transitivePeerDependencies: - '@types/react' - immer dev: false - /use-callback-ref@1.3.0(@types/react@18.2.42)(react@18.2.0): + /use-callback-ref@1.3.0(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==} engines: {node: '>=10'} peerDependencies: @@ -6625,12 +6672,12 @@ packages: '@types/react': optional: true dependencies: - '@types/react': 18.2.42 + '@types/react': 18.2.43 react: 18.2.0 tslib: 2.6.2 dev: false - /use-sidecar@1.1.2(@types/react@18.2.42)(react@18.2.0): + /use-sidecar@1.1.2(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==} engines: {node: '>=10'} peerDependencies: @@ -6640,7 +6687,7 @@ packages: '@types/react': optional: true dependencies: - '@types/react': 18.2.42 + '@types/react': 18.2.43 detect-node-es: 1.1.0 react: 18.2.0 tslib: 2.6.2 @@ -6868,7 +6915,7 @@ packages: resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} dev: false - /zustand@4.4.7(@types/react@18.2.42)(react@18.2.0): + /zustand@4.4.7(@types/react@18.2.43)(immer@10.0.3)(react@18.2.0): resolution: {integrity: sha512-QFJWJMdlETcI69paJwhSMJz7PPWjVP8Sjhclxmxmxv/RYI7ZOvR5BHX+ktH0we9gTWQMxcne8q1OY8xxz604gw==} engines: {node: '>=12.7.0'} peerDependencies: @@ -6883,7 +6930,8 @@ packages: react: optional: true dependencies: - '@types/react': 18.2.42 + '@types/react': 18.2.43 + immer: 10.0.3 react: 18.2.0 use-sync-external-store: 1.2.0(react@18.2.0) dev: false diff --git a/poetry.lock b/poetry.lock index ec80133..38da5fc 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.0 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. [[package]] name = "annotated-types" @@ -2746,37 +2746,51 @@ python-versions = ">=3.6" files = [ {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b42169467c42b692c19cf539c38d4602069d8c1505e97b86387fcf7afb766e1d"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:07238db9cbdf8fc1e9de2489a4f68474e70dffcb32232db7c08fa61ca0c7c462"}, - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:d92f81886165cb14d7b067ef37e142256f1c6a90a65cd156b063a43da1708cfd"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fff3573c2db359f091e1589c3d7c5fc2f86f5bdb6f24252c2d8e539d4e45f412"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:aa2267c6a303eb483de8d02db2871afb5c5fc15618d894300b88958f729ad74f"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:840f0c7f194986a63d2c2465ca63af8ccbbc90ab1c6001b1978f05119b5e7334"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:024cfe1fc7c7f4e1aff4a81e718109e13409767e4f871443cbff3dba3578203d"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win32.whl", hash = "sha256:c69212f63169ec1cfc9bb44723bf2917cbbd8f6191a00ef3410f5a7fe300722d"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win_amd64.whl", hash = "sha256:cabddb8d8ead485e255fe80429f833172b4cadf99274db39abc080e068cbcc31"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bef08cd86169d9eafb3ccb0a39edb11d8e25f3dae2b28f5c52fd997521133069"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:b16420e621d26fdfa949a8b4b47ade8810c56002f5389970db4ddda51dbff248"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:b5edda50e5e9e15e54a6a8a0070302b00c518a9d32accc2346ad6c984aacd279"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:25c515e350e5b739842fc3228d662413ef28f295791af5e5110b543cf0b57d9b"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_24_aarch64.whl", hash = "sha256:1707814f0d9791df063f8c19bb51b0d1278b8e9a2353abbb676c2f685dee6afe"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:46d378daaac94f454b3a0e3d8d78cafd78a026b1d71443f4966c696b48a6d899"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:09b055c05697b38ecacb7ac50bdab2240bfca1a0c4872b0fd309bb07dc9aa3a9"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win32.whl", hash = "sha256:53a300ed9cea38cf5a2a9b069058137c2ca1ce658a874b79baceb8f892f915a7"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win_amd64.whl", hash = "sha256:c2a72e9109ea74e511e29032f3b670835f8a59bbdc9ce692c5b4ed91ccf1eedb"}, {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ebc06178e8821efc9692ea7544aa5644217358490145629914d8020042c24aa1"}, {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:edaef1c1200c4b4cb914583150dcaa3bc30e592e907c01117c08b13a07255ec2"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:7048c338b6c86627afb27faecf418768acb6331fc24cfa56c93e8c9780f815fa"}, {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d176b57452ab5b7028ac47e7b3cf644bcfdc8cacfecf7e71759f7f51a59e5c92"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_24_aarch64.whl", hash = "sha256:1dc67314e7e1086c9fdf2680b7b6c2be1c0d8e3a8279f2e993ca2a7545fecf62"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3213ece08ea033eb159ac52ae052a4899b56ecc124bb80020d9bbceeb50258e9"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aab7fd643f71d7946f2ee58cc88c9b7bfc97debd71dcc93e03e2d174628e7e2d"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win32.whl", hash = "sha256:5c365d91c88390c8d0a8545df0b5857172824b1c604e867161e6b3d59a827eaa"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win_amd64.whl", hash = "sha256:1758ce7d8e1a29d23de54a16ae867abd370f01b5a69e1a3ba75223eaa3ca1a1b"}, {file = "ruamel.yaml.clib-0.2.8-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a5aa27bad2bb83670b71683aae140a1f52b0857a2deff56ad3f6c13a017a26ed"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c58ecd827313af6864893e7af0a3bb85fd529f862b6adbefe14643947cfe2942"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_12_0_arm64.whl", hash = "sha256:f481f16baec5290e45aebdc2a5168ebc6d35189ae6fea7a58787613a25f6e875"}, - {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3fcc54cb0c8b811ff66082de1680b4b14cf8a81dce0d4fbf665c2265a81e07a1"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:77159f5d5b5c14f7c34073862a6b7d34944075d9f93e681638f6d753606c6ce6"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7f67a1ee819dc4562d444bbafb135832b0b909f81cc90f7aa00260968c9ca1b3"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4ecbf9c3e19f9562c7fdd462e8d18dd902a47ca046a2e64dba80699f0b6c09b7"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:87ea5ff66d8064301a154b3933ae406b0863402a799b16e4a1d24d9fbbcbe0d3"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win32.whl", hash = "sha256:75e1ed13e1f9de23c5607fe6bd1aeaae21e523b32d83bb33918245361e9cc51b"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win_amd64.whl", hash = "sha256:3f215c5daf6a9d7bbed4a0a4f760f3113b10e82ff4c5c44bec20a68c8014f675"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1b617618914cb00bf5c34d4357c37aa15183fa229b24767259657746c9077615"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:a6a9ffd280b71ad062eae53ac1659ad86a17f59a0fdc7699fd9be40525153337"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:665f58bfd29b167039f714c6998178d27ccd83984084c286110ef26b230f259f"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:305889baa4043a09e5b76f8e2a51d4ffba44259f6b4c72dec8ca56207d9c6fe1"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:700e4ebb569e59e16a976857c8798aee258dceac7c7d6b50cab63e080058df91"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e2b4c44b60eadec492926a7270abb100ef9f72798e18743939bdbf037aab8c28"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e79e5db08739731b0ce4850bed599235d601701d5694c36570a99a0c5ca41a9d"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win32.whl", hash = "sha256:955eae71ac26c1ab35924203fda6220f84dce57d6d7884f189743e2abe3a9fbe"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win_amd64.whl", hash = "sha256:56f4252222c067b4ce51ae12cbac231bce32aee1d33fbfc9d17e5b8d6966c312"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03d1162b6d1df1caa3a4bd27aa51ce17c9afc2046c31b0ad60a0a96ec22f8001"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:bba64af9fa9cebe325a62fa398760f5c7206b215201b0ec825005f1b18b9bccf"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:9eb5dee2772b0f704ca2e45b1713e4e5198c18f515b52743576d196348f374d3"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:a1a45e0bb052edf6a1d3a93baef85319733a888363938e1fc9924cb00c8df24c"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:da09ad1c359a728e112d60116f626cc9f29730ff3e0e7db72b9a2dbc2e4beed5"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:184565012b60405d93838167f425713180b949e9d8dd0bbc7b49f074407c5a8b"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a75879bacf2c987c003368cf14bed0ffe99e8e85acfa6c0bfffc21a090f16880"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win32.whl", hash = "sha256:84b554931e932c46f94ab306913ad7e11bba988104c5cff26d90d03f68258cd5"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win_amd64.whl", hash = "sha256:25ac8c08322002b06fa1d49d1646181f0b2c72f5cbc15a85e80b4c30a544bb15"}, {file = "ruamel.yaml.clib-0.2.8.tar.gz", hash = "sha256:beb2e0404003de9a4cab9753a8805a8fe9320ee6673136ed7f04255fe60bb512"}, @@ -3635,4 +3649,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = "~3.11" -content-hash = "ab624a27e2dd921705460fdcd5b5bee6ab02dbfb97a644d191f2cb29132b8158" +content-hash = "2a1e37e69084e8683a6e1a5141f0b8db85516e747c17f3d409729801cce1a82f" diff --git a/src/main/api/routes/flowchart.ts b/src/main/api/routes/flowchart.ts index f9da106..2f1dc08 100644 --- a/src/main/api/routes/flowchart.ts +++ b/src/main/api/routes/flowchart.ts @@ -2,7 +2,6 @@ import { t } from '../trpc'; import { WebSocket } from 'ws'; import { ipcMain } from 'electron'; import { z } from 'zod'; -// import log from 'electron-log/main'; export const flowchartRouter = t.router({ startFlowchart: t.procedure.input(z.string()).mutation((opts) => { diff --git a/src/renderer/src/components/blocks/control/ToggleControl.tsx b/src/renderer/src/components/blocks/control/ToggleControl.tsx new file mode 100644 index 0000000..ef9cdc3 --- /dev/null +++ b/src/renderer/src/components/blocks/control/ToggleControl.tsx @@ -0,0 +1,20 @@ +import { Handle, Position } from 'reactflow'; +import { BlockProps } from '@/types/block'; +import { useBlockState } from '@/hooks/useBlockState'; +import { Switch } from '@/components/ui/Switch'; + +const ToggleBlock = ({ id }: BlockProps) => { + const [value, update] = useBlockState(id, false); + + return ( + <> +
+
{value}
+ +
+ + + ); +}; + +export default ToggleBlock; diff --git a/src/renderer/src/components/blocks/flow/ClockBlock.tsx b/src/renderer/src/components/blocks/flow/ClockBlock.tsx new file mode 100644 index 0000000..064a2ee --- /dev/null +++ b/src/renderer/src/components/blocks/flow/ClockBlock.tsx @@ -0,0 +1,17 @@ +import { Handle, Position } from 'reactflow'; +import { Clock } from 'lucide-react'; + +const ClockBlock = () => { + return ( + <> +
+ +
+ + {/* */} + {/* */} + + ); +}; + +export default ClockBlock; diff --git a/src/renderer/src/components/blocks/flow/ConditionalBlock.tsx b/src/renderer/src/components/blocks/flow/ConditionalBlock.tsx new file mode 100644 index 0000000..32af860 --- /dev/null +++ b/src/renderer/src/components/blocks/flow/ConditionalBlock.tsx @@ -0,0 +1,17 @@ +import { Handle, Position } from 'reactflow'; +import { BlockProps } from '@/types/block'; + +const ConditionalBlock = ({ data }: BlockProps) => { + const { label } = data; + + return ( + <> +
{label}
+ + + + + ); +}; + +export default ConditionalBlock; diff --git a/src/renderer/src/components/blocks/flow/ConstantBlock.tsx b/src/renderer/src/components/blocks/flow/ConstantBlock.tsx new file mode 100644 index 0000000..93653a4 --- /dev/null +++ b/src/renderer/src/components/blocks/flow/ConstantBlock.tsx @@ -0,0 +1,28 @@ +import { Input } from '@/components/ui/Input'; +import { useBlockUpdate } from '@/stores/flowchart'; +import { BlockProps } from '@/types/block'; +import { Handle, Position } from 'reactflow'; + +const ConstantBlock = ({ id, data }: BlockProps) => { + const updateBlock = useBlockUpdate(id); + + return ( + <> + {/*TODO: Find a more typesafe way to do this*/} +
+ { + updateBlock((block) => { + const val = parseInt(e.target.value, 10); + block.data.intrinsic_parameters['val'] = isNaN(val) ? '' : val; + }); + }} + value={data.intrinsic_parameters['val']} + /> +
+ + + ); +}; + +export default ConstantBlock; diff --git a/src/renderer/src/components/blocks/flow/FalseBlock.tsx b/src/renderer/src/components/blocks/flow/FalseBlock.tsx new file mode 100644 index 0000000..9b047de --- /dev/null +++ b/src/renderer/src/components/blocks/flow/FalseBlock.tsx @@ -0,0 +1,12 @@ +import { Handle, Position } from 'reactflow'; + +const FalseBlock = () => { + return ( + <> +
false
+ + + ); +}; + +export default FalseBlock; diff --git a/src/renderer/src/components/blocks/flow/FunctionDefinitionBlock.tsx b/src/renderer/src/components/blocks/flow/FunctionDefinitionBlock.tsx new file mode 100644 index 0000000..fe9c3dd --- /dev/null +++ b/src/renderer/src/components/blocks/flow/FunctionDefinitionBlock.tsx @@ -0,0 +1,73 @@ +import { Handle, Position } from 'reactflow'; +import { BlockProps } from '@/types/block'; +import { Input } from '@/components/ui/Input'; +import { useBlockUpdate, useFlowchartStore } from '@/stores/flowchart'; + +const FunctionDefinitionBlock = ({ id, data }: BlockProps) => { + const ins = Object.entries(data.inputs); + const outs = Object.entries(data.outputs); + + const updateBlock = useBlockUpdate(id); + + const { saveDefinition, functionDefinitions } = useFlowchartStore((state) => ({ + saveDefinition: state.saveDefinition, + functionDefinitions: state.functionDefinitionBlocks + })); + + return ( + <> +
+ { + if (id in functionDefinitions && data.label !== functionDefinitions[id].data.label) { + saveDefinition(id); + } + }} + onChange={(e) => + updateBlock((block) => { + block.data.label = e.target.value; + }) + } + /> +
+ {ins.map(([name]) => ( +
+
{name}
+ +
+ ))} +
+
+ {outs.map(([name]) => ( +
+
{name}
+ +
+ ))} +
+ {ins.map(([name], i) => ( + + ))} + {outs.map(([name], i) => ( + + ))} +
+ + ); +}; + +export default FunctionDefinitionBlock; diff --git a/src/renderer/src/components/blocks/flow/FunctionInstanceBlock.tsx b/src/renderer/src/components/blocks/flow/FunctionInstanceBlock.tsx new file mode 100644 index 0000000..e51991e --- /dev/null +++ b/src/renderer/src/components/blocks/flow/FunctionInstanceBlock.tsx @@ -0,0 +1,42 @@ +import { useFlowchartStore } from '@/stores/flowchart'; +import { FunctionInstanceData } from '@/types/block'; +import { Handle, NodeProps, Position, useUpdateNodeInternals } from 'reactflow'; + +// TODO: Figure out what to do with instance blocks when the original definition is removed +const FunctionInstanceBlock = ({ id, data }: NodeProps) => { + const functionDefinitionBlocks = useFlowchartStore((state) => state.functionDefinitionBlocks); + const defn = functionDefinitionBlocks[data.definition_block_id]; + + const ins = Object.entries(defn.data.inputs); + const outs = Object.entries(defn.data.outputs); + + const updateNodeInternals = useUpdateNodeInternals(); + + updateNodeInternals(id); + + return ( + <> +
{defn.data.label}
+ {ins.map(([name], i) => ( + + ))} + {outs.map(([name], i) => ( + + ))} + + ); +}; + +export default FunctionInstanceBlock; diff --git a/src/renderer/src/components/blocks/flow/RandBlock.tsx b/src/renderer/src/components/blocks/flow/RandBlock.tsx new file mode 100644 index 0000000..dfb80e0 --- /dev/null +++ b/src/renderer/src/components/blocks/flow/RandBlock.tsx @@ -0,0 +1,13 @@ +import { Handle, Position } from 'reactflow'; + +const RandBlock = () => { + return ( + <> +
rand
+ + + + ); +}; + +export default RandBlock; diff --git a/src/renderer/src/components/blocks/flow/SequenceBlock.tsx b/src/renderer/src/components/blocks/flow/SequenceBlock.tsx new file mode 100644 index 0000000..9e5ae06 --- /dev/null +++ b/src/renderer/src/components/blocks/flow/SequenceBlock.tsx @@ -0,0 +1,18 @@ +import { Handle, Position } from 'reactflow'; +import { BlockProps } from '@/types/block'; + +const SequenceBlock = ({ data }: BlockProps) => { + const { label } = data; + + return ( + <> +
{label}
+ + + + + + ); +}; + +export default SequenceBlock; diff --git a/src/renderer/src/components/blocks/flow/ToggleBlock.tsx b/src/renderer/src/components/blocks/flow/ToggleBlock.tsx new file mode 100644 index 0000000..b009276 --- /dev/null +++ b/src/renderer/src/components/blocks/flow/ToggleBlock.tsx @@ -0,0 +1,15 @@ +import { ToggleLeft } from 'lucide-react'; +import { Handle, Position } from 'reactflow'; + +const ToggleBlock = () => { + return ( + <> +
+ +
+ + + ); +}; + +export default ToggleBlock; diff --git a/src/renderer/src/components/blocks/flow/TrueBlock.tsx b/src/renderer/src/components/blocks/flow/TrueBlock.tsx new file mode 100644 index 0000000..66bbf21 --- /dev/null +++ b/src/renderer/src/components/blocks/flow/TrueBlock.tsx @@ -0,0 +1,12 @@ +import { Handle, Position } from 'reactflow'; + +const TrueBlock = () => { + return ( + <> +
true
+ + + ); +}; + +export default TrueBlock; diff --git a/src/renderer/src/components/flow/BlockCard.tsx b/src/renderer/src/components/flow/BlockCard.tsx index 3960f92..6ba0633 100644 --- a/src/renderer/src/components/flow/BlockCard.tsx +++ b/src/renderer/src/components/flow/BlockCard.tsx @@ -1,31 +1,33 @@ +import { BlockAddPayload, BlockType } from '@/types/block'; import { Button } from '../ui/Button'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../ui/Tooltip'; +import { DragEventHandler } from 'react'; type Props = { name: string; - block_type: string; + block_type: BlockType; desc: string; }; const BlockCard = ({ name, desc, block_type }: Props): JSX.Element => { - const onDragStart = (event, nodeType) => { - event.dataTransfer.setData('application/reactflow', nodeType); + const onDragStart: DragEventHandler = (event) => { + const payload: BlockAddPayload = { + variant: 'builtin', + block_type + }; + + event.dataTransfer.setData('application/reactflow', JSON.stringify(payload)); event.dataTransfer.effectAllowed = 'move'; // TODO: We can set the custom drag image here to be how the // actual block is going to look like! - event.dataTransfer.setDragImage(event.target, 0, 0); + event.dataTransfer.setDragImage(event.target as HTMLButtonElement, 0, 0); }; return ( - diff --git a/src/renderer/src/components/flow/BlockFunctionCard.tsx b/src/renderer/src/components/flow/BlockFunctionCard.tsx new file mode 100644 index 0000000..06ba74c --- /dev/null +++ b/src/renderer/src/components/flow/BlockFunctionCard.tsx @@ -0,0 +1,31 @@ +import { BlockAddPayload, BuiltinBlockData } from '@/types/block'; +import { Button } from '../ui/Button'; +import { DragEventHandler } from 'react'; +import { Node } from 'reactflow'; + +type Props = { + definitionBlock: Node; +}; + +const BlockFunctionCard = ({ definitionBlock }: Props): JSX.Element => { + const onDragStart: DragEventHandler = (event) => { + const payload: BlockAddPayload = { + variant: 'function_instance', + definition_block_id: definitionBlock.id + }; + + event.dataTransfer.setData('application/reactflow', JSON.stringify(payload)); + event.dataTransfer.effectAllowed = 'move'; + // TODO: We can set the custom drag image here to be how the + // actual block is going to look like! + event.dataTransfer.setDragImage(event.target as HTMLButtonElement, 0, 0); + }; + + return ( + + ); +}; + +export default BlockFunctionCard; diff --git a/src/renderer/src/components/flow/BlocksLibrary.tsx b/src/renderer/src/components/flow/BlocksLibrary.tsx index bba892d..c25a6b7 100644 --- a/src/renderer/src/components/flow/BlocksLibrary.tsx +++ b/src/renderer/src/components/flow/BlocksLibrary.tsx @@ -1,6 +1,17 @@ import BlockCard from '@/components/flow/BlockCard'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '../ui/Tabs'; +import { useFlowchartStore } from '@/stores/flowchart'; +import BlockFunctionCard from './BlockFunctionCard'; +import { X } from 'lucide-react'; const BlocksLibrary = () => { + const { functionDefinitions, removeDefinition } = useFlowchartStore((state) => ({ + functionDefinitions: state.functionDefinitionBlocks, + removeDefinition: state.removeDefinition + })); + + console.log(functionDefinitions); + return (
@@ -10,23 +21,69 @@ const BlocksLibrary = () => {
Add the block you need by dragging it to the flowchart on the right. -
+
{' '}
-
- {/* TODO: This should be auto generated */} - - - - -
+ + + Standard Library + Custom Functions + + +
+ {/* TODO: This should be auto generated */} + + + + + + + + + + + + + +
+
+ +
+ Custom functions are listed here. Try defining one using the "Function + definition" block. +
+ {Object.entries(functionDefinitions).map(([blockId, block]) => ( +
+ + removeDefinition(blockId)} + /> +
+ ))} +
+
+
); }; diff --git a/src/renderer/src/components/flow/ContextMenu.tsx b/src/renderer/src/components/flow/ContextMenu.tsx new file mode 100644 index 0000000..cc116df --- /dev/null +++ b/src/renderer/src/components/flow/ContextMenu.tsx @@ -0,0 +1,152 @@ +import { HTMLProps, PropsWithChildren } from 'react'; +import { useReactFlow, useUpdateNodeInternals } from 'reactflow'; +import { Button } from '../ui/Button'; +import { useBlockUpdate, useFlowchartStore } from '@/stores/flowchart'; +import { BlockData } from '@/types/block'; +import { BookMarked, LucideIcon, Minus, Plus, X } from 'lucide-react'; +import { cn } from '@/utils/style'; + +type ContextMenuItemProps = { + onClick: () => void; + className?: string; + icon: LucideIcon; +}; + +export const ContextMenuItem = ({ + onClick, + className, + icon, + children +}: PropsWithChildren) => { + const Icon = icon; + + return ( + + ); +}; + +type ContextMenuFunctionSectionProps = { + id: string; +}; + +const ContextMenuFunctionSection = ({ id }: ContextMenuFunctionSectionProps) => { + const updateBlock = useBlockUpdate(id); + const updateNodeInternals = useUpdateNodeInternals(); + const { saveDefinition, functionDefinitionBlocks } = useFlowchartStore((state) => ({ + saveDefinition: state.saveDefinition, + functionDefinitionBlocks: state.functionDefinitionBlocks + })); + + const withAfterUpdate = (func: () => void) => { + return () => { + func(); + if (Object.values(functionDefinitionBlocks).find((b) => b.id === id)) { + saveDefinition(id); + } + updateNodeInternals(id); + }; + }; + + const addFunctionParameter = () => { + updateBlock((block) => { + const numInputs = Object.keys(block.data.inputs).length; + block.data.inputs[`in${numInputs}`] = 'int'; + }); + }; + + const removeFunctionParameter = () => { + updateBlock((block) => { + const inputs = Object.entries(block.data.inputs).slice(0, -1); + block.data.inputs = Object.fromEntries(inputs); + }); + }; + + const addFunctionOutput = () => { + updateBlock((block) => { + const numOutputs = Object.keys(block.data.outputs).length; + block.data.outputs[`out${numOutputs}`] = 'int'; + }); + }; + + const removeFunctionOutput = () => { + updateBlock((block) => { + const outputs = Object.entries(block.data.outputs).slice(0, -1); + block.data.outputs = Object.fromEntries(outputs); + }); + }; + + return ( + <> + saveDefinition(id)}> + Save Definition + +
+ + Add Parameter + + + Add Output + + + Remove Parameter + + + Remove Output + + + ); +}; + +export type ContextMenuProps = { + id: string; + top?: number; + bottom?: number; + left?: number; + right?: number; +}; + +export const ContextMenu = ({ + id, + top, + left, + right, + bottom, + ...props +}: ContextMenuProps & HTMLProps) => { + const deleteNode = useFlowchartStore((state) => state.deleteNode); + + const { getNode } = useReactFlow(); + + const node = getNode(id); + if (!node) { + throw new Error('impossible'); + } + + const isFunctionBlock = node.data.block_type == 'flojoy.intrinsics.function'; + + return ( +
+ {isFunctionBlock && ( + <> + +
+ + )} + deleteNode(id)} icon={X}> + Delete + +
+ ); +}; diff --git a/src/renderer/src/components/flow/FlowCanvas.tsx b/src/renderer/src/components/flow/FlowCanvas.tsx index 8f6ae4f..4989f20 100644 --- a/src/renderer/src/components/flow/FlowCanvas.tsx +++ b/src/renderer/src/components/flow/FlowCanvas.tsx @@ -12,11 +12,15 @@ import ReactFlow, { import 'reactflow/dist/style.css'; import { useFlowchartStore } from '@/stores/flowchart'; import { useShallow } from 'zustand/react/shallow'; -import { useCallback, useState } from 'react'; +import { DragEventHandler, useCallback, useRef, useState } from 'react'; import useUndoRedo from '@/hooks/useUndoRedo'; + import { nodeTypes } from '@/configs/flowchart'; import CanvasControlsBottomLeft from '../reactflow/CanvasControlsBottomLeft'; import FlowControlsBottomRight from './FlowControlsBottomRight'; +import { useContextMenu } from '@/hooks/useContextMenu'; +import { ContextMenu } from './ContextMenu'; +import { BlockAddPayload } from '@/types/block'; const edgeTypes = { smart: SmartBezierEdge @@ -26,6 +30,8 @@ const FlowCanvas = () => { const [reactFlowInstance, setReactFlowInstance] = useState( undefined ); + const reactFlowRef = useRef(null); + const { menu, onPaneClick, onNodeContextMenu } = useContextMenu(reactFlowRef); const { takeSnapshot } = useUndoRedo(); const { edges, onEdgesChange, nodes, onNodesChange, onConnect, addNode } = useFlowchartStore( @@ -35,7 +41,8 @@ const FlowCanvas = () => { nodes: state.nodes, onNodesChange: state.onNodesChange, onConnect: state.onConnect, - addNode: state.addNode + addNode: state.addNode, + saveDefinition: state.saveDefinition })) ); @@ -60,12 +67,12 @@ const FlowCanvas = () => { takeSnapshot(); }, [takeSnapshot]); - const onDragOver = useCallback((event) => { + const onDragOver: DragEventHandler = useCallback((event) => { event.preventDefault(); event.dataTransfer.dropEffect = 'move'; }, []); - const onDrop = useCallback( + const onDrop: DragEventHandler = useCallback( (event) => { event.preventDefault(); @@ -75,13 +82,15 @@ const FlowCanvas = () => { return; } - const block_type = event.dataTransfer.getData('application/reactflow'); + const data = event.dataTransfer.getData('application/reactflow'); // check if the dropped element is valid - if (typeof block_type === 'undefined' || !block_type) { + if (data === '') { return; } + const payload = JSON.parse(data) as BlockAddPayload; + // reactFlowInstance.project was renamed to reactFlowInstance.screenToFlowPosition // and you don't need to subtract the reactFlowBounds.left/top anymore // details: https://reactflow.dev/whats-new/2023-11-10 @@ -90,13 +99,14 @@ const FlowCanvas = () => { y: event.clientY }); - addNode(block_type, position); + addNode(payload, position); }, [reactFlowInstance] ); return ( { onDragOver={onDragOver} onDrop={onDrop} onInit={setReactFlowInstance} + onPaneClick={onPaneClick} + onNodeContextMenu={onNodeContextMenu} proOptions={{ hideAttribution: true }} className="rounded-lg bg-background" > @@ -119,6 +131,7 @@ const FlowCanvas = () => { + {menu && } ); }; diff --git a/src/renderer/src/components/flow/FlowControlsTopRight.tsx b/src/renderer/src/components/flow/FlowControlsTopRight.tsx index f0d9f5f..fa60119 100644 --- a/src/renderer/src/components/flow/FlowControlsTopRight.tsx +++ b/src/renderer/src/components/flow/FlowControlsTopRight.tsx @@ -4,12 +4,14 @@ import { useFlowchartStore } from '@/stores/flowchart'; import { useShallow } from 'zustand/react/shallow'; import { useLifecycleStore } from '@/stores/lifecycle'; import { trpcClient } from '@/main'; +import { FunctionDefinition, Name } from '@/types/block'; const FlowControlsTopRight = (): JSX.Element => { - const { edges, nodes } = useFlowchartStore( + const { edges, nodes, functionDefinitionBlocks } = useFlowchartStore( useShallow((state) => ({ edges: state.edges, - nodes: state.nodes + nodes: state.nodes, + functionDefinitionBlocks: state.functionDefinitionBlocks })) ); @@ -32,11 +34,26 @@ const FlowControlsTopRight = (): JSX.Element => { }; const onStart = async () => { + const functionDefinitions: Record = {}; + for (const [name, block] of Object.entries(functionDefinitionBlocks)) { + const bodyNodes = nodes.filter((n) => n.parentNode === block.id); + const bodyNodeIds = new Set(bodyNodes.map((n) => n.id)); + bodyNodeIds.add(block.id); + const bodyEdges = edges.filter((e) => bodyNodeIds.has(e.target) && bodyNodeIds.has(e.source)); + + functionDefinitions[name] = { + block, + nodes: bodyNodes, + edges: bodyEdges + }; + } + await trpcClient.startFlowchart.mutate( JSON.stringify({ event: { event_type: 'start', - rf: { nodes, edges } + rf: { nodes, edges }, + function_definitions: functionDefinitions } }) ); diff --git a/src/renderer/src/components/root/StatusBar.tsx b/src/renderer/src/components/root/StatusBar.tsx index d6383d8..de2e92e 100644 --- a/src/renderer/src/components/root/StatusBar.tsx +++ b/src/renderer/src/components/root/StatusBar.tsx @@ -33,7 +33,6 @@ const StatusBar = (): JSX.Element => { // Listen for messages from the main process window.electron.ipcRenderer.on('status-bar-logging', (_, data) => { setMessage(data); - console.log(data); }); }, []); diff --git a/src/renderer/src/components/ui/Tabs.tsx b/src/renderer/src/components/ui/Tabs.tsx new file mode 100644 index 0000000..a069bd5 --- /dev/null +++ b/src/renderer/src/components/ui/Tabs.tsx @@ -0,0 +1,53 @@ +import * as React from 'react'; +import * as TabsPrimitive from '@radix-ui/react-tabs'; + +import { cn } from '@/utils/style'; + +const Tabs = TabsPrimitive.Root; + +const TabsList = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { className?: string } +>(({ className, ...props }, ref) => ( + +)); +TabsList.displayName = TabsPrimitive.List.displayName; + +const TabsTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { className?: string } +>(({ className, ...props }, ref) => ( + +)); +TabsTrigger.displayName = TabsPrimitive.Trigger.displayName; + +const TabsContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { className?: string } +>(({ className, ...props }, ref) => ( + +)); +TabsContent.displayName = TabsPrimitive.Content.displayName; + +export { Tabs, TabsList, TabsTrigger, TabsContent }; diff --git a/src/renderer/src/configs/control.ts b/src/renderer/src/configs/control.ts index 83d346c..16ab542 100644 --- a/src/renderer/src/configs/control.ts +++ b/src/renderer/src/configs/control.ts @@ -1,9 +1,11 @@ import SliderControl from '@/components/blocks/control/SliderControl'; import BigNumberDisplay from '@/components/blocks/control/BigNumberDisplay'; +import ToggleControl from '@/components/blocks/control/ToggleControl'; import ProgressDisplay from '@/components/blocks/control/ProgressDisplay'; export const nodeTypes = { 'flojoy.control.slider': SliderControl, + 'flojoy.control.toggle': ToggleControl, 'flojoy.visualization.big_num': BigNumberDisplay, 'flojoy.visualization.progress_bar': ProgressDisplay }; diff --git a/src/renderer/src/configs/flowchart.ts b/src/renderer/src/configs/flowchart.ts index dca7c51..b3f6736 100644 --- a/src/renderer/src/configs/flowchart.ts +++ b/src/renderer/src/configs/flowchart.ts @@ -2,10 +2,30 @@ import AddBlock from '@/components/blocks/flow/AddBlock'; import BigNumberBlock from '@/components/blocks/flow/BigNumberBlock'; import ProgressBlock from '@/components/blocks/flow/ProgressBlock'; import SliderBlock from '@/components/blocks/flow/SliderBlock'; +import SequenceBlock from '@/components/blocks/flow/SequenceBlock'; +import ConstantBlock from '@/components/blocks/flow/ConstantBlock'; +import ConditionalBlock from '@/components/blocks/flow/ConditionalBlock'; +import TrueBlock from '@/components/blocks/flow/TrueBlock'; +import FalseBlock from '@/components/blocks/flow/FalseBlock'; +import RandBlock from '@/components/blocks/flow/RandBlock'; +import ToggleBlock from '@/components/blocks/flow/ToggleBlock'; +import ClockBlock from '@/components/blocks/flow/ClockBlock'; +import FunctionDefinitionBlock from '@/components/blocks/flow/FunctionDefinitionBlock'; +import FunctionInstanceBlock from '@/components/blocks/flow/FunctionInstanceBlock'; export const nodeTypes = { 'flojoy.control.slider': SliderBlock, + 'flojoy.control.toggle': ToggleBlock, + 'flojoy.math.arithmetic.add': AddBlock, + 'flojoy.math.constant': ConstantBlock, + 'flojoy.math.rand': RandBlock, + 'flojoy.logic.conditional': ConditionalBlock, + 'flojoy.logic.sequence': SequenceBlock, + 'flojoy.logic.clock': ClockBlock, + 'flojoy.logic.true': TrueBlock, + 'flojoy.logic.false': FalseBlock, 'flojoy.visualization.big_num': BigNumberBlock, 'flojoy.visualization.progress_bar': ProgressBlock, - 'flojoy.math.arithmetic.add': AddBlock + 'flojoy.intrinsics.function': FunctionDefinitionBlock, + function_instance: FunctionInstanceBlock }; diff --git a/src/renderer/src/hooks/useContextMenu.ts b/src/renderer/src/hooks/useContextMenu.ts new file mode 100644 index 0000000..847c6cf --- /dev/null +++ b/src/renderer/src/hooks/useContextMenu.ts @@ -0,0 +1,34 @@ +import { MutableRefObject, useCallback, useState } from 'react'; +import { ContextMenuProps } from '@/components/flow/ContextMenu'; + +export const useContextMenu = (reactFlowRef: MutableRefObject) => { + const [menu, setMenu] = useState(null); + + const onNodeContextMenu = useCallback( + (event, node) => { + // Prevent native context menu from showing + event.preventDefault(); + + if (!reactFlowRef.current) { + return; + } + + // Calculate position of the context menu. We want to make sure it + // doesn't get positioned off-screen. + const pane = reactFlowRef.current.getBoundingClientRect(); + setMenu({ + id: node.id, + top: event.clientY < pane.height - 200 ? event.clientY - 50 : undefined, + left: event.clientX < pane.width - 200 ? event.clientX : undefined, + right: event.clientX >= pane.width - 200 ? pane.width - event.clientX : undefined, + bottom: event.clientY >= pane.height - 200 ? pane.height - event.clientY : undefined + }); + }, + [setMenu] + ); + + // Close the context menu if it's open whenever the window is clicked. + const onPaneClick = useCallback(() => setMenu(null), [setMenu]); + + return { menu, onPaneClick, onNodeContextMenu }; +}; diff --git a/src/renderer/src/main.tsx b/src/renderer/src/main.tsx index 28ae8c6..688a102 100644 --- a/src/renderer/src/main.tsx +++ b/src/renderer/src/main.tsx @@ -4,13 +4,14 @@ import './styles/reactflow.css'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import Root from './routes/root/Root'; import Setup from './routes/setup/Setup'; -import { createHashRouter, RouterProvider } from 'react-router-dom'; +import { createHashRouter, RouterProvider, useRouteError } from 'react-router-dom'; import Flow from './routes/flow/Flow'; import Library from './routes/library/Library'; import { ipcLink } from 'electron-trpc/renderer'; import { createTRPCProxyClient } from '@trpc/client'; import type { AppRouter } from 'src/main/api/root.d'; import Control from './routes/control/Control'; +import { Button } from './components/ui/Button'; const queryClient = new QueryClient(); @@ -22,6 +23,7 @@ const router = createHashRouter([ { path: '/', element: , // should contain all the providers + errorElement: , children: [ { path: '/flow', @@ -43,6 +45,34 @@ const router = createHashRouter([ } ]); +function ErrorBoundary() { + const error = useRouteError(); + + console.log(error); + + const reload = () => { + localStorage.clear(); + trpcClient.restartFlojoyStudio.mutate(); + }; + + return ( +
+
Oops!
+
Something went wrong when loading Flojoy Studio.
+ +
+ + + +
+ + +
+ ); +} + const rootElement = document.getElementById('root')!; if (!rootElement.innerHTML) { diff --git a/src/renderer/src/routes/library/Library.tsx b/src/renderer/src/routes/library/Library.tsx index 726728d..03ed42f 100644 --- a/src/renderer/src/routes/library/Library.tsx +++ b/src/renderer/src/routes/library/Library.tsx @@ -1,10 +1,14 @@ import BlocksLibrary from '@/components/flow/BlocksLibrary'; import Header from '@/components/root/Header'; +import { ScrollArea } from '@/components/ui/ScrollArea'; + const Library = () => { return (
- + + +
); }; diff --git a/src/renderer/src/stores/flowchart.ts b/src/renderer/src/stores/flowchart.ts index f82f876..9d7ba26 100644 --- a/src/renderer/src/stores/flowchart.ts +++ b/src/renderer/src/stores/flowchart.ts @@ -13,50 +13,121 @@ import { applyEdgeChanges, XYPosition } from 'reactflow'; -import { BlockType } from '@/types/block'; +import { BuiltinBlockData, BlockID, BlockData } from '@/types/block'; import { v4 as uuidv4 } from 'uuid'; import { createJSONStorage, persist } from 'zustand/middleware'; +import { immer } from 'zustand/middleware/immer'; import { useUndoRedoStore } from './undoredo'; import { shared } from 'use-broadcast-ts'; import { nodeTypes } from '@/configs/control'; +import { Draft } from 'immer'; +import _ from 'lodash'; +import { BlockAddPayload } from '@/types/block'; interface FlowchartState { - nodes: Node[]; + nodes: Node[]; edges: Edge[]; controls: Node[]; - setNodes: (nodes: Node[]) => void; + functionDefinitionBlocks: Record>; + + setNodes: (nodes: Node[]) => void; setEdges: (edges: Edge[]) => void; setControls: (edges: Node[]) => void; + updateBlock: (id: string, mutation: (block: Draft>) => void) => void; + + saveDefinition: (definitionBlockId: string) => void; + removeDefinition: (name: string) => void; + onNodesChange: OnNodesChange; onEdgesChange: OnEdgesChange; onControlsChange: OnNodesChange; onConnect: OnConnect; - addNode: (block_type: BlockType, position: XYPosition) => void; + addNode: (payload: BlockAddPayload, position: XYPosition) => void; + deleteNode: (id: string) => void; reset: () => void; } export const useFlowchartStore = create()( shared( persist( - (set, get) => ({ - nodes: [] as Node[], + immer((set, get) => ({ + nodes: [] as Node[], edges: [] as Edge[], controls: [] as Node[], - setNodes: (nodes: Node[]) => set({ nodes }), + functionDefinitionBlocks: {}, + + setNodes: (nodes: Node[]) => set({ nodes }), setEdges: (edges: Edge[]) => set({ edges }), setControls: (controls: Node[]) => set({ controls }), + updateBlock: (id: string, mutation: (block: Draft>) => void) => { + set((state) => { + const block = state.nodes.find((n) => n.id === id); + if (!block) { + throw new Error('Tried to update non-existant block'); + } + if (!isBuiltinBlock(block)) { + throw new Error("Can't update non-builtin block"); + } + + mutation(block); + }); + }, + + saveDefinition: (definitionBlockId: BlockID) => { + set((state) => { + const node = state.nodes.find((n) => n.id === definitionBlockId); + if (!node || !isFunctionDefinitionBlock(node)) { + return; + } + + state.functionDefinitionBlocks[node.id] = node; + }); + }, + + removeDefinition: (definitionBlockId: BlockID) => { + const instances = get().nodes.filter( + (n) => + n.data.block_type === 'function_instance' && + n.data.definition_block_id === definitionBlockId + ); + const instanceIds = new Set(instances.map((n) => n.id)); + + set({ + edges: get().edges.filter( + (e) => !instanceIds.has(e.source) && !instanceIds.has(e.target) + ), + nodes: get().nodes.filter((n) => !instanceIds.has(n.id)) + }); + set((state) => { + delete state.functionDefinitionBlocks[definitionBlockId]; + }); + }, + onNodesChange: (changes: NodeChange[]) => { + for (const change of changes) { + if (change.type === 'remove' && change.id in get().functionDefinitionBlocks) { + get().removeDefinition(change.id); + } + } + set({ nodes: applyNodeChanges(changes, get().nodes) }); + // Delete corresponding nodes in control canvas + set({ + controls: applyNodeChanges( + changes.filter((c) => c.type === 'remove'), + get().controls + ) + }); }, onEdgesChange: (changes: EdgeChange[]) => { set({ @@ -67,6 +138,13 @@ export const useFlowchartStore = create()( set({ controls: applyNodeChanges(changes, get().controls) }); + // Delete corresponding nodes in flow chart + set({ + nodes: applyNodeChanges( + changes.filter((c) => c.type === 'remove'), + get().nodes + ) + }); }, onConnect: (connection: Connection) => { @@ -76,39 +154,98 @@ export const useFlowchartStore = create()( edges: addEdge(connection, get().edges) }); }, - addNode: (block_type: BlockType, position: XYPosition) => { + addNode: (payload: BlockAddPayload, position: XYPosition) => { const undoredoStore = useUndoRedoStore.getState(); undoredoStore.takeSnapshot(); const uuid = uuidv4(); + + const parent = get().nodes.find((n) => { + if (!n.width || !n.height) return false; + + return ( + n.type === 'flojoy.intrinsics.function' && + n.position.x < position.x && + n.position.x + n.width > position.x && + n.position.y < position.y && + n.position.y + n.height > position.y + ); + }); + + // The position of parents within subflows is measured relative to the parent + // so we need to convert from absolute position to relative position for child nodes + const adjustedPosition = !parent + ? position + : { + x: position.x - parent.position.x, + y: position.y - parent.position.y + }; + + let data: BlockData; + switch (payload.variant) { + case 'builtin': + data = { + block_type: payload.block_type, + label: payload.block_type, + intrinsic_parameters: + payload.block_type === 'flojoy.math.constant' ? { val: 0 } : {}, + // TODO: Change this when builtin blocks will actually + // use the inputs and outputs fields to render their handles + // based on python function information + inputs: payload.block_type === 'flojoy.intrinsics.function' ? { inp: 'int' } : {}, + outputs: payload.block_type === 'flojoy.intrinsics.function' ? { out: 'int' } : {} + }; + break; + case 'function_instance': { + const definitions = get().functionDefinitionBlocks; + const defnId = payload.definition_block_id; + if (!(defnId in definitions)) { + throw new Error(`Undefined function block ${defnId}`); + } + + data = { + block_type: 'function_instance', + definition_block_id: defnId + }; + break; + } + } + set({ nodes: get().nodes.concat([ { - id: uuid, - type: block_type, - position: position, - data: { - label: block_type, - block_type - } + id: `${data.block_type}-${uuid}`, + type: data.block_type, + position: adjustedPosition, + data, + parentNode: parent ? parent.id : undefined, + extent: parent ? 'parent' : undefined } ]) }); - if (Object.keys(nodeTypes).includes(block_type)) { + if (Object.keys(nodeTypes).includes(data.block_type)) { set({ controls: get().controls.concat([ { - id: uuid, - type: block_type, + id: `${data.block_type}-${uuid}`, + type: data.block_type, position: position, data: { - label: block_type, - block_type + label: data.block_type, + block_type: data.block_type } } ]) }); } }, + + deleteNode: (id: string) => { + get().onNodesChange([{ type: 'remove', id }]); + set({ + edges: get().edges.filter((e) => e.source !== id && e.target !== id) + }); + }, + reset: () => { set({ nodes: [], @@ -116,7 +253,7 @@ export const useFlowchartStore = create()( controls: [] }); } - }), + })), { name: 'flow-state', storage: createJSONStorage(() => localStorage) @@ -124,3 +261,17 @@ export const useFlowchartStore = create()( ) ) ); + +const isBuiltinBlock = (node: Node): node is Node => { + return node.data.block_type !== 'function_instance'; +}; + +const isFunctionDefinitionBlock = (node: Node): node is Node => { + return node.data.block_type === 'flojoy.intrinsics.function'; +}; + +export const useBlockUpdate = (id: string) => { + const update = useFlowchartStore((state) => state.updateBlock); + + return _.curry(update)(id); +}; diff --git a/src/renderer/src/types/block.ts b/src/renderer/src/types/block.ts index dff82fd..aed1e87 100644 --- a/src/renderer/src/types/block.ts +++ b/src/renderer/src/types/block.ts @@ -1,17 +1,61 @@ -import { NodeProps } from 'reactflow'; +import { Node, Edge, NodeProps } from 'reactflow'; // TODO: This should not be hardcoded export type BlockType = + | 'flojoy.control.toggle' | 'flojoy.control.slider' - | 'flojoy.control.progress_bar' | 'flojoy.visualization.big_num' + | 'flojoy.visualization.progress_bar' | 'flojoy.math.arithmetic.add' | 'flojoy.math.arithmetic.subtract' - | 'flojoy.math.constant'; + | 'flojoy.math.constant' + | 'flojoy.math.rand' + | 'flojoy.logic.sequence' + | 'flojoy.logic.clock' + | 'flojoy.logic.conditional' + | 'flojoy.logic.true' + | 'flojoy.logic.false' + | 'flojoy.intrinsics.function'; -export type BlockData = { +export type IntrinsicParameterValue = number | string; + +export type Name = string; +export type BlockID = string; +type FlojoyType = 'str' | 'int' | 'bool'; + +export type BuiltinBlockData = { block_type: BlockType; label: string; + intrinsic_parameters: Record; + inputs: Record; + outputs: Record; +}; + +export type BlockProps = NodeProps; + +type RegularBlockAddPayload = { + variant: 'builtin'; + block_type: BlockType; +}; + +type FunctionBlockAddPayload = { + variant: 'function_instance'; + definition_block_id: string; +}; + +export type BlockAddPayload = RegularBlockAddPayload | FunctionBlockAddPayload; + +// A function is just a subflow, so we can define it using +// the parent function definition block and the child nodes and edges +export type FunctionDefinition = { + block: Node; + nodes: Node[]; + edges: Edge[]; +}; + +export type FunctionInstanceData = { + block_type: 'function_instance'; + definition_block_id: string; }; -export type BlockProps = NodeProps; +export type BlockData = BuiltinBlockData | FunctionInstanceData;