File size: 3,288 Bytes
f586f77
 
6c17bbb
f586f77
 
 
 
2e0eaa2
 
f586f77
2e0eaa2
 
 
f586f77
 
2e0eaa2
 
 
f586f77
 
 
 
 
 
 
 
2e0eaa2
 
 
 
 
 
f586f77
 
 
 
6c17bbb
 
f586f77
6c17bbb
f586f77
 
 
 
6c17bbb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f586f77
6c17bbb
 
 
 
f586f77
6c17bbb
 
 
f586f77
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2e0eaa2
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import { useEffect } from "react";

import { AppLayout, Button, DatasetPicker, describePlot, Divider, useDataset } from "@elvis/ui";
// import { LoadingScreen } from "@elvis/ui";

import { OKABE_ITO_COLORS } from "./colors.ts";
import { DATASETS } from "./datasets.ts";
import DecisionBoundaryPlot from "./DecisionBoundaryPlot.tsx";
import Sidebar from "./Sidebar.tsx";
import Usage from "./Usage.tsx";
import useAppLogic from "./useAppLogic.ts";


const MANUAL_CLASSES = OKABE_ITO_COLORS.map(({ name, value }) => ({ name, color: value }));

export default function App() {

  const app = useAppLogic();
  const dataset = useDataset({ task: "classification", builtins: DATASETS, classes: MANUAL_CLASSES });
  const processed = dataset.result?.ok ? dataset.result.dataset : null;

  // Plot whatever the picker produces and send it to Python. Drawing points by
  // hand keeps the view still; loading a table zooms to fit it.
  useEffect(() => {
    app.handleSetDataset(processed, dataset.source !== "manual");
  }, [dataset.result]);

  //if (!app.backendReady) {
    //return <LoadingScreen message="Loading" />;
  //}

  return (
    <AppLayout
      title="Decision Boundary"
      usage={<Usage />}
      examples={<p>Examples are coming soon.</p>}
      panel={
        <>
          <DatasetPicker dataset={dataset} />
          <Divider />
          <Sidebar
            modelSettings={app.modelSettings}
            setModelSettings={app.handleModelSettingsChange}
          />
          <Divider />
          {app.pythonStatus === "loading" && (
            <p className="text-sm text-ink-muted">Loading Python… actions will run once it is ready.</p>
          )}
          {app.pythonStatus === "failed" && (
            <p className="text-red-600 font-semibold">Python failed to load. Try reloading the page.</p>
          )}
          <section className="flex flex-col gap-4">
            <h3 className="text-xs font-semibold uppercase tracking-widest text-ink-muted">Fit</h3>
            <Button
              label="Get Decision Boundary"
              onClick={app.handleGetDecisionBoundary}
              variant="primary"
            />
            <label className="flex items-center gap-2 text-sm text-gray-700 cursor-pointer">
              <input
                type="checkbox"
                checked={app.autoFit}
                onChange={(e) => app.handleAutoFitChange(e.target.checked)}
                className="size-4 accent-ink"
              />
              Fit automatically
            </label>
            {app.modelError && (
              <p className="text-red-600 font-semibold">{app.modelError}</p>
            )}
          </section>
        </>
      }
    >
      <DecisionBoundaryPlot
        dataPoints={app.dataPoints}
        onDataClick={dataset.source === "manual" ? dataset.manual.add : null}
        decisionBoundaryResult={app.decisionBoundaryResult}
        xRangeRef={app.xRangeRef}
        yRangeRef={app.yRangeRef}
        setRanges={app.handleRangeChange}
        labelColors={app.labelColors}
        plotHandle={app.plotHandle}
      />
      {dataset.source !== "manual" && dataset.result?.ok && (
        <p className="mt-2 text-xs text-ink-muted">{describePlot(dataset.result.dataset)}</p>
      )}
    </AppLayout>
  );
}