This commit is contained in:
IDONTSUDO 2024-02-27 18:48:35 +03:00
parent c43c192a3e
commit dffc73c30f
18 changed files with 525 additions and 134 deletions

View file

@ -32,6 +32,3 @@ export const StringExtensions = () => {
};
}
};
// python3 /Users/idontsudo/framework/path.py --path /Users/idontsudo/webservice/server/build/public/0a3422cc-f2e3-4abc-87d8-ae13b8b6d26d/ --env /Users/idontsudo/framework/cad_generation/env.json
// python3 /Users/idontsudo/framework/path.py --path /Users/idontsudo/webservice/server/build/public/0a3422cc-f2e3-4abc-87d8-ae13b8b6d26d/ --env /Users/idontsudo/framework/cad_generation/env.json
// /Users/idontsudo/Desktop/FreeCAD.app/Contents/MacOS/FreeCAD /Users/idontsudo/framework/cad_generation/main.py

6
ui/package-lock.json generated
View file

@ -6882,9 +6882,9 @@
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="
},
"node_modules/diff-sequences": {
"node_modules/diff-Sequence": {
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz",
"resolved": "https://registry.npmjs.org/diff-Sequence/-/diff-Sequence-27.5.1.tgz",
"integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==",
"engines": {
"node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
@ -10154,7 +10154,7 @@
"integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==",
"dependencies": {
"chalk": "^4.0.0",
"diff-sequences": "^27.5.1",
"diff-Sequence": "^27.5.1",
"jest-get-type": "^27.5.1",
"pretty-format": "^27.5.1"
},

View file

@ -4,8 +4,12 @@ import { NumberExtensions } from "./number";
import { StringExtensions } from "./string";
export type CallBackVoidFunction = <T>(value: T) => void;
export type CallBackStringVoidFunction = (value: string) => void;
export type CallBackEventTarget = (value: EventTarget) => void;
export type OptionalProperties<T> = {
[P in keyof T]?: T[P];
};
declare global {
interface Array<T> {
@ -25,8 +29,12 @@ declare global {
}
interface Map<K, V> {
addValueOrMakeCallback(key: K, value: V, callBack: CallBackVoidFunction): void;
getKeyFromValueIsExists(value: V): K | undefined;
overrideValue(key: K, value: OptionalProperties<V>): void;
keysToJson(): string;
toArray(): V[];
getPredicateValue(callBack: (value: V) => boolean): K[];
}
interface Vector3 {}
}
export const extensions = () => {
ArrayExtensions();

View file

@ -11,5 +11,51 @@ export const MapExtensions = () => {
}
};
}
if (Map.prototype.getKeyFromValueIsExists === undefined) {
// eslint-disable-next-line no-extend-native
Map.prototype.getKeyFromValueIsExists = function (value) {
let result;
this.forEach((el, key) => {
if (el === value) {
result = key;
}
});
return result;
};
}
if (Map.prototype.overrideValue === undefined) {
// eslint-disable-next-line no-extend-native
Map.prototype.overrideValue = function (key, value) {
const result = this.get(key);
this.set(key, Object.assign(result, value));
};
}
if (Map.prototype.keysToJson === undefined) {
// eslint-disable-next-line no-extend-native
Map.prototype.keysToJson = function () {
const result: any[] = [];
this.forEach((el) => result.push(el));
return JSON.stringify(result);
};
}
if (Map.prototype.toArray === undefined) {
// eslint-disable-next-line no-extend-native
Map.prototype.toArray = function () {
return Array.from(this.values());
};
}
if (Map.prototype.getPredicateValue === undefined) {
// eslint-disable-next-line no-extend-native
Map.prototype.getPredicateValue = function (callBack) {
const result: any[] = [];
this.forEach((el, key) => {
const callBackExecute = callBack(el);
if (callBackExecute) {
result.push(key);
}
});
return result;
};
}
};
Object();

View file

@ -0,0 +1,84 @@
import { TypedEvent } from "../../../core/helper/typed_event";
import { NodeEditor } from "rete";
import { AreaExtra, Schemes } from "../presentation/ui/editor/editor";
import { Result } from "../../../core/helper/result";
import { AreaPlugin } from "rete-area-plugin";
export interface BtDrawDragAndDropView {
x: number;
y: number;
name: string;
}
export class BtNodeView extends TypedEvent<BtDrawDragAndDropView> {}
export class ReteObserver extends TypedEvent<any> {}
export class BtBuilderModel {
public static result = "";
static fromReteScene(editor: NodeEditor<Schemes>, area: AreaPlugin<Schemes, AreaExtra>) {
this.getFirstSequence(editor).map((sortedSequence) => {
const firstNodeId = sortedSequence.getKeyFromValueIsExists(1) as string;
this.findSequence(firstNodeId, editor, sortedSequence, 2);
this.result += `<${this.getNodeLabelAtId(editor, firstNodeId)} id="${firstNodeId}">`;
this.toXML(sortedSequence as Map<string, number>, editor, area, firstNodeId);
this.result += `</${this.getNodeLabelAtId(editor, firstNodeId)}>`;
});
}
public static getNodeLabelAtId(editor: NodeEditor<Schemes>, id: string) {
return editor.getNode(id).label;
}
static toXML(
sortedSequence: Map<string, number>,
editor: NodeEditor<Schemes>,
area: AreaPlugin<Schemes, AreaExtra>,
activeElementId = ""
) {
editor.getConnections().forEach((el) => {
if (el.source === activeElementId) {
this.result += `<${this.getNodeLabelAtId(editor, el.target)} id="${el.target}">`;
this.toXML(sortedSequence, editor, area, el.target);
this.result += `</${this.getNodeLabelAtId(editor, el.target)}>`;
}
});
}
public static getBtPriorities(ids: string[]) {}
public static findSequence(
nodeId: string,
editor: NodeEditor<Schemes>,
result: Map<string, boolean | number>,
lvl: number
): Map<string, number> | undefined {
const connections: string[] = [];
editor.getConnections().forEach((el) => {
if (el.source === nodeId) {
connections.push(el.target);
}
});
if (connections.isEmpty()) return result as Map<string, number>;
let lv = lvl;
connections.forEach((el) => {
result.set(el, lvl);
this.findSequence(el, editor, result, (lv += 1));
});
}
public static getFirstSequence(editor: NodeEditor<Schemes>): Result<undefined, Map<string, boolean | number>> {
const node = new Map<string, boolean | number>();
editor.getNodes().forEach((el) => {
node.set(el.id, 1);
});
editor.getConnections().forEach((el) => {
node.set(el.target, false);
});
const key = node.getKeyFromValueIsExists(1);
if (key === undefined) {
return Result.error(undefined);
}
return Result.ok(node);
}
}

View file

@ -0,0 +1,48 @@
import { AreaExtra, Schemes } from "../presentation/ui/editor/editor";
import { NodeEditor } from "rete";
import { AreaPlugin } from "rete-area-plugin";
export interface Position2D {
x: number;
y: number;
}
export class NodeBehaviorTree {
label: string;
id: string;
outputs: string[];
inputs: string[];
connectTo?: string;
position: Position2D;
constructor(label: string, id: string, outputs: string[], inputs: string[], position: Position2D) {
this.label = label;
this.id = id;
this.outputs = outputs;
this.inputs = inputs;
this.connectTo = undefined;
this.position = position;
}
static fromReteScene(editor: NodeEditor<Schemes>, area: AreaPlugin<Schemes, AreaExtra>): NodeBehaviorTree[] {
const nodes = new Map<string, NodeBehaviorTree>();
editor
.getNodes()
.forEach((el) =>
nodes.set(
el.id,
new NodeBehaviorTree(
el.label,
el.id,
Object.keys(el.outputs),
Object.keys(el.inputs),
area.nodeViews.get(el.id)!.position
)
)
);
editor.getConnections().forEach((el) => console.log(el));
editor.getConnections().forEach((el) => nodes.overrideValue(el.target, { connectTo: el.source }));
return nodes.toArray();
}
}

View file

@ -1,5 +1,6 @@
import * as React from "react";
import { CoreText, CoreTextType } from "../../../core/ui/text/text";
import { Button, Input } from "antd";
import { useRete } from "rete-react-plugin";
import { createEditor } from "./ui/editor/editor";
@ -12,18 +13,14 @@ const skills = {
name: "",
children: [
{
name: "arm",
children: [{ name: "move to", interface: "Vector3", out: "vo" }],
name: "Actions",
children: [{ name: "MoveToPose", interface: "Vector3", out: "vo" }],
},
{
name: "image",
name: "Control",
children: [
{
name: "object detection",
interface: "image",
out: "BoundBox",
},
{ name: "people detection", interface: "image", out: "BoundBox" },
{ name: "Fallback", interface: "Vector3", out: "vo" },
{ name: "Sequence", interface: "Vector3", out: "vo" },
],
},
],
@ -31,8 +28,9 @@ const skills = {
export const behaviorTreeBuilderStore = new BehaviorTreeBuilderStore();
export function BehaviorTreeBuilderScreen() {
const [store] = React.useState(behaviorTreeBuilderStore);
const store = behaviorTreeBuilderStore;
const [ref] = useRete(createEditor);
React.useEffect(() => {
store.init();
@ -62,6 +60,28 @@ export function BehaviorTreeBuilderScreen() {
}}
>
<CoreText text="Robossembler studio" type={CoreTextType.header} />
<Button
onClick={() => {
store.copyBt();
}}
>
copy
</Button>
<Input onChange={(e) => store.setBt(e.target.value)} placeholder="bt JSON" />
<Button
onClick={() => {
store.copyBt();
}}
>
update value
</Button>
<Button
onClick={() => {
store.validateBt();
}}
>
validate
</Button>
</div>
<div style={{ display: "flex" }}>
<div

View file

@ -2,23 +2,107 @@ import makeAutoObservable from "mobx-store-inheritance";
import { HttpError } from "../../../core/repository/http_repository";
import { UiErrorState } from "../../../core/store/base_store";
import { BtNodeView } from "./ui/editor/editor_view";
import { BtBuilderModel, BtNodeView, ReteObserver } from "../model/editor_view";
import { NodeEditor } from "rete";
import { AreaExtra, Schemes } from "./ui/editor/editor";
import { AreaPlugin } from "rete-area-plugin";
import { NodeBehaviorTree } from "../model/node_behavior_tree";
interface I2DArea {
x: number;
y: number;
w: number;
h: number;
}
export class BehaviorTreeBuilderStore extends UiErrorState<HttpError> {
validateBt() {}
area?: I2DArea;
errorHandingStrategy: (error: HttpError) => void;
btNodeView: BtNodeView = new BtNodeView();
reteNode?: ReteObserver;
canRun = true;
nodes: NodeBehaviorTree[] = [
{
label: "Sequence",
id: "1",
outputs: ["a"],
inputs: ["a"],
position: { x: -488.1303402823156, y: -227.90861570911895 },
},
{
label: "Sequence",
id: "2",
outputs: ["a"],
inputs: ["a"],
connectTo: "1",
position: { x: 119.95735514023244, y: -182.24902817216878 },
},
{
label: "Sequence",
id: "6",
outputs: ["a"],
inputs: ["a"],
connectTo: "2",
position: { x: 402.06111561958505, y: -100.97814086372247 },
},
{
label: "MoveToPose",
id: "7",
outputs: ["a"],
inputs: ["a"],
connectTo: "6",
position: { x: 932.0688448287292, y: 90.10780461923443 },
},
{
label: "MoveToPose",
id: "8",
outputs: ["a"],
inputs: ["a"],
connectTo: "6",
position: { x: 898.1684213039546, y: -119.80545806921208 },
},
{
label: "MoveToPose",
id: "3",
outputs: [],
inputs: ["a"],
connectTo: "1",
position: { x: 208.65749743442188, y: 16.256489050033785 },
},
{
label: "MoveToPose",
id: "4",
outputs: [],
inputs: ["a"],
connectTo: "1",
position: { x: -50.46426323140673, y: 99.22642447650014 },
},
{
label: "Sequence",
id: "5",
outputs: ["a"],
inputs: ["a"],
connectTo: "1",
position: { x: -234.54554662642457, y: 245.4012710608967 },
},
];
constructor() {
super();
makeAutoObservable(this);
}
canRun = true;
draw() {}
setBt(value: string): void {
JSON.parse(value) as BtNodeView[];
}
bt(editor: NodeEditor<Schemes>, area: AreaPlugin<Schemes, AreaExtra>) {
console.log(BtBuilderModel.fromReteScene(editor, area));
}
copyBt = () => {
this.reteNode?.emit("200");
};
errorHandingStrategy: (error: HttpError) => void;
dragEnd = (e: EventTarget) => {
if (this.canRun) {
//@ts-expect-error
@ -38,16 +122,14 @@ export class BehaviorTreeBuilderStore extends UiErrorState<HttpError> {
drawPoint.y < this.area!.y + this.area!.h &&
drawPoint.y + drawPoint.h > this.area!.y
) {
this.drawSkill(x, y - (this.area!.y + this.area!.h / 2), name);
this.btNodeView.emit({
x: x,
y: y - (this.area!.y + this.area!.h / 2),
name: name,
});
}
}
drawSkill(x: number, y: number, name: string) {
this.btNodeView.emit({
x: x,
y: y,
name: name,
});
}
async init(): Promise<any> {}
dragZoneSetOffset(offsetTop: number, offsetWidth: number, width: number, height: number) {
this.area = {

View file

@ -14,7 +14,7 @@ const Svg = styled.svg`
const Path = styled.path<{ styles?: (props: any) => any }>`
fill: none;
stroke-width: 5px;
stroke-width: 3px;
stroke: black;
pointer-events: auto;
${(props) => props.styles && props.styles(props)}

View file

@ -1,21 +1,21 @@
import * as React from "react";
import { ClassicPreset } from "rete";
import styled from "styled-components";
import { $socketsize } from "./vars";
const Styles = styled.div`
display: inline-block;
cursor: pointer;
border: 1px solid grey;
width: ${$socketsize}px;
height: ${$socketsize * 2}px;
border: 1px solid black;
width: 20px;
height: 20px;
vertical-align: middle;
background: #fff;
background: black;
z-index: 2;
box-sizing: border-box;
&:hover {
background: #ddd;
}
border-radius: 50%;
`;
export function CustomSocket<T extends ClassicPreset.Socket>(props: { data: T }) {

View file

@ -3,28 +3,39 @@ import { NodeEditor, GetSchemes, ClassicPreset } from "rete";
import { AreaPlugin, AreaExtensions } from "rete-area-plugin";
import { ConnectionPlugin, Presets as ConnectionPresets } from "rete-connection-plugin";
import { ReactPlugin, Presets, ReactArea2D } from "rete-react-plugin";
import { CustomNode } from "./custom_node";
import { CustomSocket } from "./custom_socket";
import { CustomConnection } from "./custom_connection";
import { addCustomBackground } from "./custom_background";
import { StyledNode } from "./style_node";
import { behaviorTreeBuilderStore } from "../../behavior_tree_builder_screen";
import { SequenceNode } from "./nodes/controls_node";
import { ReteObserver } from "../../../model/editor_view";
type Schemes = GetSchemes<ClassicPreset.Node, ClassicPreset.Connection<ClassicPreset.Node, ClassicPreset.Node>>;
type AreaExtra = ReactArea2D<Schemes>;
export type Schemes = GetSchemes<ClassicPreset.Node, ClassicPreset.Connection<ClassicPreset.Node, ClassicPreset.Node>>;
export type AreaExtra = ReactArea2D<Schemes>;
export async function createEditor(container: HTMLElement) {
const socket = new ClassicPreset.Socket("socket");
const observer = new ReteObserver();
behaviorTreeBuilderStore.reteNode = observer;
behaviorTreeBuilderStore.btNodeView.on(async (event) => {
setTimeout(async () => {
const node = new ClassicPreset.Node(event.name);
const { x, y } = areaContainer.area.pointer;
console.log(x, y);
node.addOutput("a", new ClassicPreset.Output(socket));
node.addInput("a", new ClassicPreset.Input(socket));
await editor.addNode(node);
await areaContainer.translate(node.id, { x, y });
}, 50);
});
observer.on(() => {
behaviorTreeBuilderStore.bt(editor, areaContainer);
});
const editor = new NodeEditor<Schemes>();
const areaContainer = new AreaPlugin<Schemes, AreaExtra>(container);
const connection = new ConnectionPlugin<Schemes, AreaExtra>();
@ -38,13 +49,7 @@ export async function createEditor(container: HTMLElement) {
Presets.classic.setup({
customize: {
node(context) {
if (context.payload.label === "Fully customized") {
return CustomNode;
}
if (context.payload.label === "Override styles") {
return StyledNode;
}
return Presets.classic.Node;
return SequenceNode;
},
socket(_context) {
return CustomSocket;
@ -66,18 +71,27 @@ export async function createEditor(container: HTMLElement) {
AreaExtensions.simpleNodesOrder(areaContainer);
const a = new ClassicPreset.Node("Override styles");
for await (const el of behaviorTreeBuilderStore.nodes) {
const node = new ClassicPreset.Node(el.label);
node.id = el.id;
el.outputs.forEach((outputName) => {
node.addOutput(outputName, new ClassicPreset.Output(socket));
});
el.inputs.forEach((inputName) => {
node.addInput(inputName, new ClassicPreset.Input(socket));
});
a.addOutput("a", new ClassicPreset.Output(socket));
a.addInput("a", new ClassicPreset.Input(socket));
await editor.addNode(a);
await editor.addNode(node);
await areaContainer.translate(node.id, el.position);
}
const b = new ClassicPreset.Node("Fully customized");
b.addOutput("a", new ClassicPreset.Output(socket));
b.addInput("a", new ClassicPreset.Input(socket));
await editor.addNode(b);
await editor.addConnection(new ClassicPreset.Connection(a, "a", b, "a"));
behaviorTreeBuilderStore.nodes.forEach(async (el) => {
if (el.connectTo) {
const nodeConnectTo = editor.getNode(el.connectTo!);
const nodeConnect = editor.getNode(el.id);
await editor.addConnection(new ClassicPreset.Connection(nodeConnectTo, "a", nodeConnect, "a"));
}
});
setTimeout(() => {
AreaExtensions.zoomAt(areaContainer, editor.getNodes());

View file

@ -1,7 +0,0 @@
import { TypedEvent } from "../../../../../core/helper/typed_event";
export interface BtDrawView {
x: number;
y: number;
name: string;
}
export class BtNodeView extends TypedEvent<BtDrawView> {}

View file

@ -0,0 +1,167 @@
import * as React from "react";
import { ClassicScheme, RenderEmit, Presets } from "rete-react-plugin";
import styled, { css } from "styled-components";
import { $nodewidth, $socketmargin, $socketsize } from "./vars";
const { RefSocket, RefControl } = Presets.classic;
type NodeExtraData = { width?: number; height?: number };
export const NodeStyles = styled.div<NodeExtraData & { selected: boolean; styles?: (props: any) => any }>`
background: blue;
border: 2px solid grey;
cursor: pointer;
box-sizing: border-box;
width: ${(props) => (Number.isFinite(props.width) ? `${props.width}px` : `${$nodewidth}px`)};
height: ${(props) => (Number.isFinite(props.height) ? `${props.height}px` : "auto")};
padding-bottom: 6px;
user-select: none;
&:hover {
background: #333;
}
${(props) =>
props.selected &&
css`
border-color: red;
`}
.title {
color: white;
font-family: sans-serif;
font-size: 18px;
padding: 8px;
position: relative;
top: 12px;
text-align-last: center;
}
.output {
text-align: right;
}
.input {
text-align: left;
}
.output-socket {
text-align: right;
margin-right: -1px;
display: inline-block;
}
.input-socket {
text-align: left;
margin-left: -1px;
display: inline-block;
}
.input-title,
.output-title {
vertical-align: middle;
color: white;
display: inline-block;
font-family: sans-serif;
font-size: 14px;
margin: ${$socketmargin}px;
line-height: ${$socketsize}px;
}
.input-control {
z-index: 1;
width: calc(100% - ${$socketsize + 2 * $socketmargin}px);
vertical-align: middle;
display: inline-block;
}
.control {
display: block;
padding: ${$socketmargin}px ${$socketsize / 2 + $socketmargin}px;
}
${(props) => props.styles && props.styles(props)}
`;
function sortByIndex<T extends [string, undefined | { index?: number }][]>(entries: T) {
entries.sort((a, b) => {
const ai = a[1]?.index || 0;
const bi = b[1]?.index || 0;
return ai - bi;
});
}
type Props<S extends ClassicScheme> = {
data: S["Node"] & NodeExtraData;
styles?: () => any;
emit: RenderEmit<S>;
};
export type NodeComponent<Scheme extends ClassicScheme> = (props: Props<Scheme>) => JSX.Element;
export function SequenceNode<Scheme extends ClassicScheme>(props: Props<Scheme>) {
const inputs = Object.entries(props.data.inputs);
const outputs = Object.entries(props.data.outputs);
const controls = Object.entries(props.data.controls);
const selected = props.data.selected || false;
const { id, label, width, height } = props.data;
sortByIndex(inputs);
sortByIndex(outputs);
sortByIndex(controls);
return (
<NodeStyles selected={selected} width={width} height={height} styles={props.styles} data-testid="node">
<div
onPointerDown={(e) => {
e.stopPropagation();
}}
className="title"
data-testid="title"
>
{label}
<div>{id}</div>
</div>
{outputs.map(
([key, output]) =>
output && (
<div className="output" key={key} data-testid={`output-${key}`}>
<div className="output-title" data-testid="output-title">
{output?.label}
</div>
<RefSocket
name="output-socket"
side="output"
emit={props.emit}
socketKey={key}
nodeId={id}
payload={output.socket}
/>
</div>
)
)}
{controls.map(([key, control]) => {
return control ? <RefControl key={key} name="control" emit={props.emit} payload={control} /> : null;
})}
{inputs.map(
([key, input]) =>
input && (
<div className="input" key={key} data-testid={`input-${key}`}>
<RefSocket
name="input-socket"
emit={props.emit}
side="input"
socketKey={key}
nodeId={id}
payload={input.socket}
/>
{input && (!input.control || !input.showControl) && (
<div className="input-title" data-testid="input-title">
{input?.label}
</div>
)}
{input?.control && input?.showControl && (
<span className="input-control">
<RefControl key={key} name="input-control" emit={props.emit} payload={input.control} />
</span>
)}
</div>
)
)}
</NodeStyles>
);
}

View file

@ -31,6 +31,7 @@ export const NodeStyles = styled.div<NodeExtraData & { selected: boolean; styles
font-family: sans-serif;
font-size: 18px;
padding: 8px;
height: 100%;
}
.output {
text-align: right;
@ -103,7 +104,6 @@ export function CustomNode<Scheme extends ClassicScheme>(props: Props<Scheme>) {
<div
onPointerDown={(e) => {
e.stopPropagation();
console.log(">>>");
}}
className="title"
data-testid="title"

View file

@ -1,68 +0,0 @@
export {};
// import React from "react";
// import { CoreError, UiErrorState } from "../core/store/base_store";
// import { SelectProjectStore } from "./select_project/presentation/select_project_store";
// export declare type ClassConstructor<T> = {
// new (...args: any[]): T;
// };
// interface MobxReactComponentProps<T extends UiErrorState<CoreError>, ClassConstructor> {
// store: ClassConstructor;
// children: (element: T) => React.ReactElement;
// }
// class UiStateErrorComponent<T extends UiErrorState<CoreError>, K> extends React.Component<
// MobxReactComponentProps<T, K>,
// { store: T | undefined }
// > {
// async componentDidMount(): Promise<void> {
// const store = this.props.store as ClassConstructor<T>;
// console.log(store);
// const s = new store();
// this.setState({ store: s });
// if (this.state !== null) {
// await this.state.store?.init();
// }
// }
// componentWillUnmount(): void {
// if (this.state.store !== undefined) {
// this.state.store.dispose();
// }
// }
// render() {
// if (this.state !== null) {
// if (this.state.store?.isLoading) {
// return <>Loading</>;
// }
// if (this.state.store !== undefined) {
// return this.props.children(this.state.store);
// }
// }
// return (
// <div>
// <>{this.props.children}</>
// </div>
// );
// }
// }
// export const ExampleScreen: React.FC = () => {
// return (
// <div>
// <UiStateErrorComponent<SelectProjectStore, {}> store={SelectProjectStore}>
// {(store) => {
// console.log(store);
// return (
// <div>
// {store.projects.map((el) => {
// return <>{el}</>;
// })}
// </div>
// );
// }}
// </UiStateErrorComponent>
// </div>
// );
// };