Skip to content

Commit 4490ba9

Browse files
committed
ENH: Extend diagram editor to support analyses of diagrams.
1 parent 8b43966 commit 4490ba9

File tree

10 files changed

+250
-73
lines changed

10 files changed

+250
-73
lines changed

packages/frontend/src/analysis/analysis_editor.tsx

Lines changed: 94 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
import Resizable, { type ContextValue } from "@corvu/resizable";
22
import { useParams } from "@solidjs/router";
3-
import { Show, createEffect, createResource, createSignal, useContext } from "solid-js";
3+
import {
4+
Match,
5+
Show,
6+
Switch,
7+
createEffect,
8+
createResource,
9+
createSignal,
10+
useContext,
11+
} from "solid-js";
412
import { Dynamic } from "solid-js/web";
513
import invariant from "tiny-invariant";
614

7-
import { getLiveDoc, useApi } from "../api";
15+
import { useApi } from "../api";
816
import { IconButton, ResizableHandle } from "../components";
9-
import { LiveModelContext, type ModelDocument, enlivenModelDocument } from "../model";
17+
import { DiagramPane } from "../diagram/diagram_editor";
1018
import { ModelPane } from "../model/model_editor";
1119
import {
1220
type CellConstructor,
@@ -17,7 +25,13 @@ import {
1725
import { BrandedToolbar, HelpButton } from "../page";
1826
import { TheoryLibraryContext } from "../stdlib";
1927
import type { AnalysisMeta } from "../theory";
20-
import type { LiveModelAnalysisDocument, ModelAnalysisDocument } from "./document";
28+
import { LiveAnalysisContext } from "./context";
29+
import {
30+
type LiveAnalysisDocument,
31+
type LiveDiagramAnalysisDocument,
32+
type LiveModelAnalysisDocument,
33+
getLiveAnalysis,
34+
} from "./document";
2135
import type { Analysis } from "./types";
2236

2337
import PanelRight from "lucide-solid/icons/panel-right";
@@ -32,22 +46,9 @@ export default function AnalysisPage() {
3246
const theories = useContext(TheoryLibraryContext);
3347
invariant(theories, "Must provide theory library as context to analysis page");
3448

35-
const [liveAnalysis] = createResource<LiveModelAnalysisDocument>(async () => {
36-
const liveDoc = await getLiveDoc<ModelAnalysisDocument>(api, refId);
37-
const { doc } = liveDoc;
38-
invariant(doc.type === "analysis", () => `Expected analysis, got type: ${doc.type}`);
39-
40-
const liveModelDoc = await getLiveDoc<ModelDocument>(api, doc.analysisOf.refId);
41-
const liveModel = enlivenModelDocument(doc.analysisOf.refId, liveModelDoc, theories);
42-
43-
return { refId, liveDoc, liveModel };
44-
});
49+
const [liveAnalysis] = createResource(() => getLiveAnalysis(refId, api, theories));
4550

46-
return (
47-
<Show when={liveAnalysis()}>
48-
{(liveAnalysis) => <AnalysisDocumentEditor liveAnalysis={liveAnalysis()} />}
49-
</Show>
50-
);
51+
return <AnalysisDocumentEditor liveAnalysis={liveAnalysis()} />;
5152
}
5253

5354
/** Editor for a model of a double theory.
@@ -56,7 +57,7 @@ The editor includes a notebook for the model itself plus another pane for
5657
performing analysis of the model.
5758
*/
5859
export function AnalysisDocumentEditor(props: {
59-
liveAnalysis: LiveModelAnalysisDocument;
60+
liveAnalysis?: LiveAnalysisDocument;
6061
}) {
6162
const [resizableContext, setResizableContext] = createSignal<ContextValue>();
6263
const [isSidePanelOpen, setSidePanelOpen] = createSignal(true);
@@ -106,7 +107,7 @@ export function AnalysisDocumentEditor(props: {
106107
</Show>
107108
</IconButton>
108109
</BrandedToolbar>
109-
<ModelPane liveModel={props.liveAnalysis.liveModel} />
110+
<AnalysisOfPane liveAnalysis={props.liveAnalysis} />
110111
</Resizable.Panel>
111112
<ResizableHandle hidden={!isSidePanelOpen()} />
112113
<Resizable.Panel
@@ -120,7 +121,11 @@ export function AnalysisDocumentEditor(props: {
120121
>
121122
<div class="notebook-container">
122123
<h2>Analysis</h2>
123-
<AnalysisNotebookEditor liveAnalysis={props.liveAnalysis} />
124+
<Show when={props.liveAnalysis}>
125+
{(liveAnalysis) => (
126+
<AnalysisNotebookEditor liveAnalysis={liveAnalysis()} />
127+
)}
128+
</Show>
124129
</div>
125130
</Resizable.Panel>
126131
</>
@@ -130,48 +135,94 @@ export function AnalysisDocumentEditor(props: {
130135
);
131136
}
132137

138+
const AnalysisOfPane = (props: {
139+
liveAnalysis?: LiveAnalysisDocument;
140+
}) => (
141+
<Switch>
142+
<Match when={props.liveAnalysis?.analysisType === "model" && props.liveAnalysis.liveModel}>
143+
{(liveModel) => <ModelPane liveModel={liveModel()} />}
144+
</Match>
145+
<Match
146+
when={props.liveAnalysis?.analysisType === "diagram" && props.liveAnalysis.liveDiagram}
147+
>
148+
{(liveDiagram) => <DiagramPane liveDiagram={liveDiagram()} />}
149+
</Match>
150+
</Switch>
151+
);
152+
133153
/** Notebook editor for analyses of models of double theories.
134154
*/
135155
export function AnalysisNotebookEditor(props: {
136-
liveAnalysis: LiveModelAnalysisDocument;
156+
liveAnalysis: LiveAnalysisDocument;
137157
}) {
138158
const liveDoc = () => props.liveAnalysis.liveDoc;
139159

140-
const cellConstructors = () =>
141-
(props.liveAnalysis.liveModel.theory()?.modelAnalyses ?? []).map(analysisCellConstructor);
160+
const cellConstructors = () => {
161+
let meta = undefined;
162+
if (props.liveAnalysis.analysisType === "model") {
163+
meta = props.liveAnalysis.liveModel.theory()?.modelAnalyses;
164+
} else if (props.liveAnalysis.analysisType === "diagram") {
165+
meta = props.liveAnalysis.liveDiagram.liveModel.theory()?.diagramAnalyses;
166+
}
167+
return (meta ?? []).map(analysisCellConstructor);
168+
};
142169

143170
return (
144-
<LiveModelContext.Provider value={props.liveAnalysis.liveModel}>
171+
<LiveAnalysisContext.Provider value={props.liveAnalysis}>
145172
<NotebookEditor
146173
handle={liveDoc().docHandle}
147174
path={["notebook"]}
148175
notebook={liveDoc().doc.notebook}
149176
changeNotebook={(f) => liveDoc().changeDoc((doc) => f(doc.notebook))}
150-
formalCellEditor={ModelAnalysisCellEditor}
177+
formalCellEditor={AnalysisCellEditor}
151178
cellConstructors={cellConstructors()}
152179
noShortcuts={true}
153180
/>
154-
</LiveModelContext.Provider>
181+
</LiveAnalysisContext.Provider>
155182
);
156183
}
157184

158-
function ModelAnalysisCellEditor(props: FormalCellEditorProps<Analysis<unknown>>) {
159-
const liveModel = useContext(LiveModelContext);
160-
invariant(liveModel, "Live model should be provided as context for analysis");
185+
function AnalysisCellEditor(props: FormalCellEditorProps<Analysis<unknown>>) {
186+
const liveAnalysis = useContext(LiveAnalysisContext);
187+
invariant(liveAnalysis, "Live analysis should be provided as context for cell editor");
161188

162189
return (
163-
<Show when={liveModel.theory()?.modelAnalysis(props.content.id)}>
164-
{(analysis) => (
165-
<Dynamic
166-
component={analysis().component}
167-
liveModel={liveModel}
168-
content={props.content.content}
169-
changeContent={(f: (c: unknown) => void) =>
170-
props.changeContent((content) => f(content.content))
171-
}
172-
/>
173-
)}
174-
</Show>
190+
<Switch>
191+
<Match
192+
when={
193+
liveAnalysis.analysisType === "model" &&
194+
liveAnalysis.liveModel.theory()?.modelAnalysis(props.content.id)
195+
}
196+
>
197+
{(analysis) => (
198+
<Dynamic
199+
component={analysis().component}
200+
liveModel={(liveAnalysis as LiveModelAnalysisDocument).liveModel}
201+
content={props.content.content}
202+
changeContent={(f: (c: unknown) => void) =>
203+
props.changeContent((content) => f(content.content))
204+
}
205+
/>
206+
)}
207+
</Match>
208+
<Match
209+
when={
210+
liveAnalysis.analysisType === "diagram" &&
211+
liveAnalysis.liveDiagram.liveModel.theory()?.diagramAnalysis(props.content.id)
212+
}
213+
>
214+
{(analysis) => (
215+
<Dynamic
216+
component={analysis().component}
217+
liveDiagram={(liveAnalysis as LiveDiagramAnalysisDocument).liveDiagram}
218+
content={props.content.content}
219+
changeContent={(f: (c: unknown) => void) =>
220+
props.changeContent((content) => f(content.content))
221+
}
222+
/>
223+
)}
224+
</Match>
225+
</Switch>
175226
);
176227
}
177228

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { createContext } from "solid-js";
2+
3+
import type { LiveAnalysisDocument } from "./document";
4+
5+
/** Context for a live analysis. */
6+
export const LiveAnalysisContext = createContext<LiveAnalysisDocument>();

packages/frontend/src/analysis/document.ts

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
import type { ExternRef, LiveDoc } from "../api";
2-
import type { LiveDiagramDocument } from "../diagram";
3-
import type { LiveModelDocument } from "../model";
1+
import invariant from "tiny-invariant";
2+
3+
import { type Api, type ExternRef, type LiveDoc, getLiveDoc } from "../api";
4+
import { type LiveDiagramDocument, getLiveDiagram } from "../diagram";
5+
import { type LiveModelDocument, getLiveModel } from "../model";
46
import { type Notebook, newNotebook } from "../notebook";
7+
import type { TheoryLibrary } from "../stdlib";
58
import type { Analysis } from "./types";
69

710
/** Common base type for all analysis documents. */
@@ -62,6 +65,8 @@ export const newDiagramAnalysisDocument = (refId: string): DiagramAnalysisDocume
6265

6366
/** A model analysis document "live" for editing. */
6467
export type LiveModelAnalysisDocument = {
68+
analysisType: "model";
69+
6570
/** The ref for which this is a live document. */
6671
refId: string;
6772

@@ -74,6 +79,8 @@ export type LiveModelAnalysisDocument = {
7479

7580
/** A diagram analysis document "live" for editing. */
7681
export type LiveDiagramAnalysisDocument = {
82+
analysisType: "diagram";
83+
7784
/** The ref for which this is a live document. */
7885
refId: string;
7986

@@ -83,3 +90,38 @@ export type LiveDiagramAnalysisDocument = {
8390
/** Live diagarm that the analysis is of. */
8491
liveDiagram: LiveDiagramDocument;
8592
};
93+
94+
/** An analysis document "live" for editing. */
95+
export type LiveAnalysisDocument = LiveModelAnalysisDocument | LiveDiagramAnalysisDocument;
96+
97+
/** Retrieve an analysis and make it "live" for editing. */
98+
export async function getLiveAnalysis(
99+
refId: string,
100+
api: Api,
101+
theories: TheoryLibrary,
102+
): Promise<LiveAnalysisDocument> {
103+
const liveDoc = await getLiveDoc<AnalysisDocument>(api, refId);
104+
const { doc } = liveDoc;
105+
invariant(doc.type === "analysis", () => `Expected analysis, got type: ${doc.type}`);
106+
107+
const analysisOf = doc.analysisOf;
108+
if (analysisOf.taxon === "model") {
109+
const liveModel = await getLiveModel(analysisOf.refId, api, theories);
110+
return {
111+
analysisType: "model",
112+
refId,
113+
liveDoc: liveDoc as LiveDoc<ModelAnalysisDocument>,
114+
liveModel,
115+
};
116+
} else if (analysisOf.taxon === "diagram") {
117+
const liveDiagram = await getLiveDiagram(analysisOf.refId, api, theories);
118+
return {
119+
analysisType: "diagram",
120+
refId,
121+
liveDoc: liveDoc as LiveDoc<DiagramAnalysisDocument>,
122+
liveDiagram,
123+
};
124+
} else {
125+
throw new Error(`Analysis of unknown document: ${analysisOf}`);
126+
}
127+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export * from "./types";
22
export * from "./document";
3+
export * from "./context";

packages/frontend/src/analysis/types.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { Component } from "solid-js";
22

3+
import type { LiveDiagramDocument } from "../diagram";
34
import type { LiveModelDocument } from "../model";
45

56
/** An analysis of a formal object.
@@ -35,3 +36,12 @@ export type ModelAnalysisProps<T> = AnalysisProps<T> & {
3536

3637
/** Component that renders an analysis of a model. */
3738
export type ModelAnalysisComponent<T> = Component<ModelAnalysisProps<T>>;
39+
40+
/** Props passed to a diagram analysis component. */
41+
export type DiagramAnalysisProps<T> = AnalysisProps<T> & {
42+
/** The diagram being analyzed. */
43+
liveDiagram: LiveDiagramDocument;
44+
};
45+
46+
/** Component that renders an analysis of diagram model. */
47+
export type DiagramAnalysisComponent<T> = Component<DiagramAnalysisProps<T>>;

0 commit comments

Comments
 (0)