@medicine-wheel/app 0.17.6 → 0.17.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/episodes/[id]/page.tsx +25 -1
- package/components/episode-lineage-web.tsx +54 -167
- package/package.json +27 -27
|
@@ -30,9 +30,12 @@ import Link from "next/link";
|
|
|
30
30
|
import type { RelationalNode, RelationalEdge } from "@/lib/types";
|
|
31
31
|
import { EpisodeFilesPanel } from "@/components/episode-files-panel";
|
|
32
32
|
import { EpisodeHoldings } from "@/components/episode-holdings";
|
|
33
|
+
import { EpisodeLineageWeb, type LineageEpisode } from "@/components/episode-lineage-web";
|
|
33
34
|
import {
|
|
34
35
|
chronicleKey,
|
|
36
|
+
episodeDateOf,
|
|
35
37
|
episodeFolderOfNode,
|
|
38
|
+
lineageEdges,
|
|
36
39
|
relationsOf,
|
|
37
40
|
type EpisodeRelation,
|
|
38
41
|
type LineageEdge,
|
|
@@ -113,6 +116,8 @@ export default function EpisodePage({ params }: { params: Promise<{ id: string }
|
|
|
113
116
|
const [episode, setEpisode] = useState<EpisodeNode | null>(null);
|
|
114
117
|
const [attention, setAttention] = useState<EpisodeNode[]>([]);
|
|
115
118
|
const [web, setWeb] = useState<{ nodes: EpisodeNode[]; edges: RelationalEdge[] } | null>(null);
|
|
119
|
+
// The whole chronicle's lineage, for the picture: two relations away needs more than this episode's web.
|
|
120
|
+
const [chronicle, setChronicle] = useState<{ episodes: LineageEpisode[]; edges: LineageEdge[] } | null>(null);
|
|
116
121
|
const [before, setBefore] = useState<EpisodeNode | null>(null);
|
|
117
122
|
const [after, setAfter] = useState<EpisodeNode | null>(null);
|
|
118
123
|
const [loading, setLoading] = useState(true);
|
|
@@ -122,10 +127,11 @@ export default function EpisodePage({ params }: { params: Promise<{ id: string }
|
|
|
122
127
|
setLoading(true);
|
|
123
128
|
try {
|
|
124
129
|
const encoded = encodeURIComponent(id);
|
|
125
|
-
const [nodeRes, attentionRes, webRes] = await Promise.all([
|
|
130
|
+
const [nodeRes, attentionRes, webRes, edgesRes] = await Promise.all([
|
|
126
131
|
fetch(`/api/nodes?kind=chronicle_episode&limit=all`),
|
|
127
132
|
fetch(`/api/nodes?kind=attention&parent_id=${encoded}&limit=all`),
|
|
128
133
|
fetch(`/api/nodes/${encoded}/web?depth=1`),
|
|
134
|
+
fetch(`/api/edges?limit=all`).catch(() => null),
|
|
129
135
|
]);
|
|
130
136
|
|
|
131
137
|
if (!nodeRes.ok) throw new Error(`Episodes: ${nodeRes.status}`);
|
|
@@ -138,6 +144,19 @@ export default function EpisodePage({ params }: { params: Promise<{ id: string }
|
|
|
138
144
|
setBefore(at > 0 ? ordered[at - 1] : null);
|
|
139
145
|
setAfter(at >= 0 && at < ordered.length - 1 ? ordered[at + 1] : null);
|
|
140
146
|
|
|
147
|
+
// The picture is a second answer: without the edges, the lists below still stand.
|
|
148
|
+
if (edgesRes?.ok) {
|
|
149
|
+
const body = await edgesRes.json();
|
|
150
|
+
const allEdges: LineageEdge[] = Array.isArray(body) ? body : (body.edges ?? []);
|
|
151
|
+
const ids = new Set(all.map((n) => n.id));
|
|
152
|
+
setChronicle({
|
|
153
|
+
episodes: ordered.map((n) => ({ id: n.id, name: n.name, direction: n.direction, date: episodeDateOf(n) })),
|
|
154
|
+
edges: lineageEdges(allEdges, (other) => ids.has(other)),
|
|
155
|
+
});
|
|
156
|
+
} else {
|
|
157
|
+
setChronicle(null);
|
|
158
|
+
}
|
|
159
|
+
|
|
141
160
|
setAttention(attentionRes.ok ? ((await attentionRes.json()).nodes ?? []) : []);
|
|
142
161
|
|
|
143
162
|
// A 404 here means the node is not in the wheel at all — distinct from a
|
|
@@ -257,6 +276,11 @@ export default function EpisodePage({ params }: { params: Promise<{ id: string }
|
|
|
257
276
|
<h2 className="text-sm font-semibold mb-2">
|
|
258
277
|
Lineage {lineage.length > 0 && `(${lineage.length})`}
|
|
259
278
|
</h2>
|
|
279
|
+
{lineage.length > 0 && chronicle && (
|
|
280
|
+
<div className="mb-3">
|
|
281
|
+
<EpisodeLineageWeb episodes={chronicle.episodes} edges={chronicle.edges} around={id} panel={false} />
|
|
282
|
+
</div>
|
|
283
|
+
)}
|
|
260
284
|
{lineage.length === 0 ? (
|
|
261
285
|
<p className="text-sm text-muted-foreground">
|
|
262
286
|
No relation to another episode has been woven yet.
|
|
@@ -4,18 +4,16 @@
|
|
|
4
4
|
* The chronicle as a lineage: every episode on a spine read left to right, in
|
|
5
5
|
* its direction's band, with the relations between episodes drawn as arcs.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* Tap an episode to see its relations and the words that say why each holds;
|
|
12
|
-
* tap it again, or its name, to open it. Touch first — a phone has no hover.
|
|
13
|
-
* Ref jgwill/medicine-wheel#156.
|
|
7
|
+
* The web itself is `LineageWeb` from `@medicine-wheel/graph-viz`, the one the
|
|
8
|
+
* Miadi chronicle draws too (#162). This file adapts the wheel's edges to it
|
|
9
|
+
* and shows, under it, the selected episode's relations and the words that say
|
|
10
|
+
* why each holds. A tap selects, a second tap opens. Ref jgwill/medicine-wheel#156.
|
|
14
11
|
*/
|
|
15
12
|
|
|
16
|
-
import {
|
|
13
|
+
import { useMemo, useState } from "react";
|
|
17
14
|
import Link from "next/link";
|
|
18
15
|
import { useRouter } from "next/navigation";
|
|
16
|
+
import { LineageWeb, type LineageWebEdge, type LineageWebLayoutConfig } from "@medicine-wheel/graph-viz";
|
|
19
17
|
import { isContinuation, relationsOf, type LineageEdge } from "@/lib/episode-lineage";
|
|
20
18
|
|
|
21
19
|
export interface LineageEpisode {
|
|
@@ -26,82 +24,52 @@ export interface LineageEpisode {
|
|
|
26
24
|
date?: string;
|
|
27
25
|
}
|
|
28
26
|
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
const TOP = 28;
|
|
33
|
-
const AXIS = 26;
|
|
34
|
-
const STEP = 15;
|
|
35
|
-
const MARGIN_L = 72;
|
|
36
|
-
const MARGIN_R = 24;
|
|
37
|
-
const H = TOP + BAND_H * BANDS.length + AXIS;
|
|
27
|
+
const BAND_LABELS = { east: "🌅 east", south: "🔥 south", west: "🌊 west", north: "❄️ north" };
|
|
28
|
+
// The wheel's page is narrower than the chronicle's: closer bands, less room for arcs.
|
|
29
|
+
const LAYOUT: Partial<LineageWebLayoutConfig> = { headroom: 90, bandGap: 84, footroom: 84, minStep: 15, minWidth: 360 };
|
|
38
30
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
31
|
+
/** `E044` from a `chronicle:<date>-episode-044-…` id; the dot's chip shows the name under it. */
|
|
32
|
+
function markOf(id: string): string {
|
|
33
|
+
const n = /episode-(\d+)/i.exec(id)?.[1];
|
|
34
|
+
return n ? `E${n.padStart(3, "0")}` : "·";
|
|
42
35
|
}
|
|
43
36
|
|
|
44
37
|
export function EpisodeLineageWeb({
|
|
45
38
|
episodes,
|
|
46
39
|
edges,
|
|
47
40
|
matches,
|
|
41
|
+
around,
|
|
42
|
+
panel = true,
|
|
48
43
|
}: {
|
|
49
44
|
/** In chronicle order, oldest first. */
|
|
50
45
|
episodes: LineageEpisode[];
|
|
51
46
|
/** Episode↔episode relations only. */
|
|
52
47
|
edges: LineageEdge[];
|
|
53
|
-
/** When a filter is typed: the episodes it matches.
|
|
48
|
+
/** When a filter is typed: the episodes it matches. Their relatives stay, as rings. */
|
|
54
49
|
matches?: Set<string> | null;
|
|
50
|
+
/** Open centred on this episode and its relatives (an episode's own page). */
|
|
51
|
+
around?: string;
|
|
52
|
+
/** The selected episode's relations under the web. Off where the page lists them already. */
|
|
53
|
+
panel?: boolean;
|
|
55
54
|
}) {
|
|
56
55
|
const router = useRouter();
|
|
57
|
-
const
|
|
58
|
-
const [selected, setSelected] = useState<string | null>(null);
|
|
59
|
-
const [hovered, setHovered] = useState<string | null>(null);
|
|
60
|
-
|
|
61
|
-
const layout = useMemo(() => {
|
|
62
|
-
const position = new Map<string, { x: number; y: number }>();
|
|
63
|
-
episodes.forEach((episode, i) => {
|
|
64
|
-
const x = MARGIN_L + i * STEP;
|
|
65
|
-
// A small stagger so neighbours in one band do not sit on one line.
|
|
66
|
-
const y = TOP + (bandOf(episode.direction) + 0.5) * BAND_H + ((i % 3) - 1) * 13;
|
|
67
|
-
position.set(episode.id, { x, y });
|
|
68
|
-
});
|
|
69
|
-
const width = Math.max(MARGIN_L + (episodes.length - 1) * STEP + MARGIN_R, 360);
|
|
70
|
-
|
|
71
|
-
const months: Array<{ x: number; label: string }> = [];
|
|
72
|
-
let last = "";
|
|
73
|
-
episodes.forEach((episode, i) => {
|
|
74
|
-
const month = episode.date?.slice(0, 7) ?? "";
|
|
75
|
-
if (month && month !== last) {
|
|
76
|
-
months.push({ x: MARGIN_L + i * STEP, label: month });
|
|
77
|
-
last = month;
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
const degree = new Map<string, number>();
|
|
82
|
-
for (const edge of edges) {
|
|
83
|
-
degree.set(edge.from_id, (degree.get(edge.from_id) ?? 0) + 1);
|
|
84
|
-
degree.set(edge.to_id, (degree.get(edge.to_id) ?? 0) + 1);
|
|
85
|
-
}
|
|
86
|
-
return { position, width, months, degree };
|
|
87
|
-
}, [episodes, edges]);
|
|
56
|
+
const [selected, setSelected] = useState<string | null>(around ?? null);
|
|
88
57
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}, [focus, edges]);
|
|
58
|
+
const webEpisodes = useMemo(
|
|
59
|
+
() => episodes.map((e) => ({ id: e.id, label: markOf(e.id), title: e.name, direction: e.direction, date: e.date })),
|
|
60
|
+
[episodes],
|
|
61
|
+
);
|
|
62
|
+
const webEdges = useMemo<LineageWebEdge[]>(
|
|
63
|
+
() =>
|
|
64
|
+
edges.map((edge) => ({
|
|
65
|
+
id: edge.id,
|
|
66
|
+
from: edge.from_id,
|
|
67
|
+
to: edge.to_id,
|
|
68
|
+
kind: isContinuation(edge) ? "continuation" : "relation",
|
|
69
|
+
words: edge.description,
|
|
70
|
+
})),
|
|
71
|
+
[edges],
|
|
72
|
+
);
|
|
105
73
|
|
|
106
74
|
const byId = useMemo(() => new Map(episodes.map((e) => [e.id, e])), [episodes]);
|
|
107
75
|
const chosen = selected ? byId.get(selected) : undefined;
|
|
@@ -110,106 +78,24 @@ export function EpisodeLineageWeb({
|
|
|
110
78
|
[selected, edges],
|
|
111
79
|
);
|
|
112
80
|
|
|
113
|
-
function open(id: string) {
|
|
114
|
-
router.push(`/episodes/${encodeURIComponent(id)}`);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function tap(id: string) {
|
|
118
|
-
if (selected === id) open(id);
|
|
119
|
-
else setSelected(id);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const faded = (id: string) =>
|
|
123
|
-
(near !== null && !near.has(id)) || (matches != null && !matches.has(id));
|
|
124
|
-
|
|
125
81
|
return (
|
|
126
82
|
<div className="space-y-3">
|
|
127
|
-
<div
|
|
128
|
-
<
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
{
|
|
139
|
-
|
|
140
|
-
<line
|
|
141
|
-
x1={0}
|
|
142
|
-
x2={layout.width}
|
|
143
|
-
y1={TOP + (i + 1) * BAND_H}
|
|
144
|
-
y2={TOP + (i + 1) * BAND_H}
|
|
145
|
-
stroke="var(--mw-border)"
|
|
146
|
-
strokeWidth={1}
|
|
147
|
-
opacity={0.5}
|
|
148
|
-
/>
|
|
149
|
-
<text x={8} y={TOP + i * BAND_H + 16} fontSize={11} fill="var(--mw-muted)">
|
|
150
|
-
{BAND_ICON[band]} {band}
|
|
151
|
-
</text>
|
|
152
|
-
</g>
|
|
153
|
-
))}
|
|
154
|
-
|
|
155
|
-
{layout.months.map((m) => (
|
|
156
|
-
<text key={m.label} x={m.x} y={H - 8} fontSize={10} fill="var(--mw-muted)">
|
|
157
|
-
{m.label}
|
|
158
|
-
</text>
|
|
159
|
-
))}
|
|
160
|
-
|
|
161
|
-
{edges.map((edge) => {
|
|
162
|
-
const a = layout.position.get(edge.from_id);
|
|
163
|
-
const b = layout.position.get(edge.to_id);
|
|
164
|
-
if (!a || !b) return null;
|
|
165
|
-
const lit = focus !== null && (edge.from_id === focus || edge.to_id === focus);
|
|
166
|
-
const lift = Math.min(110, Math.abs(b.x - a.x) * 0.3) + 18;
|
|
167
|
-
const top = Math.max(6, Math.min(a.y, b.y) - lift);
|
|
168
|
-
const continuation = isContinuation(edge);
|
|
169
|
-
return (
|
|
170
|
-
<path
|
|
171
|
-
key={edge.id}
|
|
172
|
-
d={`M ${a.x} ${a.y} Q ${(a.x + b.x) / 2} ${top} ${b.x} ${b.y}`}
|
|
173
|
-
fill="none"
|
|
174
|
-
stroke={lit ? "var(--mw-primary)" : "var(--mw-muted)"}
|
|
175
|
-
strokeWidth={lit ? 2 : 1}
|
|
176
|
-
strokeDasharray={continuation ? undefined : "3 3"}
|
|
177
|
-
opacity={lit ? 1 : focus ? 0.12 : continuation ? 0.55 : 0.3}
|
|
178
|
-
/>
|
|
179
|
-
);
|
|
180
|
-
})}
|
|
181
|
-
|
|
182
|
-
{episodes.map((episode) => {
|
|
183
|
-
const p = layout.position.get(episode.id)!;
|
|
184
|
-
const related = (layout.degree.get(episode.id) ?? 0) > 0;
|
|
185
|
-
const isSelected = episode.id === selected;
|
|
186
|
-
return (
|
|
187
|
-
<g
|
|
188
|
-
key={episode.id}
|
|
189
|
-
onClick={() => tap(episode.id)}
|
|
190
|
-
onMouseEnter={() => setHovered(episode.id)}
|
|
191
|
-
onMouseLeave={() => setHovered(null)}
|
|
192
|
-
style={{ cursor: "pointer" }}
|
|
193
|
-
opacity={faded(episode.id) ? 0.2 : 1}
|
|
194
|
-
>
|
|
195
|
-
<title>{episode.name}</title>
|
|
196
|
-
{/* A target a finger can hit, larger than the dot it holds. */}
|
|
197
|
-
<circle cx={p.x} cy={p.y} r={9} fill="transparent" />
|
|
198
|
-
<circle
|
|
199
|
-
cx={p.x}
|
|
200
|
-
cy={p.y}
|
|
201
|
-
r={related ? 4.5 : 3}
|
|
202
|
-
fill={`var(--mw-${episode.direction ?? "west"}-ink, var(--mw-muted))`}
|
|
203
|
-
stroke={isSelected ? "var(--mw-primary)" : "none"}
|
|
204
|
-
strokeWidth={isSelected ? 2.5 : 0}
|
|
205
|
-
/>
|
|
206
|
-
</g>
|
|
207
|
-
);
|
|
208
|
-
})}
|
|
209
|
-
</svg>
|
|
83
|
+
<div className="border rounded-lg bg-card p-2">
|
|
84
|
+
<LineageWeb
|
|
85
|
+
episodes={webEpisodes}
|
|
86
|
+
edges={webEdges}
|
|
87
|
+
selected={selected}
|
|
88
|
+
onSelect={setSelected}
|
|
89
|
+
onOpen={(id) => router.push(`/episodes/${encodeURIComponent(id)}`)}
|
|
90
|
+
matches={matches}
|
|
91
|
+
layout={LAYOUT}
|
|
92
|
+
bandLabels={BAND_LABELS}
|
|
93
|
+
startAt={around ? "start" : "end"}
|
|
94
|
+
defaultView={around ? { scope: "around" } : undefined}
|
|
95
|
+
/>
|
|
210
96
|
</div>
|
|
211
97
|
|
|
212
|
-
{chosen ? (
|
|
98
|
+
{!panel ? null : chosen ? (
|
|
213
99
|
<div className="p-3 border rounded-lg bg-card space-y-2">
|
|
214
100
|
<div className="flex items-baseline justify-between gap-3 flex-wrap">
|
|
215
101
|
<Link href={`/episodes/${encodeURIComponent(chosen.id)}`} className="text-sm font-medium hover:underline">
|
|
@@ -237,9 +123,10 @@ export function EpisodeLineageWeb({
|
|
|
237
123
|
</div>
|
|
238
124
|
) : (
|
|
239
125
|
<p className="text-xs text-muted-foreground">
|
|
240
|
-
Each dot is an episode, oldest on the left, in its direction's band. Solid arcs carry
|
|
241
|
-
a thread forward (continues from); dashed arcs relate two episodes. Tap a dot to see why
|
|
242
|
-
they are related; tap it again to open it.
|
|
126
|
+
Each dot is an episode, oldest on the left, in its direction's band. Solid arcs above carry
|
|
127
|
+
a thread forward (continues from); dashed arcs below relate two episodes. Tap a dot to see why
|
|
128
|
+
they are related; tap it again to open it. A filter keeps what it found and, as rings, the
|
|
129
|
+
episodes related to it.
|
|
243
130
|
</p>
|
|
244
131
|
)}
|
|
245
132
|
</div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@medicine-wheel/app",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.8",
|
|
4
4
|
"description": "Medicine Wheel — Interactive visual layer for Indigenous relational research with Four Directions, ceremonies, and narrative arcs",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mw": "dist/cli/mw.js",
|
|
@@ -91,32 +91,32 @@
|
|
|
91
91
|
"release:major": "npm run version:major && npm run publish:all && npm run release:commit"
|
|
92
92
|
},
|
|
93
93
|
"dependencies": {
|
|
94
|
-
"@medicine-wheel/ceremonial-diary": "^0.17.
|
|
95
|
-
"@medicine-wheel/ceremony-protocol": "^0.17.
|
|
96
|
-
"@medicine-wheel/community-identity": "^0.17.
|
|
97
|
-
"@medicine-wheel/community-review": "^0.17.
|
|
98
|
-
"@medicine-wheel/consent-lifecycle": "^0.17.
|
|
99
|
-
"@medicine-wheel/creative-orientation": "^0.17.
|
|
100
|
-
"@medicine-wheel/data-store": "^0.17.
|
|
101
|
-
"@medicine-wheel/data-store-postgres": "^0.17.
|
|
102
|
-
"@medicine-wheel/fire-keeper": "^0.17.
|
|
103
|
-
"@medicine-wheel/github-ceremony": "^0.17.
|
|
104
|
-
"@medicine-wheel/graph-viz": "^0.17.
|
|
105
|
-
"@medicine-wheel/honcho": "^0.17.
|
|
106
|
-
"@medicine-wheel/importance-unit": "^0.17.
|
|
107
|
-
"@medicine-wheel/mcp": "^4.17.
|
|
108
|
-
"@medicine-wheel/memory": "^0.17.
|
|
109
|
-
"@medicine-wheel/narrative-cluster": "^0.17.
|
|
110
|
-
"@medicine-wheel/narrative-engine": "^0.17.
|
|
111
|
-
"@medicine-wheel/ontology-core": "^0.17.
|
|
112
|
-
"@medicine-wheel/perception-layer": "^0.17.
|
|
113
|
-
"@medicine-wheel/prompt-decomposition": "^0.17.
|
|
114
|
-
"@medicine-wheel/relational-index": "^0.17.
|
|
115
|
-
"@medicine-wheel/relational-query": "^0.17.
|
|
116
|
-
"@medicine-wheel/session-reader": "^0.17.
|
|
117
|
-
"@medicine-wheel/storage-provider": "^0.17.
|
|
118
|
-
"@medicine-wheel/transformation-tracker": "^0.17.
|
|
119
|
-
"@medicine-wheel/ui-components": "^0.17.
|
|
94
|
+
"@medicine-wheel/ceremonial-diary": "^0.17.8",
|
|
95
|
+
"@medicine-wheel/ceremony-protocol": "^0.17.8",
|
|
96
|
+
"@medicine-wheel/community-identity": "^0.17.8",
|
|
97
|
+
"@medicine-wheel/community-review": "^0.17.8",
|
|
98
|
+
"@medicine-wheel/consent-lifecycle": "^0.17.8",
|
|
99
|
+
"@medicine-wheel/creative-orientation": "^0.17.8",
|
|
100
|
+
"@medicine-wheel/data-store": "^0.17.8",
|
|
101
|
+
"@medicine-wheel/data-store-postgres": "^0.17.8",
|
|
102
|
+
"@medicine-wheel/fire-keeper": "^0.17.8",
|
|
103
|
+
"@medicine-wheel/github-ceremony": "^0.17.8",
|
|
104
|
+
"@medicine-wheel/graph-viz": "^0.17.8",
|
|
105
|
+
"@medicine-wheel/honcho": "^0.17.8",
|
|
106
|
+
"@medicine-wheel/importance-unit": "^0.17.8",
|
|
107
|
+
"@medicine-wheel/mcp": "^4.17.8",
|
|
108
|
+
"@medicine-wheel/memory": "^0.17.8",
|
|
109
|
+
"@medicine-wheel/narrative-cluster": "^0.17.8",
|
|
110
|
+
"@medicine-wheel/narrative-engine": "^0.17.8",
|
|
111
|
+
"@medicine-wheel/ontology-core": "^0.17.8",
|
|
112
|
+
"@medicine-wheel/perception-layer": "^0.17.8",
|
|
113
|
+
"@medicine-wheel/prompt-decomposition": "^0.17.8",
|
|
114
|
+
"@medicine-wheel/relational-index": "^0.17.8",
|
|
115
|
+
"@medicine-wheel/relational-query": "^0.17.8",
|
|
116
|
+
"@medicine-wheel/session-reader": "^0.17.8",
|
|
117
|
+
"@medicine-wheel/storage-provider": "^0.17.8",
|
|
118
|
+
"@medicine-wheel/transformation-tracker": "^0.17.8",
|
|
119
|
+
"@medicine-wheel/ui-components": "^0.17.8",
|
|
120
120
|
"@miadi/episode-vessel": "^0.2.1",
|
|
121
121
|
"@neondatabase/serverless": "^0.10.0",
|
|
122
122
|
"@xyflow/react": "^12.3.0",
|