sanity-plugin-graph-view 5.0.21 → 5.0.23
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.
|
@@ -12,22 +12,22 @@ import { v4 } from "uuid";
|
|
|
12
12
|
import { styled } from "styled-components";
|
|
13
13
|
const GraphRoot = styled.div.withConfig({
|
|
14
14
|
displayName: "GraphRoot",
|
|
15
|
-
componentId: "sc-
|
|
15
|
+
componentId: "sc-h72uv9-0"
|
|
16
16
|
})`font-family:${({ theme }) => theme.fonts.text.family};position:absolute;top:0;left:0;width:100%;height:100%;background:${black.hex};`, GraphWrapper = styled.div.withConfig({
|
|
17
17
|
displayName: "GraphWrapper",
|
|
18
|
-
componentId: "sc-
|
|
18
|
+
componentId: "sc-h72uv9-1"
|
|
19
19
|
})`position:relative;width:100%;height:100%;`, HoverNode = styled.div.withConfig({
|
|
20
20
|
displayName: "HoverNode",
|
|
21
|
-
componentId: "sc-
|
|
21
|
+
componentId: "sc-h72uv9-2"
|
|
22
22
|
})`font-family:${({ theme }) => theme.fonts.text.family};display:none;position:absolute;bottom:${({ theme }) => theme.space[0]}px;left:50%;transform:translate3d(-50%,0,0);background:var(--component-bg);border-radius:${({ theme }) => theme.radius[2]}px;padding:${({ theme }) => theme.space[2]}px;z-index:1000;&:empty{display:none;}`, Legend = styled.div.withConfig({
|
|
23
23
|
displayName: "Legend",
|
|
24
|
-
componentId: "sc-
|
|
24
|
+
componentId: "sc-h72uv9-3"
|
|
25
25
|
})`color:#ccc;position:absolute;top:${({ theme }) => theme.space[4]}px;left:${({ theme }) => theme.space[4]}px;& > div{margin:5px 0;}`, LegendRow = styled.div.withConfig({
|
|
26
26
|
displayName: "LegendRow",
|
|
27
|
-
componentId: "sc-
|
|
27
|
+
componentId: "sc-h72uv9-4"
|
|
28
28
|
})`display:flex;`, LegendBadge = styled.div.withConfig({
|
|
29
29
|
displayName: "LegendBadge",
|
|
30
|
-
componentId: "sc-
|
|
30
|
+
componentId: "sc-h72uv9-5"
|
|
31
31
|
})`width:1.25em;height:1.25em;background:currentColor;border-radius:50%;margin-right:${({ theme }) => theme.space[2]}px;`;
|
|
32
32
|
function sizeOf(value) {
|
|
33
33
|
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;
|
|
@@ -153,9 +153,9 @@ function GraphView({ tool: { options: props } }) {
|
|
|
153
153
|
doc._id = stripDraftId(doc._id);
|
|
154
154
|
let docsById = {};
|
|
155
155
|
for (let d of documents) docsById[d._id] = d;
|
|
156
|
-
let oldDoc,
|
|
157
|
-
idx >= 0 ? (oldDoc =
|
|
158
|
-
let newGraph = graph.clone(), oldRefs = findRefs(oldDoc || {}).filter((id) => id === doc._id || docsById[id] !== null), newRefs = findRefs(doc).filter((
|
|
156
|
+
let oldDoc, docs = [...documents], idx = documents.findIndex((d) => d._id === doc._id);
|
|
157
|
+
idx >= 0 ? (oldDoc = docs[idx], docs[idx] = doc) : docs.push(doc), setDocuments(docs), setDocTypes(getDocTypeCounts(docs)), setMaxSize(Math.max(...docs.map(sizeOf)));
|
|
158
|
+
let newGraph = graph.clone(), oldRefs = findRefs(oldDoc || {}).filter((id) => id === doc._id || docsById[id] !== null), newRefs = findRefs(doc).filter((id) => id === doc._id || docsById[id] !== null), graphChanged = !deepEqual(oldRefs, newRefs);
|
|
159
159
|
graphChanged && (newGraph.data.links = newGraph.data.links.filter((l) => l.source.id !== doc._id).concat(newRefs.map((ref) => ({
|
|
160
160
|
source: doc._id,
|
|
161
161
|
target: ref
|
|
@@ -169,10 +169,10 @@ function GraphView({ tool: { options: props } }) {
|
|
|
169
169
|
let user = await users.getById(update.identity, client);
|
|
170
170
|
graph.setSession(user, docNode);
|
|
171
171
|
} else if (update.transition === "disappear") {
|
|
172
|
-
let docId = stripDraftId(update.documentId),
|
|
173
|
-
setDocuments(
|
|
174
|
-
let
|
|
175
|
-
|
|
172
|
+
let docId = stripDraftId(update.documentId), docs = documents.filter((d) => d._id !== docId);
|
|
173
|
+
setDocuments(docs), setDocTypes(getDocTypeCounts(docs)), setMaxSize(Math.max(...docs.map(sizeOf)));
|
|
174
|
+
let newGraph = graph.clone();
|
|
175
|
+
newGraph.data.links = newGraph.data.links.filter((l) => l.source.id !== docId && l.target.id !== docId), newGraph.data.nodes = newGraph.data.nodes.filter((n) => n.id !== docId), setGraph(newGraph);
|
|
176
176
|
}
|
|
177
177
|
});
|
|
178
178
|
return () => {
|
|
@@ -210,24 +210,24 @@ function GraphView({ tool: { options: props } }) {
|
|
|
210
210
|
nodeAutoColorBy: "group",
|
|
211
211
|
enableNodeDrag: !1,
|
|
212
212
|
onNodeHover: (node) => setHoverNode(node),
|
|
213
|
-
onNodeClick: (
|
|
213
|
+
onNodeClick: (node) => {
|
|
214
214
|
router.navigateIntent("edit", {
|
|
215
|
-
id:
|
|
216
|
-
documentType:
|
|
215
|
+
id: node.doc._id,
|
|
216
|
+
documentType: node.doc._type
|
|
217
217
|
});
|
|
218
218
|
},
|
|
219
219
|
linkColor: () => rgba(gray[500].hex, .25),
|
|
220
220
|
nodeLabel: () => "",
|
|
221
221
|
nodeRelSize: 1,
|
|
222
|
-
nodeVal: (
|
|
222
|
+
nodeVal: (node) => valueFor(node.doc, maxSize),
|
|
223
223
|
onRenderFramePost: (ctx, globalScale) => {
|
|
224
224
|
for (let session of graph.sessions) {
|
|
225
|
-
let
|
|
226
|
-
if (
|
|
227
|
-
let idleFactorRange = idleTimeout, angle = session.angle, radius = Math.sqrt(valueFor(
|
|
225
|
+
let node = graph.data.nodes.find((n) => n.doc && n.doc._id === session?.doc?._id);
|
|
226
|
+
if (node) {
|
|
227
|
+
let 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;
|
|
228
228
|
ctx.save();
|
|
229
229
|
try {
|
|
230
|
-
if (ctx.globalAlpha = fadeEasing(1 - Math.min(idleFactorRange, Date.now() - session.lastActive) / idleFactorRange), ctx.font = `bold ${Math.round(12 / globalScale)}px sans-serif`, ctx.beginPath(), ctx.strokeStyle = rgba(white.hex, 1), ctx.lineWidth = 2 / globalScale, ctx.moveTo(
|
|
230
|
+
if (ctx.globalAlpha = fadeEasing(1 - Math.min(idleFactorRange, Date.now() - session.lastActive) / idleFactorRange), ctx.font = `bold ${Math.round(12 / globalScale)}px sans-serif`, ctx.beginPath(), ctx.strokeStyle = rgba(white.hex, 1), ctx.lineWidth = 2 / globalScale, ctx.moveTo(node.x + Math.sin(angle) * (distance - imgW / 2) / globalScale, node.y + Math.cos(angle) * (distance - imgH / 2) / globalScale), ctx.lineTo(node.x + Math.sin(angle) * radius, node.y + Math.cos(angle) * radius), ctx.stroke(), ctx.beginPath(), ctx.strokeStyle = rgba(white.hex, 1), ctx.lineWidth = 2 / globalScale, ctx.arc(node.x, node.y, radius, 0, 2 * Math.PI, !1), ctx.stroke(), image) {
|
|
231
231
|
ctx.save();
|
|
232
232
|
try {
|
|
233
233
|
let f = softEasing(Math.max(0, (700 - (Date.now() - session.startTime)) / 700));
|
|
@@ -245,24 +245,24 @@ function GraphView({ tool: { options: props } }) {
|
|
|
245
245
|
}
|
|
246
246
|
}
|
|
247
247
|
},
|
|
248
|
-
nodeCanvasObject: (
|
|
249
|
-
if (
|
|
250
|
-
let nodeColor = getDocTypeColor(
|
|
251
|
-
if (
|
|
252
|
-
|
|
253
|
-
let w =
|
|
248
|
+
nodeCanvasObject: (node, ctx, globalScale) => {
|
|
249
|
+
if (node.type === "document") {
|
|
250
|
+
let nodeColor = getDocTypeColor(node.doc._type), radius = Math.sqrt(valueFor(node.doc, maxSize)), fontSize = Math.min(100, 10 / globalScale);
|
|
251
|
+
if (ctx.beginPath(), ctx.fillStyle = hoverNode !== null && node.doc._id === hoverNode.doc._id ? rgba(gray[500].hex, .8) : nodeColor.fill, ctx.strokeStyle = nodeColor.border, ctx.lineWidth = .5, ctx.arc(node.x, node.y, radius, 0, 2 * Math.PI, !1), ctx.stroke(), ctx.fill(), radius * globalScale > 10) {
|
|
252
|
+
ctx.font = `${fontSize}px sans-serif`;
|
|
253
|
+
let w = radius * 2 + 30 / globalScale;
|
|
254
254
|
for (let len = 50; len >= 5; len /= 1.2) {
|
|
255
|
-
let label = truncate(labelFor(
|
|
256
|
-
if (
|
|
257
|
-
|
|
255
|
+
let label = truncate(labelFor(node.doc), Math.round(len));
|
|
256
|
+
if (ctx.measureText(label).width < w) {
|
|
257
|
+
ctx.textAlign = "center", ctx.textBaseline = "top", ctx.strokeStyle = rgba(black.hex, .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);
|
|
258
258
|
break;
|
|
259
259
|
}
|
|
260
260
|
}
|
|
261
261
|
}
|
|
262
262
|
}
|
|
263
263
|
},
|
|
264
|
-
linkCanvasObject: (link,
|
|
265
|
-
|
|
264
|
+
linkCanvasObject: (link, ctx, globalScale) => {
|
|
265
|
+
ctx.beginPath(), ctx.strokeStyle = rgba(gray[500].hex, .125), ctx.lineWidth = 2 / globalScale, ctx.moveTo(link.source.x, link.source.y), ctx.lineTo(link.target.x, link.target.y), ctx.stroke();
|
|
266
266
|
}
|
|
267
267
|
})
|
|
268
268
|
]
|
|
@@ -281,4 +281,4 @@ function getDocTypeColor(docType) {
|
|
|
281
281
|
}
|
|
282
282
|
export { GraphView as default };
|
|
283
283
|
|
|
284
|
-
//# sourceMappingURL=GraphView-
|
|
284
|
+
//# sourceMappingURL=GraphView-BrPWN-Cw.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GraphView-BrPWN-Cw.js","names":[],"sources":["../src/tool/GraphViewStyle.tsx","../src/tool/utils.ts","../src/tool/GraphView.tsx"],"sourcesContent":["import {black} from '@sanity/color'\n// TODO: when upgrading to @sanity/ui@4 start using the new tokens\n// oxlint-disable typescript/no-deprecated\nimport type {Theme} from '@sanity/ui'\nimport {type PropsWithChildren} from 'react'\nimport {styled} from 'styled-components'\n\ntype Style = PropsWithChildren<{theme: SanityTheme}>\ntype SanityTheme = Theme['sanity']\n\nexport const GraphRoot: React.FC<Style> = styled.div<Style>`\n font-family: ${({theme}) => 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`\n\nexport const HoverNode: React.FC<Style> = styled.div<Style>`\n font-family: ${({theme}) => theme.fonts.text.family};\n display: none;\n position: absolute;\n bottom: ${({theme}) => theme.space[0]}px;\n left: 50%;\n transform: translate3d(-50%, 0, 0);\n background: var(--component-bg);\n border-radius: ${({theme}) => theme.radius[2]}px;\n padding: ${({theme}) => 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<Style>`\n color: #ccc;\n position: absolute;\n top: ${({theme}) => theme.space[4]}px;\n left: ${({theme}) => theme.space[4]}px;\n\n & > div {\n margin: 5px 0;\n }\n`\n\nexport const LegendRow: React.FC<\n PropsWithChildren<{className?: string; style?: React.CSSProperties}>\n> = styled.div`\n display: flex;\n`\n\nexport const LegendBadge: React.FC<Style> = styled.div<Style>`\n width: 1.25em;\n height: 1.25em;\n background: currentColor;\n border-radius: 50%;\n margin-right: ${({theme}) => theme.space[2]}px;\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 // oxlint-disable-next-line prefer-add-event-listener\n img.onload = () => {\n resolve(img)\n }\n // oxlint-disable-next-line prefer-add-event-listener\n img.onerror = (event) => {\n console.error('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 {black, COLOR_HUES, gray, white, hues} from '@sanity/color'\nimport {useTheme} from '@sanity/ui'\nimport BezierEasing from 'bezier-easing'\nimport deepEqual from 'deep-equal'\nimport {rgba} from 'polished'\nimport {useEffect, useState} from 'react'\nimport ForceGraph2D from 'react-force-graph-2d'\nimport {useClient, useUserColorManager, type SanityClient, type SanityDocument} from 'sanity'\nimport {useRouter} from 'sanity/router'\nimport {v4 as uuidv4} from 'uuid'\n\nimport {GraphRoot, GraphWrapper, HoverNode, Legend, LegendBadge, LegendRow} from './GraphViewStyle'\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 // oxlint-disable-next-line restrict-template-expressions,no-base-to-string\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 default function GraphView({\n tool: {options: props},\n}: {\n tool: {options: GraphViewConfig}\n}): React.JSX.Element {\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 useEffect(() => {\n void client.fetch(query).then((_docs) => {\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 return undefined\n })\n }, [client, query])\n\n useEffect(() => {\n const subscription = client.listen(query, {}, {}).subscribe(async (update) => {\n if (update.type !== 'mutation') return\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 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 return () => {\n subscription.unsubscribe()\n }\n }, [client, query, documents, graph])\n\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 key={docType}\n className={'legend__row'}\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 <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 // oxlint-disable-next-line react/todo\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"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -25,7 +25,7 @@ function GraphViewIcon() {
|
|
|
25
25
|
})
|
|
26
26
|
}), $[0] = t0) : t0 = $[0], t0;
|
|
27
27
|
}
|
|
28
|
-
const GraphView = lazy(() => import("./GraphView-
|
|
28
|
+
const GraphView = lazy(() => import("./GraphView-BrPWN-Cw.js")), contentGraphView = definePlugin((config = {}) => ({
|
|
29
29
|
name: "@sanity/content-graph-view",
|
|
30
30
|
tools: [{
|
|
31
31
|
name: "graph-your-content",
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["
|
|
1
|
+
{"version":3,"file":"index.js","names":["<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>"],"sources":["../src/tool/GraphViewIcon.tsx","../src/plugin.tsx"],"sourcesContent":["/**\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 function GraphViewIcon(): React.JSX.Element {\n return (\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}\n","import {lazy} from 'react'\nimport {definePlugin, type Plugin} from 'sanity'\nimport {route} from 'sanity/router'\n\nimport {GraphViewIcon} from './tool/GraphViewIcon'\n\nexport interface GraphViewConfig {\n query?: string\n}\n\nconst GraphView = lazy(() => import('./tool/GraphView'))\n\nexport const contentGraphView: Plugin<void | GraphViewConfig> =\n definePlugin<void | GraphViewConfig>((config: GraphViewConfig = {}) => {\n return {\n name: '@sanity/content-graph-view',\n\n tools: [\n {\n name: 'graph-your-content',\n title: 'Graph',\n icon: GraphViewIcon,\n component: GraphView,\n options: config,\n router: route.create('/:selectedDocumentId'),\n },\n ],\n }\n })\n"],"mappings":";;;;;;;;;;;;;;AAQA,SAAgB,gBAAmC;eAE/CA;CADF,2DACG,KAAA,oBAAA,OAAD;EAAK,OAAM;EAA6B,OAAM;EAAM,QAAO;EAAM,SAAQ;EACvE,UAAA,oBAAC,QAAD;GACE,MAAK;GACL,GAAE;EACH,CAAA;CACE,CAAA,2BALLA;AAOJ;ACPA,MAAM,YAAY,WAAW,OAAO,0BAAmB,GAE1C,mBACX,cAAsC,SAA0B,CAAC,OACxD;CACL,MAAM;CAEN,OAAO,CACL;EACE,MAAM;EACN,OAAO;EACP,MAAM;EACN,WAAW;EACX,SAAS;EACT,QAAQ,MAAM,OAAO,sBAAsB;CAC7C,CACF;AACF,EACD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sanity-plugin-graph-view",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.23",
|
|
4
4
|
"description": "",
|
|
5
5
|
"homepage": "https://github.com/sanity-io/plugins/tree/main/plugins/sanity-plugin-graph-view#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@sanity/color": "^3.0.8",
|
|
27
|
-
"@sanity/ui": "^4.0.
|
|
27
|
+
"@sanity/ui": "^4.0.4",
|
|
28
28
|
"bezier-easing": "^2.1.0",
|
|
29
29
|
"deep-equal": "^2.2.3",
|
|
30
30
|
"polished": "^4.3.1",
|
|
@@ -33,14 +33,14 @@
|
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@sanity/tsconfig": "^2.2.1",
|
|
36
|
-
"@sanity/tsdown-config": "^0.
|
|
36
|
+
"@sanity/tsdown-config": "^0.26.0",
|
|
37
37
|
"@types/deep-equal": "^1.0.4",
|
|
38
38
|
"@types/node": "^24.13.3",
|
|
39
39
|
"@types/react": "^19.2.18",
|
|
40
|
-
"
|
|
40
|
+
"oxc-transform-react": "^0.145.0",
|
|
41
41
|
"react": "^19.2.8",
|
|
42
|
-
"sanity": "^6.
|
|
43
|
-
"styled-components": "^6.5.
|
|
42
|
+
"sanity": "^6.10.1",
|
|
43
|
+
"styled-components": "^6.5.3",
|
|
44
44
|
"tsdown": "^0.22.14"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GraphView-iAXk-9Pb.js","names":["black","Theme","PropsWithChildren","styled","Style","theme","SanityTheme","GraphRoot","React","FC","div","fonts","text","family","hex","GraphWrapper","HoverNode","space","radius","Legend","LegendRow","className","style","CSSProperties","LegendBadge","sizeOf","value","Object","entries","reduce","total","k","v","Array","isArray","length","loadImage","url","w","h","Promise","HTMLImageElement","resolve","img","Image","onload","onerror","event","console","error","src","sortBy","array","T","f","t","sort","a","b","va","vb","truncate","s","limit","substring","black","COLOR_HUES","gray","white","hues","useTheme","BezierEasing","deepEqual","rgba","useEffect","useState","ForceGraph2D","useClient","useUserColorManager","SanityClient","SanityDocument","useRouter","v4","uuidv4","GraphRoot","GraphWrapper","HoverNode","Legend","LegendBadge","LegendRow","sortBy","loadImage","sizeOf","truncate","DEFAULT_QUERY","fadeEasing","softEasing","idleTimeout","imageSize","getTopDocTypes","counts","Record","Object","keys","docType","reverse","slice","formatDocType","substring","toUpperCase","replace","trim","getDocTypeCounts","docs","types","doc","_type","labelFor","title","name","_id","valueFor","maxSize","findRefs","obj","dest","k","v","entries","length","push","stripDraftId","Array","isArray","id","deduplicateDrafts","deduped","startsWith","assign","values","Users","_users","getById","client","user","find","u","users","image","imageUrl","Session","lastActive","startTime","angle","GraphData","sessions","data","constructor","docsById","nodes","map","d","type","links","flatMap","ref","source","target","filter","link","setSession","docNode","session","s","Date","now","Math","random","PI","reapSessions","i","clone","copy","GraphViewConfig","query","apiVersion","GraphView","tool","options","props","React","JSX","Element","userColorManager","setMaxSize","hoverNode","setHoverNode","documents","setDocuments","docTypes","setDocTypes","graph","setGraph","router","fetch","then","_docs","max","undefined","subscription","listen","subscribe","update","result","oldDoc","idx","findIndex","newGraph","oldRefs","newRefs","graphChanged","l","concat","nodeIdx","n","identity","transition","docId","documentId","unsubscribe","interval","setInterval","clearInterval","theme","sanity","color","getDocTypeColor","fill","node","navigateIntent","documentType","hex","ctx","globalScale","idleFactorRange","radius","sqrt","userColor","get","displayName","tints","distance","imgW","width","imgH","height","x","sin","y","cos","save","globalAlpha","min","font","round","beginPath","strokeStyle","lineWidth","moveTo","lineTo","stroke","arc","dur","f","fillStyle","clip","drawImage","restore","above","textY","textAlign","textBaseline","fillText","nodeColor","fontSize","border","w","len","label","textMetrics","measureText","strokeText","colorCache","typeColorNum","hue"],"sources":["../src/tool/GraphViewStyle.tsx","../src/tool/utils.ts","../src/tool/GraphView.tsx"],"sourcesContent":["import {black} from '@sanity/color'\n// TODO: when upgrading to @sanity/ui@4 start using the new tokens\n// oxlint-disable typescript/no-deprecated\nimport type {Theme} from '@sanity/ui'\nimport {type PropsWithChildren} from 'react'\nimport {styled} from 'styled-components'\n\ntype Style = PropsWithChildren<{theme: SanityTheme}>\ntype SanityTheme = Theme['sanity']\n\nexport const GraphRoot: React.FC<Style> = styled.div<Style>`\n font-family: ${({theme}) => 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`\n\nexport const HoverNode: React.FC<Style> = styled.div<Style>`\n font-family: ${({theme}) => theme.fonts.text.family};\n display: none;\n position: absolute;\n bottom: ${({theme}) => theme.space[0]}px;\n left: 50%;\n transform: translate3d(-50%, 0, 0);\n background: var(--component-bg);\n border-radius: ${({theme}) => theme.radius[2]}px;\n padding: ${({theme}) => 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<Style>`\n color: #ccc;\n position: absolute;\n top: ${({theme}) => theme.space[4]}px;\n left: ${({theme}) => theme.space[4]}px;\n\n & > div {\n margin: 5px 0;\n }\n`\n\nexport const LegendRow: React.FC<\n PropsWithChildren<{className?: string; style?: React.CSSProperties}>\n> = styled.div`\n display: flex;\n`\n\nexport const LegendBadge: React.FC<Style> = styled.div<Style>`\n width: 1.25em;\n height: 1.25em;\n background: currentColor;\n border-radius: 50%;\n margin-right: ${({theme}) => theme.space[2]}px;\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 // oxlint-disable-next-line prefer-add-event-listener\n img.onload = () => {\n resolve(img)\n }\n // oxlint-disable-next-line prefer-add-event-listener\n img.onerror = (event) => {\n console.error('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 {black, COLOR_HUES, gray, white, hues} from '@sanity/color'\nimport {useTheme} from '@sanity/ui'\nimport BezierEasing from 'bezier-easing'\nimport deepEqual from 'deep-equal'\nimport {rgba} from 'polished'\nimport {useEffect, useState} from 'react'\nimport ForceGraph2D from 'react-force-graph-2d'\nimport {useClient, useUserColorManager, type SanityClient, type SanityDocument} from 'sanity'\nimport {useRouter} from 'sanity/router'\nimport {v4 as uuidv4} from 'uuid'\n\nimport {GraphRoot, GraphWrapper, HoverNode, Legend, LegendBadge, LegendRow} from './GraphViewStyle'\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 // oxlint-disable-next-line restrict-template-expressions,no-base-to-string\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 default function GraphView({\n tool: {options: props},\n}: {\n tool: {options: GraphViewConfig}\n}): React.JSX.Element {\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 useEffect(() => {\n void client.fetch(query).then((_docs) => {\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 return undefined\n })\n }, [client, query])\n\n useEffect(() => {\n const subscription = client.listen(query, {}, {}).subscribe(async (update) => {\n if (update.type !== 'mutation') return\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 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 return () => {\n subscription.unsubscribe()\n }\n }, [client, query, documents, graph])\n\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 key={docType}\n className={'legend__row'}\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 <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"],"mappings":";;;;;;;;;;;;AAUA,MAAaO,YAA6BJ,OAAOO,IAAAA,WAAAA;;;AAAU,CAAA,CAAA,gBACzC,EAACL,YAAWA,MAAMM,MAAMC,KAAKC,OAAM,oEAMrCb,MAAMc,IAAG,IAGZC,eAAgCZ,OAAOO,IAAAA,WAAAA;;;AAAG,CAAA,CAAA,6CAM1CM,YAA6Bb,OAAOO,IAAAA,WAAAA;;;AAAU,CAAA,CAAA,gBACzC,EAACL,YAAWA,MAAMM,MAAMC,KAAKC,OAAM,0CAGxC,EAACR,YAAWA,MAAMY,MAAM,GAAE,4FAInB,EAACZ,YAAWA,MAAMa,OAAO,GAAE,cACjC,EAACb,YAAWA,MAAMY,MAAM,GAAE,yCAQ3BE,SAA0BhB,OAAOO,IAAAA,WAAAA;;;AAAU,CAAA,CAAA,qCAG9C,EAACL,YAAWA,MAAMY,MAAM,GAAE,WACzB,EAACZ,YAAWA,MAAMY,MAAM,GAAE,4BAOxBG,YAETjB,OAAOO,IAAAA,WAAAA;;;AAAG,CAAA,CAAA,iBAIDc,cAA+BrB,OAAOO,IAAAA,WAAAA;;;AAAU,CAAA,CAAA,sFAK1C,EAACL,YAAWA,MAAMY,MAAM,GAAE;ACjE7C,SAAgBQ,OAAOC,OAAoB;CAiBzC,OAhBIA,UAAU,OACL,IAGL,OAAOA,SAAU,WACZC,OAAOC,QAAQF,KAAK,CAAC,CAACG,QAAQC,OAAO,CAACC,GAAGC,OAAOF,QAAQL,OAAOM,CAAC,IAAIN,OAAOO,CAAC,GAAG,CAAC,IAGrFC,MAAMC,QAAQR,KAAK,IACdC,OAAOC,QAAQF,KAAK,CAAC,CAACG,QAAQC,OAAOE,MAAMF,QAAQL,OAAOO,CAAC,GAAG,CAAC,IAGpE,OAAON,SAAU,WACZA,MAAMS,SAGR;AACT;AAEA,SAAgBC,UAAUC,KAAaC,GAAWC,GAA6C;CAC7F,OAAO,IAAIC,SAASE,YAAY;EAC9B,IAAMC,MAAM,IAAIC,MAAMN,GAAGC,CAAC;EAU1BI,AARAA,IAAIE,eAAe;GACjBH,QAAQC,GAAG;EACb,GAEAA,IAAIG,WAAWC,UAAU;GAEvBL,AADAM,QAAQC,MAAM,eAAeF,KAAK,GAClCL,QAAQ,IAAI;EACd,GACAC,IAAIO,MAAMb;CACZ,CAAC;AACH;AAEA,SAAgBc,OAAUC,OAAYE,GAA0B;CAC9D,OAAOF,MAAMI,MAAMC,GAAGC,MAAM;EAC1B,IAAMC,KAAKL,EAAEG,CAAC,GACRG,KAAKN,EAAEI,CAAC;EACd,OAAOC,KAAKC,KAAK,KAAKD,OAAKC;CAC7B,CAAC;AACH;AAEA,SAAgBC,SAASC,GAAWC,OAAuB;CAIzD,OAHID,EAAE3B,SAAS4B,QACN,GAAGD,EAAEE,UAAU,GAAGD,KAAK,EAAC,KAE1BD;AACT;ACnCA,MAQMiC,aAAaxB,aAAa,GAAG,IAAK,GAAG,CAAC,GACtCyB,aAAazB,aAAa,KAAM,IAAK,GAAK,CAAG,GAC7C0B,cAAc;AAGpB,SAASE,eAAeC,QAAgC;CACtD,OAAOV,OAAOY,OAAOC,KAAKH,MAAM,IAAII,YAAYJ,OAAOI,YAAY,CAAC,CAAC,CAClEC,QAAQ,CAAC,CACTC,MAAM,GAAG,EAAE;AAChB;AAEA,SAASC,cAAcH,SAAiB;CACtC,QAAQA,QAAQI,UAAU,GAAG,CAAC,CAAC,CAACC,YAAY,IAAIL,QAAQI,UAAU,CAAC,EAAA,CAChEE,QAAQ,OAAO,GAAG,CAAC,CACnBA,QAAQ,UAAU,KAAK,CAAC,CACxBC,KAAK;AACV;AAEA,SAASC,iBAAiBC,MAAwB;CAChD,IAAMC,QAAgC,CAAC;CACvC,KAAK,IAAMC,OAAOF,MAChBC,MAAMC,IAAIC,UAAUF,MAAMC,IAAIC,UAAU,KAAK;CAE/C,OAAOF;AACT;AAEA,SAASG,SAASF,KAAqB;CAErC,OAAO,GAAGA,IAAIG,SAASH,IAAII,QAAQJ,IAAIK,MAAMT,KAAK;AACpD;AAEA,SAASU,SAASN,KAAUO,SAAiB;CAC3C,OAAO,IAAI,OAAO9B,OAAOuB,GAAG,IAAIO;AAClC;AAEA,SAASC,SAASC,KAAUC,OAAc,CAAA,GAAI;CAC5C,IAAID,QAAQ,MACN;MAAA,OAAOA,OAAQ,UACjB,KAAK,IAAM,CAACE,GAAGC,MAAMzB,OAAO0B,QAAQJ,GAAG,GAIrCD,AAHIG,MAAM,UAAU,OAAOC,KAAM,YAAYA,EAAEE,SAAS,KACtDJ,KAAKK,KAAKC,aAAaJ,CAAC,CAAC,GAE3BJ,SAASI,GAAGF,IAAI;OAEb,IAAIO,MAAMC,QAAQT,GAAG,GAC1B,KAAK,IAAMG,KAAKH,KACdD,SAASI,GAAGF,IAAI;CAAA;CAItB,OAAOA;AACT;AAEA,SAASM,aAAaG,IAAY;CAChC,OAAOA,GAAGxB,QAAQ,aAAa,EAAE;AACnC;AAEA,SAASyB,kBAAkBtB,MAAwB;CACjD,IAAMuB,UAA0C,CAAC;CACjD,KAAK,IAAMrB,OAAOF,MAChB,AAAKE,IAAIK,IAAIiB,WAAW,SAAS,MAC/BD,QAAQrB,IAAIK,OAAOL;CAGvB,KAAK,IAAMA,OAAOF,MAChB,IAAIE,IAAIK,IAAIiB,WAAW,SAAS,GAAG;EACjC,IAAMH,KAAKH,aAAahB,IAAIK,GAAG;EAC/BgB,QAAQF,MAAMhC,OAAOoC,OAAOvB,KAAK,EAACK,KAAKc,GAAE,CAAC;CAC5C;CAEF,OAAOhC,OAAOqC,OAAOH,OAAO;AAC9B;AAEA,IAAMI,QAAN,MAAY;CACVC,SAAgB,CAAA;CAEhB,MAAMC,QAAQR,IAAYS,QAAsB;EAC9C,IAAIC,OAAO,KAAKH,OAAOI,MAAMC,MAAMA,EAAE1B,QAAQc,EAAE;EAW/C,OAVKU,SACHA,OAAO,MAAMD,OAAOI,MAAML,QAAQR,EAAE,GACpC,KAAKO,OAAOX,KAAKc,IAAI,GACrBA,KAAKI,QAAQ,MAAMzD,UACjBqD,KAAKK,YACH,wGACFnD,IACAA,EACF,IAEK8C;CACT;AACF,GAEMM,UAAN,MAAc;CACZhB,KAAoB;CACpBU,OAAY;CACZ7B,MAA6B;CAC7BoC,aAAqB;CACrBC,YAAoB;CACpBC,QAAgB;AAClB,GAEMC,YAAN,MAAMA,UAAU;CACdC,WAAsB,CAAA;CACtBC;CAEAC,YAAY5C,OAAyB,CAAA,GAAI;EACvC,IAAM6C,WAA2C,CAAC;EAClD,KAAK,IAAM3C,OAAOF,MAChB6C,SAAS3C,IAAIK,OAAOL;EAGtB,KAAKyC,OAAO;GACVG,OAAO9C,KAAK+C,KAAKC,MAAM3D,OAAOoC,OAAO;IAACJ,IAAI2B,EAAEzC;IAAK0C,MAAM;IAAY/C,KAAK8C;GAAC,CAAC,CAAC;GAC3EE,OAAOlD,KACJmD,SAASjD,QAAQQ,SAASR,GAAG,CAAC,CAAC6C,KAAKK,SAAS;IAACC,QAAQnD,IAAIK;IAAK+C,QAAQF;GAAG,EAAE,CAAC,CAAC,CAC9EG,QAAQC,SAASX,SAASW,KAAKH,WAAWR,SAASW,KAAKF,OAAO;EACpE;CACF;CAEAG,WAAW1B,MAAW2B,SAAc;EAClC,IAAIC,UAAU,KAAKjB,SAASV,MAAM4B,MAAMA,EAAE7B,KAAKV,OAAOU,KAAKV,MAAMuC,EAAE1D,KAAKK,QAAQmD,QAAQxD,IAAIK,GAAG;EAU/FoD,AATKA,YACHA,UAAU,IAAItB,QAAQ,GACtBsB,QAAQtC,KAAKnD,GAAO,GACpByF,QAAQ5B,OAAOA,MACf4B,QAAQpB,YAAYsB,KAAKC,IAAI,GAC7BH,QAAQzD,MAAMwD,QAAQxD,KACtByD,QAAQnB,QAAQuB,KAAKC,OAAO,IAAI,IAAID,KAAKE,IACzC,KAAKvB,SAASzB,KAAK0C,OAAO,IAE5BA,QAAQrB,aAAauB,KAAKC,IAAI;CAChC;CAEAI,eAAe;EACb,KAAK,IAAIC,IAAI,GAAGA,IAAI,KAAKzB,SAAS1B,QAAQmD,KAAK;GAC7C,IAAMR,UAAU,KAAKjB,SAASyB;GAC9B,AAAIN,KAAKC,IAAI,IAAIH,QAAQrB,aAAatD,gBACpC,KAAK0D,WAAW,CAAC,GAAG,KAAKA,SAASjD,MAAM,GAAG0E,CAAC,GAAG,GAAG,KAAKzB,SAASjD,MAAM0E,IAAI,CAAC,CAAC,GAC5EA;EAEJ;CACF;CAEAC,QAAQ;EACN,IAAMC,OAAO,IAAI5B,UAAU;EAM3B,OALApD,OAAOoC,OAAO4C,MAAM,IAAI,GACxBA,KAAK1B,OAAO;GACVG,OAAO,CAAC,GAAG,KAAKH,KAAKG,KAAK;GAC1BI,OAAO,CAAC,GAAG,KAAKP,KAAKO,KAAK;EAC5B,GACOmB;CACT;AACF;AAEA,MAAMnC,QAAQ,IAAIP,MAAM;AAQxB,SAAwB8C,UAAU,EAChCC,MAAM,EAACC,SAASC,WAGI;CACpB,IAAML,QAAQK,MAAML,SAAS1F,sHACvB2F,aAAaI,MAAMJ,cAAc,cAEjCQ,mBAAmBnH,oBAAoB,GACvC,CAAC4C,SAASwE,cAAcvH,SAAS,CAAC,GAClC,CAACwH,WAAWC,gBAAgBzH,SAAc,IAAI,GAC9C,CAAC0H,WAAWC,gBAAgB3H,SAA2B,CAAA,CAAE,GACzD,CAAC4H,UAAUC,eAAe7H,SAAiC,CAAC,CAAC,GAC7D,CAAC8H,OAAOC,YAAY/H,eAAe,IAAI+E,UAAU,CAAC,GAClDiD,SAAS1H,UAAU,GACnB8D,SAASlE,UAAU,EAAC4G,WAAU,CAAC;CAyFrC/G,AAvFAA,gBAAgB;EACd,OAAYkI,MAAMpB,KAAK,CAAC,CAACqB,MAAMC,UAAU;GACvC,IAAM7F,OAAOsB,kBAAkBuE,KAAK;GAIpCJ,AAHAR,WAAWlB,KAAK+B,IAAI,GAAG9F,KAAK+C,IAAIpE,MAAM,CAAC,CAAC,GACxC0G,aAAarF,IAAI,GACjBuF,YAAYxF,iBAAiBC,IAAI,CAAC,GAClCyF,SAAS,IAAIhD,UAAUzC,IAAI,CAAC;EAE9B,CAAC;CACH,GAAG,CAAC8B,QAAQyC,KAAK,CAAC,GAElB9G,gBAAgB;EACd,IAAMuI,eAAelE,OAAOmE,OAAO1B,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC2B,UAAU,OAAOC,WAAW;GAC5E,IAAIA,OAAOlD,SAAS,YAAY;GAChC,IAAM/C,MAAMiG,OAAOC;GACnB,IAAIlG,KAAK;IACPA,IAAIK,MAAMW,aAAahB,IAAIK,GAAG;IAE9B,IAAMsC,WAA2C,CAAC;IAClD,KAAK,IAAMG,KAAKoC,WACdvC,SAASG,EAAEzC,OAAOyC;IAGpB,IAAIqD,QACErG,SAAO,CAAC,GAAGoF,SAAS,GACpBkB,MAAMlB,UAAUmB,WAAWvD,QAAMA,IAAEzC,QAAQL,IAAIK,GAAG;IASxD0E,AARIqB,OAAO,KACTD,SAASrG,OAAKsG,MACdtG,OAAKsG,OAAOpG,OAEZF,OAAKiB,KAAKf,GAAG,GAEfmF,aAAarF,MAAI,GACjBuF,YAAYxF,iBAAiBC,MAAI,CAAC,GAClCiF,WAAWlB,KAAK+B,IAAI,GAAG9F,OAAK+C,IAAIpE,MAAM,CAAC,CAAC;IAExC,IAAM6H,WAAWhB,MAAMpB,MAAM,GAEvBqC,UAAU/F,SAAS2F,UAAU,CAAC,CAAC,CAAC,CAAC9C,QACpClC,OAAOA,OAAOnB,IAAIK,OAAOsC,SAASxB,QAAQ,IAC7C,GACMqF,UAAUhG,SAASR,GAAG,CAAC,CAACqD,QAAQlC,SAAOA,SAAOnB,IAAIK,OAAOsC,SAASxB,UAAQ,IAAI,GAEhFsF,eAAe,CAACpJ,UAAUkJ,SAASC,OAAO;IAC9C,AAAIC,iBACFH,SAAS7D,KAAKO,QAAQsD,SAAS7D,KAAKO,MACjCK,QAAQqD,MAAWA,EAAEvD,OAAOhC,OAAOnB,IAAIK,GAAG,CAAC,CAC3CsG,OAAOH,QAAQ3D,KAAKK,SAAS;KAACC,QAAQnD,IAAIK;KAAK+C,QAAQF;IAAG,EAAE,CAAC;IAGlE,IAAIM,SACEoD,UAAUtB,MAAM7C,KAAKG,MAAMyD,WAAWQ,MAAWA,EAAE7G,OAAO6G,EAAE7G,IAAIK,QAAQL,IAAIK,GAAG;IASrF,AARIuG,WAAW,KACbpD,UAAU8B,MAAM7C,KAAKG,MAAMgE,UAC3BpD,QAAQxD,MAAMA,QAEdwD,UAAU;KAACrC,IAAInB,IAAIK;KAAK0C,MAAM;KAAiB/C;IAAG,GAClDsG,SAAS7D,KAAKG,MAAM7B,KAAKyC,OAAO,GAChCiD,eAAe,KAEbA,gBACFlB,SAASe,QAAQ;IAGnB,IAAMzE,OAAO,MAAMG,MAAML,QAAQsE,OAAOa,UAAUlF,MAAM;IACxD0D,MAAM/B,WAAW1B,MAAM2B,OAAO;GAChC,OAAO,IAAIyC,OAAOc,eAAe,aAAa;IAC5C,IAAMC,QAAQhG,aAAaiF,OAAOgB,UAAU,GAEtCnH,SAAOoF,UAAU7B,QAAQP,QAAMA,IAAEzC,QAAQ2G,KAAK;IAGpDjC,AAFAI,aAAarF,MAAI,GACjBuF,YAAYxF,iBAAiBC,MAAI,CAAC,GAClCiF,WAAWlB,KAAK+B,IAAI,GAAG9F,OAAK+C,IAAIpE,MAAM,CAAC,CAAC;IAExC,IAAM6H,aAAWhB,MAAMpB,MAAM;IAK7BqB,AAJAe,WAAS7D,KAAKO,QAAQsD,WAAS7D,KAAKO,MAAMK,QACvCqD,QAAWA,IAAEvD,OAAOhC,OAAO6F,SAASN,IAAEtD,OAAOjC,OAAO6F,KACvD,GACAV,WAAS7D,KAAKG,QAAQ0D,WAAS7D,KAAKG,MAAMS,QAAQwD,QAAWA,IAAE1F,OAAO6F,KAAK,GAC3EzB,SAASe,UAAQ;GACnB;EACF,CAAC;EACD,aAAa;GACXR,aAAaoB,YAAY;EAC3B;CACF,GAAG;EAACtF;EAAQyC;EAAOa;EAAWI;CAAK,CAAC,GAEpC/H,gBAAgB;EACd,IAAM4J,WAAWC,kBAAkB9B,MAAMtB,aAAa,GAAG,GAAI;EAC7D,aAAaqD,cAAcF,QAAQ;CACrC,GAAG,CAAC7B,KAAK,CAAC;CAEV,IAAMgC,QAAQnK,SAAS,CAAC,CAACoK;CAEzB,OACE,oBAAC,cAAD;EAAqBD;EACnB,UAAA,qBAAC,WAAD;GAAkBA;GAAlB,UAAA;IACE,oBAAC,QAAD;KAAeA;KACZtI,UAAAA,eAAeoG,QAAQ,CAAC,CAACvC,KAAKxD,YAC7B,qBAAC,WAAD;MAEE,WAAW;MACX,OAAO,EAACmI,OAAOC,gBAAgBpI,OAAO,CAAC,CAACqI,KAAI;MAH9C,UAAA,CAKE,oBAAC,aAAD,EAAoBJ,MAAM,CAAA,GAC1B,oBAAC,OAAD,EAAA,UAAM9H,cAAcH,OAAO,EAAO,CAAA,CACzB;KANJA,GAAAA,OAMI,CACZ;IACK,CAAA;IACP2F,aAAa,oBAAC,WAAD;KAAkBsC;KAAQpH,UAAAA,SAAS8E,UAAUhF,GAAG;IAAa,CAAA;IAC3E,oBAAC,cAAD;KACE,WAAWsF,MAAM7C;KACjB,iBAAgB;KAChB,gBAAgB;KAChB,cAAckF,SAAc1C,aAAa0C,IAAI;KAC7C,cAAcA,WAAc;MAC1BnC,OAAOoC,eAAe,QAAQ;OAACzG,IAAIwG,OAAK3H,IAAIK;OAAKwH,cAAcF,OAAK3H,IAAIC;MAAK,CAAC;KAChF;KACA,iBAAiB3C,KAAKN,KAAK,IAAI,CAAC8K,KAAK,GAAI;KACzC,iBAAiB;KACjB,aAAa;KACb,UAAUH,WAAcrH,SAASqH,OAAK3H,KAAKO,OAAO;KAClD,oBAAoBwH,KAAKC,gBAAgB;MACvC,KAAK,IAAMvE,WAAW6B,MAAM9C,UAAU;OACpC,IAAMmF,SAAOrC,MAAM7C,KAAKG,MAAMd,MAC3B+E,QAAWA,IAAE7G,OAAO6G,IAAE7G,IAAIK,QAAQoD,SAASzD,KAAKK,GACnD;OACA,IAAIsH,QAAM;QACR,IAAMM,kBAAkBnJ,aAClBwD,QAAQmB,QAAQnB,OAChB4F,SAASrE,KAAKsE,KAAK7H,SAASqH,OAAK3H,KAAKO,OAAO,CAAC,GAC9C0B,QAAQwB,QAAQ5B,KAAKI,OACrBmG,YAAYtD,iBAAiBuD,IAAI5E,QAAQ5B,KAAKyG,WAAW,CAAC,CAACC,MAAM,IAAI,CAACT,KACtEU,WAAWN,SAASF,cAAc,IAClCS,OAAOxG,QAAQA,MAAMyG,QAAQ,GAC7BC,OAAO1G,QAAQA,MAAM2G,SAAS,GAC9BC,IAAIlB,OAAKkB,IAAKhF,KAAKiF,IAAIxG,KAAK,IAAIkG,WAAYR,aAC5Ce,IAAIpB,OAAKoB,IAAKlF,KAAKmF,IAAI1G,KAAK,IAAIkG,WAAYR;QAElDD,IAAIkB,KAAK;QACT,IAAI;SAuBF,IAtBAlB,IAAImB,cAActK,WAChB,IACEiF,KAAKsF,IAAIlB,iBAAiBtE,KAAKC,IAAI,IAAIH,QAAQrB,UAAU,IAAI6F,eACjE,GACAF,IAAIqB,OAAO,QAAQvF,KAAKwF,MAAM,KAAKrB,WAAW,EAAC,gBAE/CD,IAAIuB,UAAU,GACdvB,IAAIwB,cAAcjM,KAAKL,MAAM6K,KAAK,CAAG,GACrCC,IAAIyB,YAAY,IAAIxB,aACpBD,IAAI0B,OACF9B,OAAKkB,IAAKhF,KAAKiF,IAAIxG,KAAK,KAAKkG,WAAWC,OAAO,KAAMT,aACrDL,OAAKoB,IAAKlF,KAAKmF,IAAI1G,KAAK,KAAKkG,WAAWG,OAAO,KAAMX,WACvD,GACAD,IAAI2B,OAAO/B,OAAKkB,IAAIhF,KAAKiF,IAAIxG,KAAK,IAAI4F,QAAQP,OAAKoB,IAAIlF,KAAKmF,IAAI1G,KAAK,IAAI4F,MAAM,GAC/EH,IAAI4B,OAAO,GAEX5B,IAAIuB,UAAU,GACdvB,IAAIwB,cAAcjM,KAAKL,MAAM6K,KAAK,CAAG,GACrCC,IAAIyB,YAAY,IAAIxB,aACpBD,IAAI6B,IAAIjC,OAAKkB,GAAGlB,OAAKoB,GAAGb,QAAQ,GAAG,IAAIrE,KAAKE,IAAI,EAAK,GACrDgE,IAAI4B,OAAO,GAEP1H,OAAO;UACT8F,IAAIkB,KAAK;UACT,IAAI;WACF,IACMa,IAAIjL,WACRgF,KAAK+B,IAAI,IAAIiE,OAAOlG,KAAKC,IAAI,IAAIH,QAAQpB,cAAcwH,GAAG,CAC5D;WA2BA9B,AA1BI+B,IAAI,MACN/B,IAAIuB,UAAU,GACdvB,IAAIgC,YAAYzM,KAAK8K,WAAW0B,CAAC,GACjC/B,IAAI6B,IAAIf,GAAGE,IAAIN,OAAO,IAAI,MAAMT,aAAa,GAAG,IAAInE,KAAKE,IAAI,EAAK,GAClEgE,IAAIL,KAAK,IAGXK,IAAIuB,UAAU,GACdvB,IAAIgC,YAAYzM,KAAKL,MAAM6K,KAAK,CAAG,GACnCC,IAAI6B,IAAIf,GAAGE,GAAGN,OAAOT,cAAc,GAAG,GAAG,IAAInE,KAAKE,IAAI,EAAK,GAC3DgE,IAAIiC,KAAK,GAETjC,IAAIkC,UACFhI,OACA4G,IAAIJ,OAAOT,cAAc,GACzBe,IAAIJ,OAAOX,cAAc,GACzBS,OAAOT,aACPW,OAAOX,WACT,GAEAD,IAAIwB,cAAczM,MAAMgL,KACxBC,IAAIyB,YAAY,IAAIxB,aACpBD,IAAI4B,OAAO,GAEX5B,IAAIwB,cAAcnB,WAClBL,IAAIyB,YAAY,IAAIxB,aACpBD,IAAI4B,OAAO;UACb,UAAU;WACR5B,IAAImC,QAAQ;UACd;SACF;SAMAnC,AAJAA,IAAIuB,UAAU,GACdvB,IAAIwB,cAAcjM,KAAKR,MAAMgL,KAAK,CAAC,GACnCC,IAAIyB,YAAY,KAAMxB,aACtBD,IAAI6B,IAAIf,GAAGE,GAAGN,OAAOT,cAAc,GAAG,GAAG,IAAInE,KAAKE,IAAI,EAAK,GAC3DgE,IAAI4B,OAAO;SAEX,IAAMQ,QAAQ7H,SAASuB,KAAKE,KAAK,KAAKzB,QAAQuB,KAAKE,KAAK,KAClDqG,QAAQD,QACVpB,KAAKJ,OAAO,IAAI,KAAKX,cACrBe,KAAKJ,OAAO,IAAI,KAAKX;SAIzBD,AAHAA,IAAIgC,YAAYzM,KAAKL,MAAM6K,KAAK,CAAG,GACnCC,IAAIsC,YAAY,UAChBtC,IAAIuC,eAAeH,QAAQ,WAAW,OACtCpC,IAAIwC,SAAS9G,QAAQ5B,KAAKyG,aAAaO,GAAGuB,KAAK;QACjD,UAAU;SACRrC,IAAImC,QAAQ;QACd;OACF;MACF;KACF;KACA,mBAAmBvC,QAAWI,OAAKC,kBAAgB;MACjD,IAAQL,OAAK5E,SACN,YAAY;OACf,IAAMyH,YAAY/C,gBAAgBE,OAAK3H,IAAIC,KAAK,GAC1CiI,WAASrE,KAAKsE,KAAK7H,SAASqH,OAAK3H,KAAKO,OAAO,CAAC,GAC9CkK,WAAW5G,KAAKsF,IAAI,KAAK,KAAOnB,aAAW;OAajD,IAXAD,MAAIuB,UAAU,GACdvB,MAAIgC,YACF/E,cAAc,QAAQ2C,OAAK3H,IAAIK,QAAQ2E,UAAUhF,IAAIK,MACjD/C,KAAKN,KAAK,IAAI,CAAC8K,KAAK,EAAG,IACvB0C,UAAU9C,MAChBK,MAAIwB,cAAciB,UAAUE,QAC5B3C,MAAIyB,YAAY,IAChBzB,MAAI6B,IAAIjC,OAAKkB,GAAGlB,OAAKoB,GAAGb,UAAQ,GAAG,IAAIrE,KAAKE,IAAI,EAAK,GACrDgE,MAAI4B,OAAO,GACX5B,MAAIL,KAAK,GAELQ,WAASF,gBAAc,IAAI;QAC7BD,MAAIqB,OAAO,GAAGqB,SAAQ;QACtB,IAAME,IAAIzC,WAAS,IAAI,KAAKF;QAC5B,KAAK,IAAI4C,MAAM,IAAIA,OAAO,GAAGA,OAAO,KAAK;SACvC,IAAMC,QAAQnM,SAASwB,SAASyH,OAAK3H,GAAG,GAAG6D,KAAKwF,MAAMuB,GAAG,CAAC;SAE1D,IADoB7C,MAAIgD,YAAYF,KAChCC,CAAW,CAACpC,QAAQiC,GAAG;UASzB5C,AAPAA,MAAIsC,YAAY,UAChBtC,MAAIuC,eAAe,OAEnBvC,MAAIwB,cAAcjM,KAAKR,MAAMgL,KAAK,EAAG,GACrCC,MAAIyB,YAAY,IAAIxB,eACpBD,MAAIiD,WAAWH,OAAOlD,OAAKkB,GAAGlB,OAAKoB,IAAIb,WAAS,IAAIF,aAAW,GAE/DD,MAAIwC,SAASM,OAAOlD,OAAKkB,GAAGlB,OAAKoB,IAAIb,WAAS,IAAIF,aAAW;UAC7D;SACF;QACF;OACF;MACF;KAEJ;KACA,mBAAmB1E,MAAWyE,OAAKC,kBAAgB;MAMjDD,AALAA,MAAIuB,UAAU,GACdvB,MAAIwB,cAAcjM,KAAKN,KAAK,IAAI,CAAC8K,KAAK,IAAK,GAC3CC,MAAIyB,YAAY,IAAIxB,eACpBD,MAAI0B,OAAOnG,KAAKH,OAAO0F,GAAGvF,KAAKH,OAAO4F,CAAC,GACvChB,MAAI2B,OAAOpG,KAAKF,OAAOyF,GAAGvF,KAAKF,OAAO2F,CAAC,GACvChB,MAAI4B,OAAO;KACb;IAAE,CAAA;GAEK;;CACC,CAAA;AAElB;AAEA,MAAMsB,aAA6D,CAAC;AACpE,IAAIC,eAAe;AAEnB,SAASzD,gBAAgBpI,SAAc;CACrC,IAAI4L,WAAW5L,UACb,OAAO4L,WAAW5L;CAGpB,IAAM8L,MAAMpO,WAAWmO,eAAenO,WAAW+D;CASjD,OAPAoK,gBAAgB,GAEhBD,WAAW5L,WAAW;EACpBqI,MAAMxK,KAAKiO,IAAI,CAAC,IAAI,CAACrD;EACrB4C,QAAQpN,KAAKR,MAAMgL,KAAK,EAAG;CAC7B,GAEOmD,WAAW5L;AACpB"}
|