sanity-plugin-graph-view 3.0.0-v3-studio.7 → 3.0.0

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2022 Sanity.io
3
+ Copyright (c) 2025 Sanity.io
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,16 +1,10 @@
1
- > **NOTE**
2
- >
3
- > This is the **Sanity Studio v3 version** of sanity-plugin-graph-view.
4
- >
5
- > For the v2 version, please refer to the [v2-branch](https://github.com/sanity-io/sanity-plugin-graph-view).
6
-
7
1
  <div align="center">
8
- <img src="assets/sanity-logo.png" width="177" alt="Sanity" />
9
2
  <h1>Graph View Plugin</h1>
10
3
  <p>A tool for Sanity Studio to graph your content and see changes in real-time.</p>
11
4
  <p><img src="assets/screengrab.gif" width="540" alt="Screengrab of the Graph tool" /></p>
12
5
  </div>
13
6
 
7
+
14
8
  Wonder how a visualization of your dataset will look? How many authors do you have? How many items have they worked on? And are currently working on! Edits and changes are shown in real-time!
15
9
 
16
10
  **Explore your data with this plugin, seek out strange corners and data types, boldly go where you could not before!**
@@ -18,13 +12,7 @@ Wonder how a visualization of your dataset will look? How many authors do you ha
18
12
  ## Installation
19
13
 
20
14
  ```
21
- npm install --save sanity-plugin-graph-view@studio-v3
22
- ```
23
-
24
- or
25
-
26
- ```
27
- yarn add sanity-plugin-graph-view@studio-v3
15
+ npm install --save sanity-plugin-graph-view
28
16
  ```
29
17
 
30
18
 
@@ -35,7 +23,7 @@ Add it as a plugin in sanity.config.ts (or .js):
35
23
  ```js
36
24
  import { contentGraphView } from "sanity-plugin-graph-view";
37
25
 
38
- export default defineConfigConfig({
26
+ export default defineConfig({
39
27
  // ...
40
28
  plugins: [
41
29
  contentGraphView({}),
@@ -90,27 +78,6 @@ contentGraphView({
90
78
  })
91
79
  ```
92
80
 
93
- ## Get help in the Sanity Community
94
-
95
- [![Slack Community Button](https://slack.sanity.io/badge.svg)](https://slack.sanity.io/)
96
-
97
- Join [Sanity’s developer community](https://slack.sanity.io) or ping us [on twitter](https://twitter.com/sanity_io).
98
-
99
81
  ## License
100
82
 
101
83
  MIT-licensed. See LICENSE.
102
-
103
- ## Develop & test
104
-
105
- This plugin uses [@sanity/plugin-kit](https://github.com/sanity-io/plugin-kit)
106
- with default configuration for build & watch scripts.
107
-
108
- See [Testing a plugin in Sanity Studio](https://github.com/sanity-io/plugin-kit#testing-a-plugin-in-sanity-studio)
109
- on how to run this plugin with hotreload in the studio.
110
-
111
- ### Release new version
112
-
113
- Run ["CI & Release" workflow](https://github.com/sanity-io/sanity-plugin-graph-view/actions/workflows/main.yml).
114
- Make sure to select the v3 branch and check "Release new version".
115
-
116
- Semantic release will only release on configured branches, so it is safe to run release on any branch.
package/dist/index.cjs ADDED
@@ -0,0 +1,359 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: !0 });
3
+ var jsxRuntime = require("react/jsx-runtime"), sanity = require("sanity"), router = require("sanity/router"), BezierEasing = require("bezier-easing"), deepEqual = require("deep-equal"), polished = require("polished"), react = require("react"), reactForceGraph = require("react-force-graph"), uuid = require("uuid"), color = require("@sanity/color"), ui = require("@sanity/ui"), styled = require("styled-components");
4
+ function _interopDefaultCompat(e) {
5
+ return e && typeof e == "object" && "default" in e ? e : { default: e };
6
+ }
7
+ var BezierEasing__default = /* @__PURE__ */ _interopDefaultCompat(BezierEasing), deepEqual__default = /* @__PURE__ */ _interopDefaultCompat(deepEqual), styled__default = /* @__PURE__ */ _interopDefaultCompat(styled);
8
+ const GraphRoot = styled__default.default.div`
9
+ font-family: ${({ theme }) => theme.fonts.text.family};
10
+ position: absolute;
11
+ top: 0;
12
+ left: 0;
13
+ width: 100%;
14
+ height: 100%;
15
+ background: ${color.black.hex};
16
+ `, GraphWrapper = styled__default.default.div`
17
+ position: relative;
18
+ width: 100%;
19
+ height: 100%;
20
+ `, HoverNode = styled__default.default.div`
21
+ font-family: ${({ theme }) => theme.fonts.text.family};
22
+ display: none;
23
+ position: absolute;
24
+ bottom: ${({ theme }) => theme.space[0]}px;
25
+ left: 50%;
26
+ transform: translate3d(-50%, 0, 0);
27
+ background: var(--component-bg);
28
+ border-radius: ${({ theme }) => theme.radius[2]}px;
29
+ padding: ${({ theme }) => theme.space[2]}px;
30
+ z-index: 1000;
31
+
32
+ &:empty {
33
+ display: none;
34
+ }
35
+ `, Legend = styled__default.default.div`
36
+ color: #ccc;
37
+ position: absolute;
38
+ top: ${({ theme }) => theme.space[4]}px;
39
+ left: ${({ theme }) => theme.space[4]}px;
40
+
41
+ & > div {
42
+ margin: 5px 0;
43
+ }
44
+ `, LegendRow = styled__default.default.div`
45
+ display: flex;
46
+ `, LegendBadge = styled__default.default.div`
47
+ width: 1.25em;
48
+ height: 1.25em;
49
+ background: currentColor;
50
+ border-radius: 50%;
51
+ margin-right: ${({ theme }) => theme.space[2]}px;
52
+ `;
53
+ function useListen(query, params, options, onUpdate, dependencies, client) {
54
+ react.useEffect(() => {
55
+ const subscription = client.listen(query, params, options).subscribe((update) => {
56
+ onUpdate(update);
57
+ });
58
+ return () => {
59
+ subscription.unsubscribe();
60
+ };
61
+ }, [...dependencies, client]);
62
+ }
63
+ function useFetchDocuments(query, onFetch, dependencies, client) {
64
+ react.useEffect(() => {
65
+ client.fetch(query).then((result) => {
66
+ onFetch(result);
67
+ });
68
+ }, [...dependencies, client]);
69
+ }
70
+ function sizeOf(value) {
71
+ return value === null ? 0 : typeof value == "object" ? Object.entries(value).reduce((total, [k, v]) => total + sizeOf(k) + sizeOf(v), 0) : Array.isArray(value) ? Object.entries(value).reduce((total, v) => total + sizeOf(v), 0) : typeof value == "string" ? value.length : 1;
72
+ }
73
+ function loadImage(url, w, h) {
74
+ return new Promise((resolve) => {
75
+ const img = new Image(w, h);
76
+ img.onload = () => {
77
+ resolve(img);
78
+ }, img.onerror = (event) => {
79
+ console.log("Image error", event), resolve(null);
80
+ }, img.src = url;
81
+ });
82
+ }
83
+ function sortBy(array, f) {
84
+ return array.sort((a, b) => {
85
+ const va = f(a), vb = f(b);
86
+ return va < vb ? -1 : va > vb ? 1 : 0;
87
+ });
88
+ }
89
+ function truncate(s, limit) {
90
+ return s.length > limit ? `${s.substring(0, limit)}\u2026` : s;
91
+ }
92
+ const DEFAULT_QUERY = `
93
+ *[
94
+ !(_id in path("_.*")) &&
95
+ !(_type match "system.*") &&
96
+ !(_type match "sanity.*")
97
+ ]
98
+ `, fadeEasing = BezierEasing__default.default(0, 0.9, 1, 1), softEasing = BezierEasing__default.default(0.25, 0.1, 0, 1), idleTimeout = 1e4, imageSize = 40;
99
+ function getTopDocTypes(counts) {
100
+ return sortBy(Object.keys(counts), (docType) => counts[docType] || 0).reverse().slice(0, 10);
101
+ }
102
+ function formatDocType(docType) {
103
+ return (docType.substring(0, 1).toUpperCase() + docType.substring(1)).replace(/\./g, " ").replace(/[A-Z]/g, " $&").trim();
104
+ }
105
+ function getDocTypeCounts(docs) {
106
+ const types = {};
107
+ for (const doc of docs)
108
+ types[doc._type] = (types[doc._type] || 0) + 1;
109
+ return types;
110
+ }
111
+ function labelFor(doc) {
112
+ return `${doc.title || doc.name || doc._id}`.trim();
113
+ }
114
+ function valueFor(doc, maxSize) {
115
+ return 5 + 100 * (sizeOf(doc) / maxSize);
116
+ }
117
+ function findRefs(obj, dest = []) {
118
+ if (obj !== null) {
119
+ if (typeof obj == "object")
120
+ for (const [k, v] of Object.entries(obj))
121
+ k === "_ref" && typeof v == "string" && v.length > 0 && dest.push(stripDraftId(v)), findRefs(v, dest);
122
+ else if (Array.isArray(obj))
123
+ for (const v of obj)
124
+ findRefs(v, dest);
125
+ }
126
+ return dest;
127
+ }
128
+ function stripDraftId(id) {
129
+ return id.replace(/^drafts\./, "");
130
+ }
131
+ function deduplicateDrafts(docs) {
132
+ const deduped = {};
133
+ for (const doc of docs)
134
+ doc._id.startsWith("drafts.") || (deduped[doc._id] = doc);
135
+ for (const doc of docs)
136
+ if (doc._id.startsWith("drafts.")) {
137
+ const id = stripDraftId(doc._id);
138
+ deduped[id] = Object.assign(doc, { _id: id });
139
+ }
140
+ return Object.values(deduped);
141
+ }
142
+ class Users {
143
+ _users = [];
144
+ async getById(id, client) {
145
+ let user = this._users.find((u) => u._id === id);
146
+ return user || (user = await client.users.getById(id), this._users.push(user), user.image = await loadImage(
147
+ user.imageUrl || "https://raw.githubusercontent.com/sanity-io/sanity-plugin-graph-view/main/assets/head-silhouette.jpg",
148
+ imageSize,
149
+ imageSize
150
+ )), user;
151
+ }
152
+ }
153
+ class Session {
154
+ id = null;
155
+ user = null;
156
+ doc = null;
157
+ lastActive = 0;
158
+ startTime = 0;
159
+ angle = 0;
160
+ }
161
+ class GraphData {
162
+ sessions = [];
163
+ data;
164
+ constructor(docs = []) {
165
+ const docsById = {};
166
+ for (const doc of docs)
167
+ docsById[doc._id] = doc;
168
+ this.data = {
169
+ nodes: docs.map((d) => Object.assign({ id: d._id, type: "document", doc: d })),
170
+ links: docs.flatMap((doc) => findRefs(doc).map((ref) => ({ source: doc._id, target: ref }))).filter((link) => docsById[link.source] && docsById[link.target])
171
+ };
172
+ }
173
+ setSession(user, docNode) {
174
+ let session = this.sessions.find((s) => s.user.id === user.id && s.doc?._id === docNode.doc._id);
175
+ session || (session = new Session(), session.id = uuid.v4(), session.user = user, session.startTime = Date.now(), session.doc = docNode.doc, session.angle = Math.random() * 2 * Math.PI, this.sessions.push(session)), session.lastActive = Date.now();
176
+ }
177
+ reapSessions() {
178
+ for (let i = 0; i < this.sessions.length; i++) {
179
+ const session = this.sessions[i];
180
+ Date.now() - session.lastActive > idleTimeout && (this.sessions = [...this.sessions.slice(0, i), ...this.sessions.slice(i + 1)], i--);
181
+ }
182
+ }
183
+ clone() {
184
+ const copy = new GraphData();
185
+ return Object.assign(copy, this), copy.data = {
186
+ nodes: [...this.data.nodes],
187
+ links: [...this.data.links]
188
+ }, copy;
189
+ }
190
+ }
191
+ const users = new Users();
192
+ function GraphView(props) {
193
+ const query = props.query || DEFAULT_QUERY, apiVersion = props.apiVersion ?? "2022-09-01", userColorManager = sanity.useUserColorManager(), [maxSize, setMaxSize] = react.useState(0), [hoverNode, setHoverNode] = react.useState(null), [documents, setDocuments] = react.useState([]), [docTypes, setDocTypes] = react.useState({}), [graph, setGraph] = react.useState(() => new GraphData()), router$1 = router.useRouter(), client = sanity.useClient({ apiVersion }), fetchCallback = react.useCallback((_docs) => {
194
+ const docs = deduplicateDrafts(_docs);
195
+ setMaxSize(Math.max(...docs.map(sizeOf))), setDocuments(docs), setDocTypes(getDocTypeCounts(docs)), setGraph(new GraphData(docs));
196
+ }, []), listenCallback = react.useCallback(
197
+ async (update) => {
198
+ const doc = update.result;
199
+ if (doc) {
200
+ doc._id = stripDraftId(doc._id);
201
+ const docsById = {};
202
+ for (const d of documents)
203
+ docsById[d._id] = d;
204
+ let oldDoc;
205
+ const docs = [...documents], idx = documents.findIndex((d) => d._id === doc._id);
206
+ idx >= 0 ? (oldDoc = docs[idx], docs[idx] = doc) : docs.push(doc), setDocuments(docs), setDocTypes(getDocTypeCounts(docs)), setMaxSize(Math.max(...docs.map(sizeOf)));
207
+ const newGraph = graph.clone(), oldRefs = findRefs(oldDoc || {}).filter(
208
+ (id) => id === doc._id || docsById[id] !== null
209
+ ), newRefs = findRefs(doc).filter((id) => id === doc._id || docsById[id] !== null);
210
+ let graphChanged = !deepEqual__default.default(oldRefs, newRefs);
211
+ graphChanged && (newGraph.data.links = newGraph.data.links.filter((l) => l.source.id !== doc._id).concat(newRefs.map((ref) => ({ source: doc._id, target: ref }))));
212
+ let docNode;
213
+ const nodeIdx = graph.data.nodes.findIndex((n) => n.doc && n.doc._id === doc._id);
214
+ nodeIdx >= 0 ? (docNode = graph.data.nodes[nodeIdx], docNode.doc = doc) : (docNode = { id: doc._id, type: "document", doc }, newGraph.data.nodes.push(docNode), graphChanged = !0), graphChanged && setGraph(newGraph);
215
+ const user = await users.getById(update.identity, client);
216
+ graph.setSession(user, docNode);
217
+ } else if (update.transition === "disappear") {
218
+ const docId = stripDraftId(update.documentId), docs = documents.filter((d) => d._id !== docId);
219
+ setDocuments(docs), setDocTypes(getDocTypeCounts(docs)), setMaxSize(Math.max(...docs.map(sizeOf)));
220
+ const newGraph = graph.clone();
221
+ newGraph.data.links = newGraph.data.links.filter(
222
+ (l) => l.source.id !== docId && l.target.id !== docId
223
+ ), newGraph.data.nodes = newGraph.data.nodes.filter((n) => n.id !== docId), setGraph(newGraph);
224
+ }
225
+ },
226
+ [documents, graph, client]
227
+ );
228
+ useFetchDocuments(query, fetchCallback, [], client), useListen(query, {}, {}, listenCallback, [documents, graph], client), react.useEffect(() => {
229
+ const interval = setInterval(() => graph.reapSessions(), 1e3);
230
+ return () => clearInterval(interval);
231
+ }, [graph]);
232
+ const theme = ui.useTheme().sanity;
233
+ return /* @__PURE__ */ jsxRuntime.jsx(GraphWrapper, { theme, children: /* @__PURE__ */ jsxRuntime.jsxs(GraphRoot, { theme, children: [
234
+ /* @__PURE__ */ jsxRuntime.jsx(Legend, { theme, children: getTopDocTypes(docTypes).map((docType) => /* @__PURE__ */ jsxRuntime.jsxs(
235
+ LegendRow,
236
+ {
237
+ className: "legend__row",
238
+ style: { color: getDocTypeColor(docType).fill },
239
+ children: [
240
+ /* @__PURE__ */ jsxRuntime.jsx(LegendBadge, { theme }),
241
+ /* @__PURE__ */ jsxRuntime.jsx("div", { children: formatDocType(docType) })
242
+ ]
243
+ },
244
+ docType
245
+ )) }),
246
+ hoverNode && /* @__PURE__ */ jsxRuntime.jsx(HoverNode, { theme, children: labelFor(hoverNode.doc) }),
247
+ /* @__PURE__ */ jsxRuntime.jsx(
248
+ reactForceGraph.ForceGraph2D,
249
+ {
250
+ graphData: graph.data,
251
+ nodeAutoColorBy: "group",
252
+ enableNodeDrag: !1,
253
+ onNodeHover: (node) => setHoverNode(node),
254
+ onNodeClick: (node) => {
255
+ router$1.navigateIntent("edit", { id: node.doc._id, documentType: node.doc._type });
256
+ },
257
+ linkColor: () => polished.rgba(color.gray[500].hex, 0.25),
258
+ nodeLabel: () => "",
259
+ nodeRelSize: 1,
260
+ nodeVal: (node) => valueFor(node.doc, maxSize),
261
+ onRenderFramePost: (ctx, globalScale) => {
262
+ for (const session of graph.sessions) {
263
+ const node = graph.data.nodes.find(
264
+ (n) => n.doc && n.doc._id === session?.doc?._id
265
+ );
266
+ if (node) {
267
+ const idleFactorRange = idleTimeout, angle = session.angle, radius = Math.sqrt(valueFor(node.doc, maxSize)), image = session.user.image, userColor = userColorManager.get(session.user.displayName).tints[400].hex, distance = radius * globalScale + 40, imgW = image ? image.width : 0, imgH = image ? image.height : 0, x = node.x + Math.sin(angle) * distance / globalScale, y = node.y + Math.cos(angle) * distance / globalScale;
268
+ ctx.save();
269
+ try {
270
+ if (ctx.globalAlpha = fadeEasing(
271
+ 1 - Math.min(idleFactorRange, Date.now() - session.lastActive) / idleFactorRange
272
+ ), ctx.font = `bold ${Math.round(12 / globalScale)}px sans-serif`, ctx.beginPath(), ctx.strokeStyle = polished.rgba(color.white.hex, 1), ctx.lineWidth = 2 / globalScale, ctx.moveTo(
273
+ node.x + Math.sin(angle) * (distance - imgW / 2) / globalScale,
274
+ node.y + Math.cos(angle) * (distance - imgH / 2) / globalScale
275
+ ), ctx.lineTo(node.x + Math.sin(angle) * radius, node.y + Math.cos(angle) * radius), ctx.stroke(), ctx.beginPath(), ctx.strokeStyle = polished.rgba(color.white.hex, 1), ctx.lineWidth = 2 / globalScale, ctx.arc(node.x, node.y, radius, 0, 2 * Math.PI, !1), ctx.stroke(), image) {
276
+ ctx.save();
277
+ try {
278
+ const f = softEasing(
279
+ Math.max(0, (700 - (Date.now() - session.startTime)) / 700)
280
+ );
281
+ f > 0 && (ctx.beginPath(), ctx.fillStyle = polished.rgba(userColor, f), ctx.arc(x, y, (imgW / 2 + 10) / globalScale, 0, 2 * Math.PI, !1), ctx.fill()), ctx.beginPath(), ctx.fillStyle = polished.rgba(color.white.hex, 1), ctx.arc(x, y, imgW / globalScale / 2, 0, 2 * Math.PI, !1), ctx.clip(), ctx.drawImage(
282
+ image,
283
+ x - imgW / globalScale / 2,
284
+ y - imgH / globalScale / 2,
285
+ imgW / globalScale,
286
+ imgH / globalScale
287
+ ), ctx.strokeStyle = color.black.hex, ctx.lineWidth = 6 / globalScale, ctx.stroke(), ctx.strokeStyle = userColor, ctx.lineWidth = 4 / globalScale, ctx.stroke();
288
+ } finally {
289
+ ctx.restore();
290
+ }
291
+ }
292
+ ctx.beginPath(), ctx.strokeStyle = polished.rgba(color.black.hex, 1), ctx.lineWidth = 0.5 / globalScale, ctx.arc(x, y, imgW / globalScale / 2, 0, 2 * Math.PI, !1), ctx.stroke();
293
+ const above = angle >= Math.PI / 2 && angle < Math.PI * 1.5, textY = above ? y - (imgH / 2 + 5) / globalScale : y + (imgH / 2 + 5) / globalScale;
294
+ ctx.fillStyle = polished.rgba(color.white.hex, 1), ctx.textAlign = "center", ctx.textBaseline = above ? "bottom" : "top", ctx.fillText(session.user.displayName, x, textY);
295
+ } finally {
296
+ ctx.restore();
297
+ }
298
+ }
299
+ }
300
+ },
301
+ nodeCanvasObject: (node, ctx, globalScale) => {
302
+ switch (node.type) {
303
+ case "document": {
304
+ const nodeColor = getDocTypeColor(node.doc._type), radius = Math.sqrt(valueFor(node.doc, maxSize)), fontSize = Math.min(100, 10 / globalScale);
305
+ if (ctx.beginPath(), ctx.fillStyle = hoverNode !== null && node.doc._id === hoverNode.doc._id ? polished.rgba(color.gray[500].hex, 0.8) : nodeColor.fill, ctx.strokeStyle = nodeColor.border, ctx.lineWidth = 0.5, ctx.arc(node.x, node.y, radius, 0, 2 * Math.PI, !1), ctx.stroke(), ctx.fill(), radius * globalScale > 10) {
306
+ ctx.font = `${fontSize}px sans-serif`;
307
+ const w = radius * 2 + 30 / globalScale;
308
+ for (let len = 50; len >= 5; len /= 1.2) {
309
+ const label = truncate(labelFor(node.doc), Math.round(len));
310
+ if (ctx.measureText(label).width < w) {
311
+ ctx.textAlign = "center", ctx.textBaseline = "top", ctx.strokeStyle = polished.rgba(color.black.hex, 0.5), ctx.lineWidth = 2 / globalScale, ctx.strokeText(label, node.x, node.y + radius + 5 / globalScale), ctx.fillText(label, node.x, node.y + radius + 5 / globalScale);
312
+ break;
313
+ }
314
+ }
315
+ }
316
+ }
317
+ }
318
+ },
319
+ linkCanvasObject: (link, ctx, globalScale) => {
320
+ ctx.beginPath(), ctx.strokeStyle = polished.rgba(color.gray[500].hex, 0.125), ctx.lineWidth = 2 / globalScale, ctx.moveTo(link.source.x, link.source.y), ctx.lineTo(link.target.x, link.target.y), ctx.stroke();
321
+ }
322
+ }
323
+ )
324
+ ] }) });
325
+ }
326
+ const colorCache = {};
327
+ let typeColorNum = 0;
328
+ function getDocTypeColor(docType) {
329
+ if (colorCache[docType])
330
+ return colorCache[docType];
331
+ const hue = color.COLOR_HUES[typeColorNum % color.COLOR_HUES.length];
332
+ return typeColorNum += 1, colorCache[docType] = {
333
+ fill: color.hues[hue][400].hex,
334
+ border: polished.rgba(color.black.hex, 0.5)
335
+ }, colorCache[docType];
336
+ }
337
+ const GraphViewIcon = () => /* @__PURE__ */ jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "1em", height: "1em", viewBox: "0 0 46.063 46.063", children: /* @__PURE__ */ jsxRuntime.jsx(
338
+ "path",
339
+ {
340
+ fill: "currentColor",
341
+ d: "M18.022 38.676v2.813h-1.21q-4.864 0-6.525-1.447-1.64-1.445-1.64-5.762v-4.666q0-2.95-1.055-4.083-1.055-1.13-3.828-1.13h-1.19v-2.794h1.19q2.793 0 3.828-1.114 1.055-1.132 1.055-4.043V11.76q0-4.316 1.64-5.742 1.66-1.446 6.524-1.446h1.212v2.793h-1.328q-2.754 0-3.594.86-.84.86-.84 3.613v4.844q0 3.066-.898 4.453-.88 1.387-3.028 1.875 2.168.527 3.047 1.914.88 1.387.88 4.434v4.843q0 2.754.84 3.614.84.86 3.594.86h1.328zM28.04 38.676h1.368q2.735 0 3.555-.84.84-.84.84-3.633V29.36q0-3.047.88-4.434.878-1.387 3.046-1.914-2.17-.488-3.048-1.875-.88-1.387-.88-4.453V11.84q0-2.773-.84-3.613-.82-.86-3.554-.86H28.04V4.574h1.232q4.863 0 6.484 1.446 1.64 1.426 1.64 5.742v4.687q0 2.91 1.055 4.042 1.056 1.114 3.83 1.114h1.21V24.4h-1.21q-2.774 0-3.83 1.13-1.053 1.134-1.053 4.084v4.667q0 4.318-1.64 5.763-1.622 1.446-6.485 1.446h-1.23v-2.814z"
342
+ }
343
+ ) }), contentGraphView = sanity.definePlugin((config = {}) => ({
344
+ name: "@sanity/content-graph-view",
345
+ tools: (prev) => [
346
+ ...prev,
347
+ {
348
+ name: "graph-your-content",
349
+ title: "Graph",
350
+ icon: GraphViewIcon,
351
+ component: function() {
352
+ return /* @__PURE__ */ jsxRuntime.jsx(GraphView, { ...config });
353
+ },
354
+ router: router.route.create("/:selectedDocumentId")
355
+ }
356
+ ]
357
+ }));
358
+ exports.contentGraphView = contentGraphView;
359
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../src/tool/GraphViewStyle.tsx","../src/tool/hooks.ts","../src/tool/utils.ts","../src/tool/GraphView.tsx","../src/tool/GraphViewIcon.tsx","../src/plugin.tsx"],"sourcesContent":["import React, {type PropsWithChildren} from 'react'\nimport styled from 'styled-components'\n\nimport type {Theme} from '@sanity/ui'\n\nimport {black} from '@sanity/color'\n\ntype Style = PropsWithChildren<{theme: SanityTheme}>\ntype SanityTheme = Theme['sanity']\n\nexport const GraphRoot: React.FC<Style> = styled.div`\n font-family: ${({theme}: Style) => theme.fonts.text.family};\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: ${black.hex};\n`\n\nexport const GraphWrapper: React.FC<Style> = styled.div`\n position: relative;\n width: 100%;\n height: 100%;\n` as React.FC<Style>\n\nexport const HoverNode: React.FC<Style> = styled.div`\n font-family: ${({theme}: Style) => theme.fonts.text.family};\n display: none;\n position: absolute;\n bottom: ${({theme}: Style) => theme.space[0]}px;\n left: 50%;\n transform: translate3d(-50%, 0, 0);\n background: var(--component-bg);\n border-radius: ${({theme}: Style) => theme.radius[2]}px;\n padding: ${({theme}: Style) => theme.space[2]}px;\n z-index: 1000;\n\n &:empty {\n display: none;\n }\n`\n\nexport const Legend: React.FC<Style> = styled.div`\n color: #ccc;\n position: absolute;\n top: ${({theme}: Style) => theme.space[4]}px;\n left: ${({theme}: Style) => theme.space[4]}px;\n\n & > div {\n margin: 5px 0;\n }\n`\n\nexport const LegendRow = styled.div`\n display: flex;\n`\n\nexport const LegendBadge: React.FC<Style> = styled.div`\n width: 1.25em;\n height: 1.25em;\n background: currentColor;\n border-radius: 50%;\n margin-right: ${({theme}: Style) => theme.space[2]}px;\n`\n","import {useEffect} from 'react'\n\nimport type {ListenEvent, ListenOptions, SanityClient} from '@sanity/client'\n\nexport function useListen(\n query: string,\n params: {[key: string]: any},\n options: ListenOptions,\n onUpdate: (event: ListenEvent<any>) => void,\n dependencies: unknown[],\n client: SanityClient,\n): void {\n useEffect(() => {\n const subscription = client.listen(query, params, options).subscribe((update) => {\n onUpdate(update)\n })\n return () => {\n subscription.unsubscribe()\n }\n }, [...dependencies, client])\n}\n\nexport function useFetchDocuments(\n query: string,\n onFetch: (event: ListenEvent<any>) => void,\n dependencies: unknown[],\n client: SanityClient,\n): void {\n useEffect(() => {\n void client.fetch(query).then((result) => {\n onFetch(result)\n })\n }, [...dependencies, client])\n}\n","export function sizeOf(value: any): number {\n if (value === null) {\n return 0\n }\n\n if (typeof value === 'object') {\n return Object.entries(value).reduce((total, [k, v]) => total + sizeOf(k) + sizeOf(v), 0)\n }\n\n if (Array.isArray(value)) {\n return Object.entries(value).reduce((total, v) => total + sizeOf(v), 0)\n }\n\n if (typeof value === 'string') {\n return value.length\n }\n\n return 1\n}\n\nexport function loadImage(url: string, w: number, h: number): Promise<HTMLImageElement | null> {\n return new Promise((resolve) => {\n const img = new Image(w, h)\n img.onload = () => {\n resolve(img)\n }\n img.onerror = (event) => {\n console.log('Image error', event)\n resolve(null)\n }\n img.src = url\n })\n}\n\nexport function sortBy<T>(array: T[], f: (t: T) => number): T[] {\n return array.sort((a, b) => {\n const va = f(a)\n const vb = f(b)\n return va < vb ? -1 : va > vb ? 1 : 0\n })\n}\n\nexport function truncate(s: string, limit: number): string {\n if (s.length > limit) {\n return `${s.substring(0, limit)}…`\n }\n return s\n}\n","import BezierEasing from 'bezier-easing'\nimport deepEqual from 'deep-equal'\nimport {rgba} from 'polished'\nimport React, {useCallback, useEffect, useState} from 'react'\nimport {ForceGraph2D} from 'react-force-graph'\nimport {useClient, useUserColorManager} from 'sanity'\nimport {useRouter} from 'sanity/router'\nimport {v4 as uuidv4} from 'uuid'\n\nimport type {SanityDocument, SanityClient} from '@sanity/client'\n\nimport {black, COLOR_HUES, gray, white, hues} from '@sanity/color'\nimport {useTheme} from '@sanity/ui'\n\nimport {GraphRoot, GraphWrapper, HoverNode, Legend, LegendBadge, LegendRow} from './GraphViewStyle'\nimport {useFetchDocuments, useListen} from './hooks'\nimport {sortBy, loadImage, sizeOf, truncate} from './utils'\n\nconst DEFAULT_QUERY = `\n *[\n !(_id in path(\"_.*\")) &&\n !(_type match \"system.*\") &&\n !(_type match \"sanity.*\")\n ]\n`\n\nconst fadeEasing = BezierEasing(0, 0.9, 1, 1)\nconst softEasing = BezierEasing(0.25, 0.1, 0.0, 1.0)\nconst idleTimeout = 10000\nconst imageSize = 40\n\nfunction getTopDocTypes(counts: Record<string, number>) {\n return sortBy(Object.keys(counts), (docType) => counts[docType] || 0)\n .reverse()\n .slice(0, 10)\n}\n\nfunction formatDocType(docType: string) {\n return (docType.substring(0, 1).toUpperCase() + docType.substring(1))\n .replace(/\\./g, ' ')\n .replace(/[A-Z]/g, ' $&')\n .trim()\n}\n\nfunction getDocTypeCounts(docs: SanityDocument[]) {\n const types: Record<string, number> = {}\n for (const doc of docs) {\n types[doc._type] = (types[doc._type] || 0) + 1\n }\n return types\n}\n\nfunction labelFor(doc: SanityDocument) {\n return `${doc.title || doc.name || doc._id}`.trim()\n}\n\nfunction valueFor(doc: any, maxSize: number) {\n return 5 + 100 * (sizeOf(doc) / maxSize)\n}\n\nfunction findRefs(obj: any, dest: any[] = []) {\n if (obj !== null) {\n if (typeof obj === 'object') {\n for (const [k, v] of Object.entries(obj)) {\n if (k === '_ref' && typeof v === 'string' && v.length > 0) {\n dest.push(stripDraftId(v))\n }\n findRefs(v, dest)\n }\n } else if (Array.isArray(obj)) {\n for (const v of obj) {\n findRefs(v, dest)\n }\n }\n }\n return dest\n}\n\nfunction stripDraftId(id: string) {\n return id.replace(/^drafts\\./, '')\n}\n\nfunction deduplicateDrafts(docs: SanityDocument[]) {\n const deduped: Record<string, SanityDocument> = {}\n for (const doc of docs) {\n if (!doc._id.startsWith('drafts.')) {\n deduped[doc._id] = doc\n }\n }\n for (const doc of docs) {\n if (doc._id.startsWith('drafts.')) {\n const id = stripDraftId(doc._id)\n deduped[id] = Object.assign(doc, {_id: id})\n }\n }\n return Object.values(deduped)\n}\n\nclass Users {\n _users: any[] = []\n\n async getById(id: string, client: SanityClient) {\n let user = this._users.find((u) => u._id === id)\n if (!user) {\n user = await client.users.getById(id)\n this._users.push(user)\n user.image = await loadImage(\n user.imageUrl ||\n 'https://raw.githubusercontent.com/sanity-io/sanity-plugin-graph-view/main/assets/head-silhouette.jpg',\n imageSize,\n imageSize,\n )\n }\n return user\n }\n}\n\nclass Session {\n id: string | null = null\n user: any = null\n doc: SanityDocument | null = null\n lastActive: number = 0\n startTime: number = 0\n angle: number = 0\n}\n\nclass GraphData {\n sessions: Session[] = []\n data: any\n\n constructor(docs: SanityDocument[] = []) {\n const docsById: Record<string, SanityDocument> = {}\n for (const doc of docs) {\n docsById[doc._id] = doc\n }\n\n this.data = {\n nodes: docs.map((d) => Object.assign({id: d._id, type: 'document', doc: d})),\n links: docs\n .flatMap((doc) => findRefs(doc).map((ref) => ({source: doc._id, target: ref})))\n .filter((link) => docsById[link.source] && docsById[link.target]),\n }\n }\n\n setSession(user: any, docNode: any) {\n let session = this.sessions.find((s) => s.user.id === user.id && s.doc?._id === docNode.doc._id)\n if (!session) {\n session = new Session()\n session.id = uuidv4()\n session.user = user\n session.startTime = Date.now()\n session.doc = docNode.doc\n session.angle = Math.random() * 2 * Math.PI\n this.sessions.push(session)\n }\n session.lastActive = Date.now()\n }\n\n reapSessions() {\n for (let i = 0; i < this.sessions.length; i++) {\n const session = this.sessions[i]\n if (Date.now() - session.lastActive > idleTimeout) {\n this.sessions = [...this.sessions.slice(0, i), ...this.sessions.slice(i + 1)]\n i--\n }\n }\n }\n\n clone() {\n const copy = new GraphData()\n Object.assign(copy, this)\n copy.data = {\n nodes: [...this.data.nodes],\n links: [...this.data.links],\n }\n return copy\n }\n}\n\nconst users = new Users()\n\ninterface GraphViewConfig {\n query?: string\n /** default: '2022-09-01' */\n apiVersion?: string\n}\n\nexport function GraphView(props: GraphViewConfig) {\n const query = props.query || DEFAULT_QUERY\n const apiVersion = props.apiVersion ?? '2022-09-01'\n\n const userColorManager = useUserColorManager()\n const [maxSize, setMaxSize] = useState(0)\n const [hoverNode, setHoverNode] = useState<any>(null)\n const [documents, setDocuments] = useState<SanityDocument[]>([])\n const [docTypes, setDocTypes] = useState<Record<string, number>>({})\n const [graph, setGraph] = useState(() => new GraphData())\n const router = useRouter()\n const client = useClient({apiVersion})\n\n const fetchCallback = useCallback((_docs: any) => {\n const docs = deduplicateDrafts(_docs)\n setMaxSize(Math.max(...docs.map(sizeOf)))\n setDocuments(docs)\n setDocTypes(getDocTypeCounts(docs))\n setGraph(new GraphData(docs))\n }, [])\n\n const listenCallback = useCallback(\n async (update: any) => {\n const doc = update.result\n if (doc) {\n doc._id = stripDraftId(doc._id)\n\n const docsById: Record<string, SanityDocument> = {}\n for (const d of documents) {\n docsById[d._id] = d\n }\n\n let oldDoc\n const docs = [...documents]\n const idx = documents.findIndex((d) => d._id === doc._id)\n if (idx >= 0) {\n oldDoc = docs[idx]\n docs[idx] = doc\n } else {\n docs.push(doc)\n }\n setDocuments(docs)\n setDocTypes(getDocTypeCounts(docs))\n setMaxSize(Math.max(...docs.map(sizeOf)))\n\n const newGraph = graph.clone()\n\n const oldRefs = findRefs(oldDoc || {}).filter(\n (id) => id === doc._id || docsById[id] !== null,\n )\n const newRefs = findRefs(doc).filter((id) => id === doc._id || docsById[id] !== null)\n\n let graphChanged = !deepEqual(oldRefs, newRefs)\n if (graphChanged) {\n newGraph.data.links = newGraph.data.links\n .filter((l: any) => l.source.id !== doc._id)\n .concat(newRefs.map((ref) => ({source: doc._id, target: ref})))\n }\n\n let docNode\n const nodeIdx = graph.data.nodes.findIndex((n: any) => n.doc && n.doc._id === doc._id)\n if (nodeIdx >= 0) {\n docNode = graph.data.nodes[nodeIdx]\n docNode.doc = doc\n } else {\n docNode = {id: doc._id, type: 'document', doc: doc}\n newGraph.data.nodes.push(docNode)\n graphChanged = true\n }\n if (graphChanged) {\n setGraph(newGraph)\n }\n\n // @ts-expect-error - TODO: fix this\n const user = await users.getById(update.identity, client)\n graph.setSession(user, docNode)\n } else if (update.transition === 'disappear') {\n const docId = stripDraftId(update.documentId)\n\n const docs = documents.filter((d) => d._id !== docId)\n setDocuments(docs)\n setDocTypes(getDocTypeCounts(docs))\n setMaxSize(Math.max(...docs.map(sizeOf)))\n\n const newGraph = graph.clone()\n newGraph.data.links = newGraph.data.links.filter(\n (l: any) => l.source.id !== docId && l.target.id !== docId,\n )\n newGraph.data.nodes = newGraph.data.nodes.filter((n: any) => n.id !== docId)\n setGraph(newGraph)\n }\n },\n [documents, graph, client],\n )\n // @ts-expect-error - TODO: fix this\n useFetchDocuments(query, fetchCallback, [], client)\n // @ts-expect-error - TODO: fix this\n useListen(query, {}, {}, listenCallback, [documents, graph], client)\n useEffect(() => {\n const interval = setInterval(() => graph.reapSessions(), 1000)\n return () => clearInterval(interval)\n }, [graph])\n\n const theme = useTheme().sanity\n\n return (\n <GraphWrapper theme={theme}>\n <GraphRoot theme={theme}>\n <Legend theme={theme}>\n {getTopDocTypes(docTypes).map((docType) => (\n <LegendRow\n className={'legend__row'}\n key={docType}\n style={{color: getDocTypeColor(docType).fill}}\n >\n <LegendBadge theme={theme} />\n <div>{formatDocType(docType)}</div>\n </LegendRow>\n ))}\n </Legend>\n {hoverNode && <HoverNode theme={theme}>{labelFor(hoverNode.doc)}</HoverNode>}\n\n {/* @ts-expect-error - TODO: fix this */}\n <ForceGraph2D\n graphData={graph.data}\n nodeAutoColorBy=\"group\"\n enableNodeDrag={false}\n onNodeHover={(node: any) => setHoverNode(node)}\n onNodeClick={(node: any) => {\n router.navigateIntent('edit', {id: node.doc._id, documentType: node.doc._type})\n }}\n linkColor={() => rgba(gray[500].hex, 0.25)}\n nodeLabel={() => ''}\n nodeRelSize={1}\n nodeVal={(node: any) => valueFor(node.doc, maxSize)}\n onRenderFramePost={(ctx, globalScale) => {\n for (const session of graph.sessions) {\n const node = graph.data.nodes.find(\n (n: any) => n.doc && n.doc._id === session?.doc?._id,\n )\n if (node) {\n const idleFactorRange = idleTimeout\n const angle = session.angle\n const radius = Math.sqrt(valueFor(node.doc, maxSize))\n const image = session.user.image\n const userColor = userColorManager.get(session.user.displayName).tints[400].hex\n const distance = radius * globalScale + 40\n const imgW = image ? image.width : 0\n const imgH = image ? image.height : 0\n const x = node.x + (Math.sin(angle) * distance) / globalScale\n const y = node.y + (Math.cos(angle) * distance) / globalScale\n\n ctx.save()\n try {\n ctx.globalAlpha = fadeEasing(\n 1 -\n Math.min(idleFactorRange, Date.now() - session.lastActive) / idleFactorRange,\n )\n ctx.font = `bold ${Math.round(12 / globalScale)}px sans-serif`\n\n ctx.beginPath()\n ctx.strokeStyle = rgba(white.hex, 1.0)\n ctx.lineWidth = 2 / globalScale\n ctx.moveTo(\n node.x + (Math.sin(angle) * (distance - imgW / 2)) / globalScale,\n node.y + (Math.cos(angle) * (distance - imgH / 2)) / globalScale,\n )\n ctx.lineTo(node.x + Math.sin(angle) * radius, node.y + Math.cos(angle) * radius)\n ctx.stroke()\n\n ctx.beginPath()\n ctx.strokeStyle = rgba(white.hex, 1.0)\n ctx.lineWidth = 2 / globalScale\n ctx.arc(node.x, node.y, radius, 0, 2 * Math.PI, false)\n ctx.stroke()\n\n if (image) {\n ctx.save()\n try {\n const dur = 700\n const f = softEasing(\n Math.max(0, (dur - (Date.now() - session.startTime)) / dur),\n )\n if (f > 0) {\n ctx.beginPath()\n ctx.fillStyle = rgba(userColor, f)\n ctx.arc(x, y, (imgW / 2 + 10) / globalScale, 0, 2 * Math.PI, false)\n ctx.fill()\n }\n\n ctx.beginPath()\n ctx.fillStyle = rgba(white.hex, 1.0)\n ctx.arc(x, y, imgW / globalScale / 2, 0, 2 * Math.PI, false)\n ctx.clip()\n\n ctx.drawImage(\n image,\n x - imgW / globalScale / 2,\n y - imgH / globalScale / 2,\n imgW / globalScale,\n imgH / globalScale,\n )\n\n ctx.strokeStyle = black.hex\n ctx.lineWidth = 6 / globalScale\n ctx.stroke()\n\n ctx.strokeStyle = userColor\n ctx.lineWidth = 4 / globalScale\n ctx.stroke()\n } finally {\n ctx.restore()\n }\n }\n\n ctx.beginPath()\n ctx.strokeStyle = rgba(black.hex, 1)\n ctx.lineWidth = 0.5 / globalScale\n ctx.arc(x, y, imgW / globalScale / 2, 0, 2 * Math.PI, false)\n ctx.stroke()\n\n const above = angle >= Math.PI / 2 && angle < Math.PI * 1.5\n const textY = above\n ? y - (imgH / 2 + 5) / globalScale\n : y + (imgH / 2 + 5) / globalScale\n ctx.fillStyle = rgba(white.hex, 1.0)\n ctx.textAlign = 'center'\n ctx.textBaseline = above ? 'bottom' : 'top'\n ctx.fillText(session.user.displayName, x, textY)\n } finally {\n ctx.restore()\n }\n }\n }\n }}\n nodeCanvasObject={(node: any, ctx, globalScale) => {\n switch (node.type) {\n case 'document': {\n const nodeColor = getDocTypeColor(node.doc._type)\n const radius = Math.sqrt(valueFor(node.doc, maxSize))\n const fontSize = Math.min(100, 10.0 / globalScale)\n\n ctx.beginPath()\n ctx.fillStyle =\n hoverNode !== null && node.doc._id === hoverNode.doc._id\n ? rgba(gray[500].hex, 0.8)\n : nodeColor.fill\n ctx.strokeStyle = nodeColor.border\n ctx.lineWidth = 0.5\n ctx.arc(node.x, node.y, radius, 0, 2 * Math.PI, false)\n ctx.stroke()\n ctx.fill()\n\n if (radius * globalScale > 10) {\n ctx.font = `${fontSize}px sans-serif`\n const w = radius * 2 + 30 / globalScale\n for (let len = 50; len >= 5; len /= 1.2) {\n const label = truncate(labelFor(node.doc), Math.round(len))\n const textMetrics = ctx.measureText(label)\n if (textMetrics.width < w) {\n // ctx.fillStyle = rgba(color.white.hex, 1.0)\n ctx.textAlign = 'center'\n ctx.textBaseline = 'top'\n\n ctx.strokeStyle = rgba(black.hex, 0.5)\n ctx.lineWidth = 2 / globalScale\n ctx.strokeText(label, node.x, node.y + radius + 5 / globalScale)\n\n ctx.fillText(label, node.x, node.y + radius + 5 / globalScale)\n break\n }\n }\n }\n }\n }\n }}\n linkCanvasObject={(link: any, ctx, globalScale) => {\n ctx.beginPath()\n ctx.strokeStyle = rgba(gray[500].hex, 0.125)\n ctx.lineWidth = 2 / globalScale\n ctx.moveTo(link.source.x, link.source.y)\n ctx.lineTo(link.target.x, link.target.y)\n ctx.stroke()\n }}\n />\n </GraphRoot>\n </GraphWrapper>\n )\n}\n\nconst colorCache: Record<string, {fill: string; border: string}> = {}\nlet typeColorNum = 0\n\nfunction getDocTypeColor(docType: any) {\n if (colorCache[docType]) {\n return colorCache[docType]\n }\n\n const hue = COLOR_HUES[typeColorNum % COLOR_HUES.length]\n\n typeColorNum += 1\n\n colorCache[docType] = {\n fill: hues[hue][400].hex,\n border: rgba(black.hex, 0.5),\n }\n\n return colorCache[docType]\n}\n","import React from 'react'\n\n/**\n * Couple of things to note:\n * - width and height is set to 1em\n * - fill is `currentColor` - this will ensure that the icon looks uniform and\n * that the hover/active state works. You can of course render anything you\n * would like here, but for plugins that are to be used in more than one\n * studio, we suggest these rules are followed\n **/\nexport const GraphViewIcon = () => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1em\" height=\"1em\" viewBox=\"0 0 46.063 46.063\">\n <path\n fill=\"currentColor\"\n d=\"M18.022 38.676v2.813h-1.21q-4.864 0-6.525-1.447-1.64-1.445-1.64-5.762v-4.666q0-2.95-1.055-4.083-1.055-1.13-3.828-1.13h-1.19v-2.794h1.19q2.793 0 3.828-1.114 1.055-1.132 1.055-4.043V11.76q0-4.316 1.64-5.742 1.66-1.446 6.524-1.446h1.212v2.793h-1.328q-2.754 0-3.594.86-.84.86-.84 3.613v4.844q0 3.066-.898 4.453-.88 1.387-3.028 1.875 2.168.527 3.047 1.914.88 1.387.88 4.434v4.843q0 2.754.84 3.614.84.86 3.594.86h1.328zM28.04 38.676h1.368q2.735 0 3.555-.84.84-.84.84-3.633V29.36q0-3.047.88-4.434.878-1.387 3.046-1.914-2.17-.488-3.048-1.875-.88-1.387-.88-4.453V11.84q0-2.773-.84-3.613-.82-.86-3.554-.86H28.04V4.574h1.232q4.863 0 6.484 1.446 1.64 1.426 1.64 5.742v4.687q0 2.91 1.055 4.042 1.056 1.114 3.83 1.114h1.21V24.4h-1.21q-2.774 0-3.83 1.13-1.053 1.134-1.053 4.084v4.667q0 4.318-1.64 5.763-1.622 1.446-6.485 1.446h-1.23v-2.814z\"\n />\n </svg>\n)\n","import React from 'react'\nimport {definePlugin} from 'sanity'\nimport {route} from 'sanity/router'\n\nimport {GraphView} from './tool/GraphView'\nimport {GraphViewIcon} from './tool/GraphViewIcon'\n\nexport interface GraphViewConfig {\n query?: string\n}\n\nexport const contentGraphView = definePlugin<GraphViewConfig>((config: GraphViewConfig = {}) => {\n return {\n name: '@sanity/content-graph-view',\n\n tools: (prev) => {\n return [\n ...prev,\n {\n name: 'graph-your-content',\n title: 'Graph',\n icon: GraphViewIcon,\n component: function component() {\n return <GraphView {...config} />\n },\n router: route.create('/:selectedDocumentId'),\n },\n ]\n },\n }\n})\n"],"names":["styled","black","useEffect","BezierEasing","uuidv4","useUserColorManager","useState","router","useRouter","useClient","useCallback","deepEqual","useTheme","jsx","jsxs","ForceGraph2D","rgba","gray","white","COLOR_HUES","hues","definePlugin","route"],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import * as sanity0 from "sanity";
2
+ interface GraphViewConfig {
3
+ query?: string;
4
+ }
5
+ declare const contentGraphView: sanity0.Plugin<GraphViewConfig>;
6
+ export { type GraphViewConfig, contentGraphView };
@@ -0,0 +1,6 @@
1
+ import * as sanity0 from "sanity";
2
+ interface GraphViewConfig {
3
+ query?: string;
4
+ }
5
+ declare const contentGraphView: sanity0.Plugin<GraphViewConfig>;
6
+ export { type GraphViewConfig, contentGraphView };