sanity-plugin-graph-view 2.0.1 → 2.0.2
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 +1 -1
- package/README.md +1 -31
- package/{lib/src → dist}/index.d.ts +4 -4
- package/dist/index.esm.js +1 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -0
- package/package.json +28 -43
- package/src/plugin.tsx +4 -3
- package/src/tool/GraphView.tsx +21 -21
- package/src/tool/GraphViewStyle.tsx +3 -2
- package/src/tool/hooks.ts +4 -6
- package/src/tool/utils.ts +3 -7
- package/lib/index.esm.js +0 -2
- package/lib/index.esm.js.map +0 -1
- package/lib/index.js +0 -2
- package/lib/index.js.map +0 -1
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
<div align="center">
|
|
2
|
-
<img src="assets/sanity-logo.png" width="177" alt="Sanity" />
|
|
3
2
|
<h1>Graph View Plugin</h1>
|
|
4
3
|
<p>A tool for Sanity Studio to graph your content and see changes in real-time.</p>
|
|
5
4
|
<p><img src="assets/screengrab.gif" width="540" alt="Screengrab of the Graph tool" /></p>
|
|
6
5
|
</div>
|
|
7
6
|
|
|
8
|
-
> This is a **Sanity Studio v3** plugin.
|
|
9
|
-
> For the v2 version, please refer to the [v2-branch](https://github.com/sanity-io/sanity-plugin-graph-view/tree/studio-v2).
|
|
10
7
|
|
|
11
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!
|
|
12
9
|
|
|
@@ -18,12 +15,6 @@ Wonder how a visualization of your dataset will look? How many authors do you ha
|
|
|
18
15
|
npm install --save sanity-plugin-graph-view
|
|
19
16
|
```
|
|
20
17
|
|
|
21
|
-
or
|
|
22
|
-
|
|
23
|
-
```
|
|
24
|
-
yarn add sanity-plugin-graph-view
|
|
25
|
-
```
|
|
26
|
-
|
|
27
18
|
|
|
28
19
|
## Usage
|
|
29
20
|
|
|
@@ -32,7 +23,7 @@ Add it as a plugin in sanity.config.ts (or .js):
|
|
|
32
23
|
```js
|
|
33
24
|
import { contentGraphView } from "sanity-plugin-graph-view";
|
|
34
25
|
|
|
35
|
-
export default
|
|
26
|
+
export default defineConfig({
|
|
36
27
|
// ...
|
|
37
28
|
plugins: [
|
|
38
29
|
contentGraphView({}),
|
|
@@ -87,27 +78,6 @@ contentGraphView({
|
|
|
87
78
|
})
|
|
88
79
|
```
|
|
89
80
|
|
|
90
|
-
## Get help in the Sanity Community
|
|
91
|
-
|
|
92
|
-
[](https://slack.sanity.io/)
|
|
93
|
-
|
|
94
|
-
Join [Sanity’s developer community](https://slack.sanity.io) or ping us [on twitter](https://twitter.com/sanity_io).
|
|
95
|
-
|
|
96
81
|
## License
|
|
97
82
|
|
|
98
83
|
MIT-licensed. See LICENSE.
|
|
99
|
-
|
|
100
|
-
## Develop & test
|
|
101
|
-
|
|
102
|
-
This plugin uses [@sanity/plugin-kit](https://github.com/sanity-io/plugin-kit)
|
|
103
|
-
with default configuration for build & watch scripts.
|
|
104
|
-
|
|
105
|
-
See [Testing a plugin in Sanity Studio](https://github.com/sanity-io/plugin-kit#testing-a-plugin-in-sanity-studio)
|
|
106
|
-
on how to run this plugin with hotreload in the studio.
|
|
107
|
-
|
|
108
|
-
### Release new version
|
|
109
|
-
|
|
110
|
-
Run ["CI & Release" workflow](https://github.com/sanity-io/sanity-plugin-graph-view/actions/workflows/main.yml).
|
|
111
|
-
Make sure to select the v3 branch and check "Release new version".
|
|
112
|
-
|
|
113
|
-
Semantic release will only release on configured branches, so it is safe to run release on any branch.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {Plugin as Plugin_2} from
|
|
1
|
+
import { Plugin as Plugin_2 } from "sanity";
|
|
2
2
|
|
|
3
|
-
export declare const contentGraphView: Plugin_2<GraphViewConfig
|
|
3
|
+
export declare const contentGraphView: Plugin_2<GraphViewConfig>;
|
|
4
4
|
|
|
5
5
|
export declare interface GraphViewConfig {
|
|
6
|
-
query?: string
|
|
6
|
+
query?: string;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
export {}
|
|
9
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var t,e,n,o,i,r;function s(t,e){return e||(e=t.slice(0)),Object.freeze(Object.defineProperties(t,{raw:{value:Object.freeze(e)}}))}import{jsx as a,jsxs as l}from"react/jsx-runtime";import{useUserColorManager as c,useClient as d,definePlugin as h}from"sanity";import{useRouter as u,route as f}from"sanity/router";import p from"bezier-easing";import m from"deep-equal";import{rgba as y}from"polished";import{useEffect as g,useState as b,useCallback as v}from"react";import{ForceGraph2D as x}from"react-force-graph";import{v4 as _}from"uuid";import{black as w,gray as k,white as M,hues as I,COLOR_HUES as j}from"@sanity/color";import{useTheme as P}from"@sanity/ui";import O from"styled-components";const q=O.div(t||(t=s(["\n font-family: ",";\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: ",";\n"])),t=>{let{theme:e}=t;return e.fonts.text.family},w.hex),S=O.div(e||(e=s(["\n position: relative;\n width: 100%;\n height: 100%;\n"]))),A=O.div(n||(n=s(["\n font-family: ",";\n display: none;\n position: absolute;\n bottom: ","px;\n left: 50%;\n transform: translate3d(-50%, 0, 0);\n background: var(--component-bg);\n border-radius: ","px;\n padding: ","px;\n z-index: 1000;\n\n &:empty {\n display: none;\n }\n"])),t=>{let{theme:e}=t;return e.fonts.text.family},t=>{let{theme:e}=t;return e.space[0]},t=>{let{theme:e}=t;return e.radius[2]},t=>{let{theme:e}=t;return e.space[2]}),T=O.div(o||(o=s(["\n color: #ccc;\n position: absolute;\n top: ","px;\n left: ","px;\n\n & > div {\n margin: 5px 0;\n }\n"])),t=>{let{theme:e}=t;return e.space[4]},t=>{let{theme:e}=t;return e.space[4]}),W=O.div(i||(i=s(["\n display: flex;\n"]))),C=O.div(r||(r=s(["\n width: 1.25em;\n height: 1.25em;\n background: currentColor;\n border-radius: 50%;\n margin-right: ","px;\n"])),t=>{let{theme:e}=t;return e.space[2]});function D(t){return null===t?0:Array.isArray(t)?Object.entries(t).reduce((t,e)=>t+D(e),0):"object"==typeof t?Object.entries(t).reduce((t,e)=>{let[n,o]=e;return t+D(n)+D(o)},0):"string"==typeof t?t.length:1}function V(t,e){return t.length>e?"".concat(t.substring(0,e),"…"):t}const z=p(0,.9,1,1),B=p(.25,.1,0,1);function N(t){return(t.substring(0,1).toUpperCase()+t.substring(1)).replace(/\./g," ").replace(/[A-Z]/g," $&").trim()}function H(t){const e={};for(const n of t)e[n._type]=(e[n._type]||0)+1;return e}function R(t){return"".concat(t.title||t.name||t._id).trim()}function U(t,e){return 5+D(t)/e*100}function E(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[];if(null!==t)if("object"==typeof t)for(const[n,o]of Object.entries(t))"_ref"===n&&"string"==typeof o&&o.length>0&&e.push(F(o)),E(o,e);else if(Array.isArray(t))for(const n of t)E(n,e);return e}function F(t){return t.replace(/^drafts\./,"")}class G{constructor(){this.id=null,this.user=null,this.doc=null,this.lastActive=0,this.startTime=0,this.angle=0}}class L{constructor(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.sessions=[];const e={};for(const n of t)e[n._id]=n;this.data={nodes:t.map(t=>Object.assign({id:t._id,type:"document",doc:t})),links:t.flatMap(t=>E(t).map(e=>({source:t._id,target:e}))).filter(t=>e[t.source]&&e[t.target])}}setSession(t,e){let n=this.sessions.find(n=>{var o;return n.user.id===t.id&&(null==(o=n.doc)?void 0:o._id)===e.doc._id});n||(n=new G,n.id=_(),n.user=t,n.startTime=Date.now(),n.doc=e.doc,n.angle=2*Math.random()*Math.PI,this.sessions.push(n)),n.lastActive=Date.now()}reapSessions(){for(let t=0;t<this.sessions.length;t++){const e=this.sessions[t];Date.now()-e.lastActive>1e4&&(this.sessions=[...this.sessions.slice(0,t),...this.sessions.slice(t+1)],t--)}}clone(){const t=new L;return Object.assign(t,this),t.data={nodes:[...this.data.nodes],links:[...this.data.links]},t}}const Z=new class{constructor(){this._users=[]}async getById(t,e){let n=this._users.find(e=>e._id===t);var o,i,r;return n||(n=await e.users.getById(t),this._users.push(n),n.image=await(o=n.imageUrl||"https://raw.githubusercontent.com/sanity-io/sanity-plugin-graph-view/main/assets/head-silhouette.jpg",i=40,r=40,new Promise(t=>{const e=new Image(i,r);e.onload=()=>{t(e)},e.onerror=e=>{console.log("Image error",e),t(null)},e.src=o}))),n}};function $(t){var e;const n=t.query||'\n *[\n !(_id in path("_.*")) &&\n !(_type match "system.*") &&\n !(_type match "sanity.*")\n ]\n',o=null!=(e=t.apiVersion)?e:"2022-09-01",i=c(),[r,s]=b(0),[h,f]=b(null),[p,_]=b([]),[I,j]=b({}),[O,G]=b(()=>new L),$=u(),J=d({apiVersion:o}),K=v(t=>{const e=function(t){const e={};for(const n of t)n._id.startsWith("drafts.")||(e[n._id]=n);for(const n of t)if(n._id.startsWith("drafts.")){const t=F(n._id);e[t]=Object.assign(n,{_id:t})}return Object.values(e)}(t);s(Math.max(...e.map(D))),_(e),j(H(e)),G(new L(e))},[]),X=v(async t=>{const e=t.result;if(e){e._id=F(e._id);const n={};for(const t of p)n[t._id]=t;let o;const i=[...p],r=p.findIndex(t=>t._id===e._id);r>=0?(o=i[r],i[r]=e):i.push(e),_(i),j(H(i)),s(Math.max(...i.map(D)));const a=O.clone(),l=E(o||{}).filter(t=>t===e._id||null!==n[t]),c=E(e).filter(t=>t===e._id||null!==n[t]);let d,h=!m(l,c);h&&(a.data.links=a.data.links.filter(t=>t.source.id!==e._id).concat(c.map(t=>({source:e._id,target:t}))));const u=O.data.nodes.findIndex(t=>t.doc&&t.doc._id===e._id);u>=0?(d=O.data.nodes[u],d.doc=e):(d={id:e._id,type:"document",doc:e},a.data.nodes.push(d),h=!0),h&&G(a);const f=await Z.getById(t.identity,J);O.setSession(f,d)}else if("disappear"===t.transition){const e=F(t.documentId),n=p.filter(t=>t._id!==e);_(n),j(H(n)),s(Math.max(...n.map(D)));const o=O.clone();o.data.links=o.data.links.filter(t=>t.source.id!==e&&t.target.id!==e),o.data.nodes=o.data.nodes.filter(t=>t.id!==e),G(o)}},[p,O,J]);!function(t,e,n,o){g(()=>{o.fetch(t).then(t=>{e(t)})},[...n,o])}(n,K,[],J),function(t,e,n,o,i,r){g(()=>{const i=r.listen(t,e,n).subscribe(t=>{o(t)});return()=>{i.unsubscribe()}},[...i,r])}(n,{},{},X,[p,O],J),g(()=>{const t=setInterval(()=>O.reapSessions(),1e3);return()=>clearInterval(t)},[O]);const Y=P().sanity;return a(S,{theme:Y,children:l(q,{theme:Y,children:[a(T,{theme:Y,children:(tt=I,(et=Object.keys(tt),nt=t=>tt[t]||0,et.sort((t,e)=>{const n=nt(t),o=nt(e);return n<o?-1:n>o?1:0})).reverse().slice(0,10)).map(t=>l(W,{className:"legend__row",style:{color:Q(t).fill},children:[a(C,{theme:Y}),a("div",{children:N(t)})]},t))}),h&&a(A,{theme:Y,children:R(h.doc)}),a(x,{graphData:O.data,nodeAutoColorBy:"group",enableNodeDrag:!1,onNodeHover:t=>f(t),onNodeClick:t=>{$.navigateIntent("edit",{id:t.doc._id,documentType:t.doc._type})},linkColor:()=>y(k[500].hex,.25),nodeLabel:()=>"",nodeRelSize:1,nodeVal:t=>U(t.doc,r),onRenderFramePost:(t,e)=>{for(const n of O.sessions){const o=O.data.nodes.find(t=>{var e;return t.doc&&t.doc._id===(null==(e=null==n?void 0:n.doc)?void 0:e._id)});if(o){const s=1e4,a=n.angle,l=Math.sqrt(U(o.doc,r)),c=n.user.image,d=i.get(n.user.displayName).tints[400].hex,h=l*e+40,u=c?c.width:0,f=c?c.height:0,p=o.x+Math.sin(a)*h/e,m=o.y+Math.cos(a)*h/e;t.save();try{if(t.globalAlpha=z(1-Math.min(s,Date.now()-n.lastActive)/s),t.font="bold ".concat(Math.round(12/e),"px sans-serif"),t.beginPath(),t.strokeStyle=y(M.hex,1),t.lineWidth=2/e,t.moveTo(o.x+Math.sin(a)*(h-u/2)/e,o.y+Math.cos(a)*(h-f/2)/e),t.lineTo(o.x+Math.sin(a)*l,o.y+Math.cos(a)*l),t.stroke(),t.beginPath(),t.strokeStyle=y(M.hex,1),t.lineWidth=2/e,t.arc(o.x,o.y,l,0,2*Math.PI,!1),t.stroke(),c){t.save();try{const o=700,i=B(Math.max(0,(o-(Date.now()-n.startTime))/o));i>0&&(t.beginPath(),t.fillStyle=y(d,i),t.arc(p,m,(u/2+10)/e,0,2*Math.PI,!1),t.fill()),t.beginPath(),t.fillStyle=y(M.hex,1),t.arc(p,m,u/e/2,0,2*Math.PI,!1),t.clip(),t.drawImage(c,p-u/e/2,m-f/e/2,u/e,f/e),t.strokeStyle=w.hex,t.lineWidth=6/e,t.stroke(),t.strokeStyle=d,t.lineWidth=4/e,t.stroke()}finally{t.restore()}}t.beginPath(),t.strokeStyle=y(w.hex,1),t.lineWidth=.5/e,t.arc(p,m,u/e/2,0,2*Math.PI,!1),t.stroke();const i=a>=Math.PI/2&&a<1.5*Math.PI,r=i?m-(f/2+5)/e:m+(f/2+5)/e;t.fillStyle=y(M.hex,1),t.textAlign="center",t.textBaseline=i?"bottom":"top",t.fillText(n.user.displayName,p,r)}finally{t.restore()}}}},nodeCanvasObject:(t,e,n)=>{if("document"===t.type){const o=Q(t.doc._type),i=Math.sqrt(U(t.doc,r)),s=Math.min(100,10/n);if(e.beginPath(),e.fillStyle=null!==h&&t.doc._id===h.doc._id?y(k[500].hex,.8):o.fill,e.strokeStyle=o.border,e.lineWidth=.5,e.arc(t.x,t.y,i,0,2*Math.PI,!1),e.stroke(),e.fill(),i*n>10){e.font="".concat(s,"px sans-serif");const o=2*i+30/n;for(let r=50;r>=5;r/=1.2){const s=V(R(t.doc),Math.round(r));if(e.measureText(s).width<o){e.textAlign="center",e.textBaseline="top",e.strokeStyle=y(w.hex,.5),e.lineWidth=2/n,e.strokeText(s,t.x,t.y+i+5/n),e.fillText(s,t.x,t.y+i+5/n);break}}}}},linkCanvasObject:(t,e,n)=>{e.beginPath(),e.strokeStyle=y(k[500].hex,.125),e.lineWidth=2/n,e.moveTo(t.source.x,t.source.y),e.lineTo(t.target.x,t.target.y),e.stroke()}})]})});var tt,et,nt}const J={};let K=0;function Q(t){if(J[t])return J[t];const e=j[K%j.length];return K+=1,J[t]={fill:I[e][400].hex,border:y(w.hex,.5)},J[t]}const X=()=>a("svg",{xmlns:"http://www.w3.org/2000/svg",width:"1em",height:"1em",viewBox:"0 0 46.063 46.063",children:a("path",{fill:"currentColor",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"})});var Y=Object.defineProperty,tt=Object.getOwnPropertySymbols,et=Object.prototype.hasOwnProperty,nt=Object.prototype.propertyIsEnumerable,ot=(t,e,n)=>e in t?Y(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;const it=h(function(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{name:"@sanity/content-graph-view",tools:e=>[...e,{name:"graph-your-content",title:"Graph",icon:X,component:function(){return a($,((t,e)=>{for(var n in e||(e={}))et.call(e,n)&&ot(t,n,e[n]);if(tt)for(var n of tt(e))nt.call(e,n)&&ot(t,n,e[n]);return t})({},t))},router:f.create("/:selectedDocumentId")}]}});export{it as contentGraphView};//# sourceMappingURL=index.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var e,t,n,r,s,i;function o(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}Object.defineProperty(exports,"__esModule",{value:!0});var a=require("react/jsx-runtime"),l=require("sanity"),c=require("sanity/router"),d=require("bezier-easing"),u=require("deep-equal"),h=require("polished"),f=require("react"),p=require("react-force-graph"),g=require("uuid"),y=require("@sanity/color"),m=require("@sanity/ui"),b=require("styled-components");function x(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var v=x(d),_=x(u),w=x(b);const k=w.default.div(e||(e=o(["\n font-family: ",";\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: ",";\n"])),e=>{let{theme:t}=e;return t.fonts.text.family},y.black.hex),j=w.default.div(t||(t=o(["\n position: relative;\n width: 100%;\n height: 100%;\n"]))),q=w.default.div(n||(n=o(["\n font-family: ",";\n display: none;\n position: absolute;\n bottom: ","px;\n left: 50%;\n transform: translate3d(-50%, 0, 0);\n background: var(--component-bg);\n border-radius: ","px;\n padding: ","px;\n z-index: 1000;\n\n &:empty {\n display: none;\n }\n"])),e=>{let{theme:t}=e;return t.fonts.text.family},e=>{let{theme:t}=e;return t.space[0]},e=>{let{theme:t}=e;return t.radius[2]},e=>{let{theme:t}=e;return t.space[2]}),M=w.default.div(r||(r=o(["\n color: #ccc;\n position: absolute;\n top: ","px;\n left: ","px;\n\n & > div {\n margin: 5px 0;\n }\n"])),e=>{let{theme:t}=e;return t.space[4]},e=>{let{theme:t}=e;return t.space[4]}),S=w.default.div(s||(s=o(["\n display: flex;\n"]))),O=w.default.div(i||(i=o(["\n width: 1.25em;\n height: 1.25em;\n background: currentColor;\n border-radius: 50%;\n margin-right: ","px;\n"])),e=>{let{theme:t}=e;return t.space[2]});function P(e){return null===e?0:Array.isArray(e)?Object.entries(e).reduce((e,t)=>e+P(t),0):"object"==typeof e?Object.entries(e).reduce((e,t)=>{let[n,r]=t;return e+P(n)+P(r)},0):"string"==typeof e?e.length:1}function I(e,t){return e.length>t?"".concat(e.substring(0,t),"…"):e}const C=v.default(0,.9,1,1),A=v.default(.25,.1,0,1);function T(e){return(e.substring(0,1).toUpperCase()+e.substring(1)).replace(/\./g," ").replace(/[A-Z]/g," $&").trim()}function W(e){const t={};for(const n of e)t[n._type]=(t[n._type]||0)+1;return t}function D(e){return"".concat(e.title||e.name||e._id).trim()}function V(e,t){return 5+P(e)/t*100}function z(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[];if(null!==e)if("object"==typeof e)for(const[n,r]of Object.entries(e))"_ref"===n&&"string"==typeof r&&r.length>0&&t.push(B(r)),z(r,t);else if(Array.isArray(e))for(const n of e)z(n,t);return t}function B(e){return e.replace(/^drafts\./,"")}class E{constructor(){this.id=null,this.user=null,this.doc=null,this.lastActive=0,this.startTime=0,this.angle=0}}class N{constructor(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.sessions=[];const t={};for(const n of e)t[n._id]=n;this.data={nodes:e.map(e=>Object.assign({id:e._id,type:"document",doc:e})),links:e.flatMap(e=>z(e).map(t=>({source:e._id,target:t}))).filter(e=>t[e.source]&&t[e.target])}}setSession(e,t){let n=this.sessions.find(n=>{var r;return n.user.id===e.id&&(null==(r=n.doc)?void 0:r._id)===t.doc._id});n||(n=new E,n.id=g.v4(),n.user=e,n.startTime=Date.now(),n.doc=t.doc,n.angle=2*Math.random()*Math.PI,this.sessions.push(n)),n.lastActive=Date.now()}reapSessions(){for(let e=0;e<this.sessions.length;e++){const t=this.sessions[e];Date.now()-t.lastActive>1e4&&(this.sessions=[...this.sessions.slice(0,e),...this.sessions.slice(e+1)],e--)}}clone(){const e=new N;return Object.assign(e,this),e.data={nodes:[...this.data.nodes],links:[...this.data.links]},e}}const R=new class{constructor(){this._users=[]}async getById(e,t){let n=this._users.find(t=>t._id===e);var r,s,i;return n||(n=await t.users.getById(e),this._users.push(n),n.image=await(r=n.imageUrl||"https://raw.githubusercontent.com/sanity-io/sanity-plugin-graph-view/main/assets/head-silhouette.jpg",s=40,i=40,new Promise(e=>{const t=new Image(s,i);t.onload=()=>{e(t)},t.onerror=t=>{console.log("Image error",t),e(null)},t.src=r}))),n}};function U(e){var t;const n=e.query||'\n *[\n !(_id in path("_.*")) &&\n !(_type match "system.*") &&\n !(_type match "sanity.*")\n ]\n',r=null!=(t=e.apiVersion)?t:"2022-09-01",s=l.useUserColorManager(),[i,o]=f.useState(0),[d,u]=f.useState(null),[g,b]=f.useState([]),[x,v]=f.useState({}),[w,E]=f.useState(()=>new N),U=c.useRouter(),H=l.useClient({apiVersion:r}),G=f.useCallback(e=>{const t=function(e){const t={};for(const n of e)n._id.startsWith("drafts.")||(t[n._id]=n);for(const n of e)if(n._id.startsWith("drafts.")){const e=B(n._id);t[e]=Object.assign(n,{_id:e})}return Object.values(t)}(e);o(Math.max(...t.map(P))),b(t),v(W(t)),E(new N(t))},[]),F=f.useCallback(async e=>{const t=e.result;if(t){t._id=B(t._id);const n={};for(const e of g)n[e._id]=e;let r;const s=[...g],i=g.findIndex(e=>e._id===t._id);i>=0?(r=s[i],s[i]=t):s.push(t),b(s),v(W(s)),o(Math.max(...s.map(P)));const a=w.clone(),l=z(r||{}).filter(e=>e===t._id||null!==n[e]),c=z(t).filter(e=>e===t._id||null!==n[e]);let d,u=!_.default(l,c);u&&(a.data.links=a.data.links.filter(e=>e.source.id!==t._id).concat(c.map(e=>({source:t._id,target:e}))));const h=w.data.nodes.findIndex(e=>e.doc&&e.doc._id===t._id);h>=0?(d=w.data.nodes[h],d.doc=t):(d={id:t._id,type:"document",doc:t},a.data.nodes.push(d),u=!0),u&&E(a);const f=await R.getById(e.identity,H);w.setSession(f,d)}else if("disappear"===e.transition){const t=B(e.documentId),n=g.filter(e=>e._id!==t);b(n),v(W(n)),o(Math.max(...n.map(P)));const r=w.clone();r.data.links=r.data.links.filter(e=>e.source.id!==t&&e.target.id!==t),r.data.nodes=r.data.nodes.filter(e=>e.id!==t),E(r)}},[g,w,H]);!function(e,t,n,r){f.useEffect(()=>{r.fetch(e).then(e=>{t(e)})},[...n,r])}(n,G,[],H),function(e,t,n,r,s,i){f.useEffect(()=>{const s=i.listen(e,t,n).subscribe(e=>{r(e)});return()=>{s.unsubscribe()}},[...s,i])}(n,{},{},F,[g,w],H),f.useEffect(()=>{const e=setInterval(()=>w.reapSessions(),1e3);return()=>clearInterval(e)},[w]);const Z=m.useTheme().sanity;return a.jsx(j,{theme:Z,children:a.jsxs(k,{theme:Z,children:[a.jsx(M,{theme:Z,children:($=x,(J=Object.keys($),K=e=>$[e]||0,J.sort((e,t)=>{const n=K(e),r=K(t);return n<r?-1:n>r?1:0})).reverse().slice(0,10)).map(e=>a.jsxs(S,{className:"legend__row",style:{color:L(e).fill},children:[a.jsx(O,{theme:Z}),a.jsx("div",{children:T(e)})]},e))}),d&&a.jsx(q,{theme:Z,children:D(d.doc)}),a.jsx(p.ForceGraph2D,{graphData:w.data,nodeAutoColorBy:"group",enableNodeDrag:!1,onNodeHover:e=>u(e),onNodeClick:e=>{U.navigateIntent("edit",{id:e.doc._id,documentType:e.doc._type})},linkColor:()=>h.rgba(y.gray[500].hex,.25),nodeLabel:()=>"",nodeRelSize:1,nodeVal:e=>V(e.doc,i),onRenderFramePost:(e,t)=>{for(const n of w.sessions){const r=w.data.nodes.find(e=>{var t;return e.doc&&e.doc._id===(null==(t=null==n?void 0:n.doc)?void 0:t._id)});if(r){const o=1e4,a=n.angle,l=Math.sqrt(V(r.doc,i)),c=n.user.image,d=s.get(n.user.displayName).tints[400].hex,u=l*t+40,f=c?c.width:0,p=c?c.height:0,g=r.x+Math.sin(a)*u/t,m=r.y+Math.cos(a)*u/t;e.save();try{if(e.globalAlpha=C(1-Math.min(o,Date.now()-n.lastActive)/o),e.font="bold ".concat(Math.round(12/t),"px sans-serif"),e.beginPath(),e.strokeStyle=h.rgba(y.white.hex,1),e.lineWidth=2/t,e.moveTo(r.x+Math.sin(a)*(u-f/2)/t,r.y+Math.cos(a)*(u-p/2)/t),e.lineTo(r.x+Math.sin(a)*l,r.y+Math.cos(a)*l),e.stroke(),e.beginPath(),e.strokeStyle=h.rgba(y.white.hex,1),e.lineWidth=2/t,e.arc(r.x,r.y,l,0,2*Math.PI,!1),e.stroke(),c){e.save();try{const r=700,s=A(Math.max(0,(r-(Date.now()-n.startTime))/r));s>0&&(e.beginPath(),e.fillStyle=h.rgba(d,s),e.arc(g,m,(f/2+10)/t,0,2*Math.PI,!1),e.fill()),e.beginPath(),e.fillStyle=h.rgba(y.white.hex,1),e.arc(g,m,f/t/2,0,2*Math.PI,!1),e.clip(),e.drawImage(c,g-f/t/2,m-p/t/2,f/t,p/t),e.strokeStyle=y.black.hex,e.lineWidth=6/t,e.stroke(),e.strokeStyle=d,e.lineWidth=4/t,e.stroke()}finally{e.restore()}}e.beginPath(),e.strokeStyle=h.rgba(y.black.hex,1),e.lineWidth=.5/t,e.arc(g,m,f/t/2,0,2*Math.PI,!1),e.stroke();const s=a>=Math.PI/2&&a<1.5*Math.PI,i=s?m-(p/2+5)/t:m+(p/2+5)/t;e.fillStyle=h.rgba(y.white.hex,1),e.textAlign="center",e.textBaseline=s?"bottom":"top",e.fillText(n.user.displayName,g,i)}finally{e.restore()}}}},nodeCanvasObject:(e,t,n)=>{if("document"===e.type){const r=L(e.doc._type),s=Math.sqrt(V(e.doc,i)),o=Math.min(100,10/n);if(t.beginPath(),t.fillStyle=null!==d&&e.doc._id===d.doc._id?h.rgba(y.gray[500].hex,.8):r.fill,t.strokeStyle=r.border,t.lineWidth=.5,t.arc(e.x,e.y,s,0,2*Math.PI,!1),t.stroke(),t.fill(),s*n>10){t.font="".concat(o,"px sans-serif");const r=2*s+30/n;for(let i=50;i>=5;i/=1.2){const o=I(D(e.doc),Math.round(i));if(t.measureText(o).width<r){t.textAlign="center",t.textBaseline="top",t.strokeStyle=h.rgba(y.black.hex,.5),t.lineWidth=2/n,t.strokeText(o,e.x,e.y+s+5/n),t.fillText(o,e.x,e.y+s+5/n);break}}}}},linkCanvasObject:(e,t,n)=>{t.beginPath(),t.strokeStyle=h.rgba(y.gray[500].hex,.125),t.lineWidth=2/n,t.moveTo(e.source.x,e.source.y),t.lineTo(e.target.x,e.target.y),t.stroke()}})]})});var $,J,K}const H={};let G=0;function L(e){if(H[e])return H[e];const t=y.COLOR_HUES[G%y.COLOR_HUES.length];return G+=1,H[e]={fill:y.hues[t][400].hex,border:h.rgba(y.black.hex,.5)},H[e]}const F=()=>a.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",width:"1em",height:"1em",viewBox:"0 0 46.063 46.063",children:a.jsx("path",{fill:"currentColor",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"})});var Z=Object.defineProperty,$=Object.getOwnPropertySymbols,J=Object.prototype.hasOwnProperty,K=Object.prototype.propertyIsEnumerable,Q=(e,t,n)=>t in e?Z(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;const X=l.definePlugin(function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{name:"@sanity/content-graph-view",tools:t=>[...t,{name:"graph-your-content",title:"Graph",icon:F,component:function(){return a.jsx(U,((e,t)=>{for(var n in t||(t={}))J.call(t,n)&&Q(e,n,t[n]);if($)for(var n of $(t))K.call(t,n)&&Q(e,n,t[n]);return e})({},e))},router:c.route.create("/:selectedDocumentId")}]}});exports.contentGraphView=X;//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,50 +1,38 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sanity-plugin-graph-view",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "",
|
|
5
|
-
"homepage": "https://github.com/sanity-io/sanity-plugin-graph-view#readme",
|
|
5
|
+
"homepage": "https://github.com/sanity-io/plugins/tree/main/plugins/sanity-plugin-graph-view#readme",
|
|
6
6
|
"bugs": {
|
|
7
|
-
"url": "https://github.com/sanity-io/
|
|
7
|
+
"url": "https://github.com/sanity-io/plugins/issues"
|
|
8
8
|
},
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
11
|
-
"url": "git@github.com
|
|
11
|
+
"url": "git+ssh://git@github.com/sanity-io/plugins.git",
|
|
12
|
+
"directory": "plugins/sanity-plugin-graph-view"
|
|
12
13
|
},
|
|
13
14
|
"license": "MIT",
|
|
14
15
|
"author": "Sanity.io <hello@sanity.io>",
|
|
15
16
|
"exports": {
|
|
16
17
|
".": {
|
|
17
|
-
"types": "./
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
18
19
|
"source": "./src/index.ts",
|
|
19
|
-
"import": "./
|
|
20
|
-
"require": "./
|
|
21
|
-
"default": "./
|
|
20
|
+
"import": "./dist/index.esm.js",
|
|
21
|
+
"require": "./dist/index.js",
|
|
22
|
+
"default": "./dist/index.esm.js"
|
|
22
23
|
},
|
|
23
24
|
"./package.json": "./package.json"
|
|
24
25
|
},
|
|
25
|
-
"main": "./
|
|
26
|
-
"module": "./
|
|
26
|
+
"main": "./dist/index.js",
|
|
27
|
+
"module": "./dist/index.esm.js",
|
|
27
28
|
"source": "./src/index.ts",
|
|
28
|
-
"types": "./
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
29
30
|
"files": [
|
|
30
31
|
"src",
|
|
31
|
-
"
|
|
32
|
+
"dist",
|
|
32
33
|
"v2-incompatible.js",
|
|
33
34
|
"sanity.json"
|
|
34
35
|
],
|
|
35
|
-
"scripts": {
|
|
36
|
-
"prebuild": "npm run clean && plugin-kit verify-package --silent && pkg-utils",
|
|
37
|
-
"build": "pkg-utils build --strict",
|
|
38
|
-
"clean": "rimraf lib",
|
|
39
|
-
"compile": "tsc --noEmit",
|
|
40
|
-
"format": "prettier src -w",
|
|
41
|
-
"link-watch": "plugin-kit link-watch",
|
|
42
|
-
"lint": "eslint .",
|
|
43
|
-
"lint:fix": "eslint . --fix",
|
|
44
|
-
"prepare": "husky install",
|
|
45
|
-
"prepublishOnly": "npm run build",
|
|
46
|
-
"watch": "pkg-utils watch"
|
|
47
|
-
},
|
|
48
36
|
"dependencies": {
|
|
49
37
|
"@sanity/client": "^3.3.0",
|
|
50
38
|
"@sanity/color": "^2.1.19",
|
|
@@ -53,31 +41,16 @@
|
|
|
53
41
|
"bezier-easing": "^2.1.0",
|
|
54
42
|
"deep-equal": "^2.0.5",
|
|
55
43
|
"polished": "^4.2.2",
|
|
56
|
-
"react-force-graph": "
|
|
44
|
+
"react-force-graph": "1.41.14",
|
|
57
45
|
"styled-components": "^5.3.5",
|
|
58
46
|
"uuid": "^8.3.2"
|
|
59
47
|
},
|
|
60
48
|
"devDependencies": {
|
|
61
|
-
"@commitlint/cli": "^17.2.0",
|
|
62
|
-
"@commitlint/config-conventional": "^17.2.0",
|
|
63
49
|
"@sanity/pkg-utils": "^1.17.2",
|
|
64
50
|
"@sanity/plugin-kit": "^2.1.6",
|
|
65
|
-
"@sanity/semantic-release-preset": "^2.0.2",
|
|
66
51
|
"@types/deep-equal": "^1.0.1",
|
|
67
52
|
"@types/react": "^18",
|
|
68
53
|
"@types/styled-components": "^5.1.25",
|
|
69
|
-
"@typescript-eslint/eslint-plugin": "^5.42.0",
|
|
70
|
-
"@typescript-eslint/parser": "^5.42.0",
|
|
71
|
-
"eslint": "^8.26.0",
|
|
72
|
-
"eslint-config-prettier": "^8.5.0",
|
|
73
|
-
"eslint-config-sanity": "^6.0.0",
|
|
74
|
-
"eslint-plugin-prettier": "^4.2.1",
|
|
75
|
-
"eslint-plugin-react": "^7.31.10",
|
|
76
|
-
"eslint-plugin-react-hooks": "^4.6.0",
|
|
77
|
-
"husky": "^8.0.1",
|
|
78
|
-
"lint-staged": "^13.0.3",
|
|
79
|
-
"prettier": "^2.7.1",
|
|
80
|
-
"prettier-plugin-packagejson": "^2.3.0",
|
|
81
54
|
"react": "^18",
|
|
82
55
|
"rimraf": "^3.0.2",
|
|
83
56
|
"sanity": "^3.0.0",
|
|
@@ -90,5 +63,17 @@
|
|
|
90
63
|
"engines": {
|
|
91
64
|
"node": ">=14"
|
|
92
65
|
},
|
|
93
|
-
"sanityExchangeUrl": "https://www.sanity.io/plugins/graph-view"
|
|
94
|
-
|
|
66
|
+
"sanityExchangeUrl": "https://www.sanity.io/plugins/graph-view",
|
|
67
|
+
"sanityPlugin": {
|
|
68
|
+
"verifyPackage": {
|
|
69
|
+
"eslintImports": false,
|
|
70
|
+
"scripts": false,
|
|
71
|
+
"tsc": false
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"scripts": {
|
|
75
|
+
"prebuild": "npm run clean && plugin-kit verify-package --silent && pkg-utils",
|
|
76
|
+
"build": "pkg-utils build --strict",
|
|
77
|
+
"clean": "rimraf dist"
|
|
78
|
+
}
|
|
79
|
+
}
|
package/src/plugin.tsx
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {GraphViewIcon} from './tool/GraphViewIcon'
|
|
1
|
+
import React from 'react'
|
|
3
2
|
import {definePlugin} from 'sanity'
|
|
4
3
|
import {route} from 'sanity/router'
|
|
5
|
-
|
|
4
|
+
|
|
5
|
+
import {GraphView} from './tool/GraphView'
|
|
6
|
+
import {GraphViewIcon} from './tool/GraphViewIcon'
|
|
6
7
|
|
|
7
8
|
export interface GraphViewConfig {
|
|
8
9
|
query?: string
|
package/src/tool/GraphView.tsx
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
import
|
|
1
|
+
import BezierEasing from 'bezier-easing'
|
|
2
2
|
import deepEqual from 'deep-equal'
|
|
3
|
+
import {rgba} from 'polished'
|
|
3
4
|
import React, {useCallback, useEffect, useState} from 'react'
|
|
4
5
|
import {ForceGraph2D} from 'react-force-graph'
|
|
5
|
-
import {v4 as uuidv4} from 'uuid'
|
|
6
|
-
import BezierEasing from 'bezier-easing'
|
|
7
|
-
import {useFetchDocuments, useListen} from './hooks'
|
|
8
|
-
import {sortBy, loadImage, sizeOf, truncate} from './utils'
|
|
9
|
-
import {SanityDocument, SanityClient} from '@sanity/client'
|
|
10
6
|
import {useClient, useUserColorManager} from 'sanity'
|
|
11
7
|
import {useRouter} from 'sanity/router'
|
|
12
|
-
import {
|
|
13
|
-
|
|
8
|
+
import {v4 as uuidv4} from 'uuid'
|
|
9
|
+
|
|
10
|
+
import {SanityDocument, SanityClient} from '@sanity/client'
|
|
14
11
|
import {black, COLOR_HUES, gray, white, hues} from '@sanity/color'
|
|
12
|
+
import {useTheme} from '@sanity/ui'
|
|
13
|
+
|
|
14
|
+
import {GraphRoot, GraphWrapper, HoverNode, Legend, LegendBadge, LegendRow} from './GraphViewStyle'
|
|
15
|
+
import {useFetchDocuments, useListen} from './hooks'
|
|
16
|
+
import {sortBy, loadImage, sizeOf, truncate} from './utils'
|
|
15
17
|
|
|
16
18
|
const DEFAULT_QUERY = `
|
|
17
19
|
*[
|
|
@@ -80,12 +82,12 @@ function stripDraftId(id: string) {
|
|
|
80
82
|
function deduplicateDrafts(docs: SanityDocument[]) {
|
|
81
83
|
const deduped: Record<string, SanityDocument> = {}
|
|
82
84
|
for (const doc of docs) {
|
|
83
|
-
if (
|
|
85
|
+
if (!doc._id.startsWith('drafts.')) {
|
|
84
86
|
deduped[doc._id] = doc
|
|
85
87
|
}
|
|
86
88
|
}
|
|
87
89
|
for (const doc of docs) {
|
|
88
|
-
if (
|
|
90
|
+
if (doc._id.startsWith('drafts.')) {
|
|
89
91
|
const id = stripDraftId(doc._id)
|
|
90
92
|
deduped[id] = Object.assign(doc, {_id: id})
|
|
91
93
|
}
|
|
@@ -105,7 +107,7 @@ class Users {
|
|
|
105
107
|
user.imageUrl ||
|
|
106
108
|
'https://raw.githubusercontent.com/sanity-io/sanity-plugin-graph-view/main/assets/head-silhouette.jpg',
|
|
107
109
|
imageSize,
|
|
108
|
-
imageSize
|
|
110
|
+
imageSize,
|
|
109
111
|
)
|
|
110
112
|
}
|
|
111
113
|
return user
|
|
@@ -230,7 +232,7 @@ export function GraphView(props: GraphViewConfig) {
|
|
|
230
232
|
const newGraph = graph.clone()
|
|
231
233
|
|
|
232
234
|
const oldRefs = findRefs(oldDoc || {}).filter(
|
|
233
|
-
(id) => id === doc._id || docsById[id] !== null
|
|
235
|
+
(id) => id === doc._id || docsById[id] !== null,
|
|
234
236
|
)
|
|
235
237
|
const newRefs = findRefs(doc).filter((id) => id === doc._id || docsById[id] !== null)
|
|
236
238
|
|
|
@@ -267,13 +269,13 @@ export function GraphView(props: GraphViewConfig) {
|
|
|
267
269
|
|
|
268
270
|
const newGraph = graph.clone()
|
|
269
271
|
newGraph.data.links = newGraph.data.links.filter(
|
|
270
|
-
(l) => l.source.id !== docId && l.target.id !== docId
|
|
272
|
+
(l) => l.source.id !== docId && l.target.id !== docId,
|
|
271
273
|
)
|
|
272
274
|
newGraph.data.nodes = newGraph.data.nodes.filter((n) => n.id !== docId)
|
|
273
275
|
setGraph(newGraph)
|
|
274
276
|
}
|
|
275
277
|
},
|
|
276
|
-
[documents, graph, client]
|
|
278
|
+
[documents, graph, client],
|
|
277
279
|
)
|
|
278
280
|
useFetchDocuments(query, fetchCallback, [], client)
|
|
279
281
|
useListen(query, {}, {}, listenCallback, [documents, graph], client)
|
|
@@ -331,7 +333,8 @@ export function GraphView(props: GraphViewConfig) {
|
|
|
331
333
|
ctx.save()
|
|
332
334
|
try {
|
|
333
335
|
ctx.globalAlpha = fadeEasing(
|
|
334
|
-
1 -
|
|
336
|
+
1 -
|
|
337
|
+
Math.min(idleFactorRange, Date.now() - session.lastActive) / idleFactorRange,
|
|
335
338
|
)
|
|
336
339
|
ctx.font = `bold ${Math.round(12 / globalScale)}px sans-serif`
|
|
337
340
|
|
|
@@ -340,7 +343,7 @@ export function GraphView(props: GraphViewConfig) {
|
|
|
340
343
|
ctx.lineWidth = 2 / globalScale
|
|
341
344
|
ctx.moveTo(
|
|
342
345
|
node.x + (Math.sin(angle) * (distance - imgW / 2)) / globalScale,
|
|
343
|
-
node.y + (Math.cos(angle) * (distance - imgH / 2)) / globalScale
|
|
346
|
+
node.y + (Math.cos(angle) * (distance - imgH / 2)) / globalScale,
|
|
344
347
|
)
|
|
345
348
|
ctx.lineTo(node.x + Math.sin(angle) * radius, node.y + Math.cos(angle) * radius)
|
|
346
349
|
ctx.stroke()
|
|
@@ -353,13 +356,11 @@ export function GraphView(props: GraphViewConfig) {
|
|
|
353
356
|
|
|
354
357
|
if (image) {
|
|
355
358
|
ctx.save()
|
|
356
|
-
// eslint-disable-next-line max-depth
|
|
357
359
|
try {
|
|
358
360
|
const dur = 700
|
|
359
361
|
const f = softEasing(
|
|
360
|
-
Math.max(0, (dur - (Date.now() - session.startTime)) / dur)
|
|
362
|
+
Math.max(0, (dur - (Date.now() - session.startTime)) / dur),
|
|
361
363
|
)
|
|
362
|
-
// eslint-disable-next-line max-depth
|
|
363
364
|
if (f > 0) {
|
|
364
365
|
ctx.beginPath()
|
|
365
366
|
ctx.fillStyle = rgba(userColor, f)
|
|
@@ -377,7 +378,7 @@ export function GraphView(props: GraphViewConfig) {
|
|
|
377
378
|
x - imgW / globalScale / 2,
|
|
378
379
|
y - imgH / globalScale / 2,
|
|
379
380
|
imgW / globalScale,
|
|
380
|
-
imgH / globalScale
|
|
381
|
+
imgH / globalScale,
|
|
381
382
|
)
|
|
382
383
|
|
|
383
384
|
ctx.strokeStyle = black.hex
|
|
@@ -413,7 +414,6 @@ export function GraphView(props: GraphViewConfig) {
|
|
|
413
414
|
}
|
|
414
415
|
}}
|
|
415
416
|
nodeCanvasObject={(node: any, ctx, globalScale) => {
|
|
416
|
-
// eslint-disable-next-line default-case
|
|
417
417
|
switch (node.type) {
|
|
418
418
|
case 'document': {
|
|
419
419
|
const nodeColor = getDocTypeColor(node.doc._type)
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import React, {PropsWithChildren} from 'react'
|
|
1
2
|
import styled from 'styled-components'
|
|
2
|
-
|
|
3
|
+
|
|
3
4
|
import {black} from '@sanity/color'
|
|
4
|
-
import
|
|
5
|
+
import {Theme} from '@sanity/ui'
|
|
5
6
|
|
|
6
7
|
type Style = PropsWithChildren<{theme: SanityTheme}>
|
|
7
8
|
type SanityTheme = Theme['sanity']
|
package/src/tool/hooks.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import {useEffect} from 'react'
|
|
2
|
+
|
|
2
3
|
import {ListenEvent, ListenOptions, SanityClient} from '@sanity/client'
|
|
3
4
|
|
|
4
|
-
// eslint-disable-next-line max-params
|
|
5
5
|
export function useListen(
|
|
6
6
|
query: string,
|
|
7
7
|
params: {[key: string]: any},
|
|
8
8
|
options: ListenOptions,
|
|
9
9
|
onUpdate: (event: ListenEvent<any>) => void,
|
|
10
10
|
dependencies: unknown[],
|
|
11
|
-
client: SanityClient
|
|
11
|
+
client: SanityClient,
|
|
12
12
|
): void {
|
|
13
13
|
useEffect(() => {
|
|
14
14
|
const subscription = client.listen(query, params, options).subscribe((update) => {
|
|
@@ -17,7 +17,6 @@ export function useListen(
|
|
|
17
17
|
return () => {
|
|
18
18
|
subscription.unsubscribe()
|
|
19
19
|
}
|
|
20
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
21
20
|
}, [...dependencies, client])
|
|
22
21
|
}
|
|
23
22
|
|
|
@@ -25,12 +24,11 @@ export function useFetchDocuments(
|
|
|
25
24
|
query: string,
|
|
26
25
|
onFetch: (event: ListenEvent<any>) => void,
|
|
27
26
|
dependencies: unknown[],
|
|
28
|
-
client: SanityClient
|
|
27
|
+
client: SanityClient,
|
|
29
28
|
): void {
|
|
30
29
|
useEffect(() => {
|
|
31
|
-
client.fetch(query).then((result) => {
|
|
30
|
+
void client.fetch(query).then((result) => {
|
|
32
31
|
onFetch(result)
|
|
33
32
|
})
|
|
34
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
35
33
|
}, [...dependencies, client])
|
|
36
34
|
}
|
package/src/tool/utils.ts
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
2
1
|
export function sizeOf(value: any): number {
|
|
3
2
|
if (value === null) {
|
|
4
3
|
return 0
|
|
5
4
|
}
|
|
6
5
|
|
|
7
|
-
if (typeof value === 'object') {
|
|
8
|
-
return Object.entries(value).reduce((total, [k, v]) => total + sizeOf(k) + sizeOf(v), 0)
|
|
9
|
-
}
|
|
10
|
-
|
|
11
6
|
if (Array.isArray(value)) {
|
|
12
7
|
return Object.entries(value).reduce((total, v) => total + sizeOf(v), 0)
|
|
13
8
|
}
|
|
14
9
|
|
|
10
|
+
if (typeof value === 'object') {
|
|
11
|
+
return Object.entries(value).reduce((total, [k, v]) => total + sizeOf(k) + sizeOf(v), 0)
|
|
12
|
+
}
|
|
15
13
|
if (typeof value === 'string') {
|
|
16
14
|
return value.length
|
|
17
15
|
}
|
|
@@ -26,7 +24,6 @@ export function loadImage(url: string, w: number, h: number): Promise<HTMLImageE
|
|
|
26
24
|
resolve(img)
|
|
27
25
|
}
|
|
28
26
|
img.onerror = (event) => {
|
|
29
|
-
// eslint-disable-next-line no-console
|
|
30
27
|
console.log('Image error', event)
|
|
31
28
|
resolve(null)
|
|
32
29
|
}
|
|
@@ -38,7 +35,6 @@ export function sortBy<T>(array: T[], f: (t: T) => number): T[] {
|
|
|
38
35
|
return array.sort((a, b) => {
|
|
39
36
|
const va = f(a)
|
|
40
37
|
const vb = f(b)
|
|
41
|
-
// eslint-disable-next-line no-nested-ternary
|
|
42
38
|
return va < vb ? -1 : va > vb ? 1 : 0
|
|
43
39
|
})
|
|
44
40
|
}
|
package/lib/index.esm.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
var t,e,n,r,o,i;function s(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);e&&(r=r.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,r)}return n}function a(t){for(var e=1;e<arguments.length;e++){var n=null!=arguments[e]?arguments[e]:{};e%2?s(Object(n),!0).forEach((function(e){c(t,e,n[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(n)):s(Object(n)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(n,e))}))}return t}function c(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function l(t,e){return e||(e=t.slice(0)),Object.freeze(Object.defineProperties(t,{raw:{value:Object.freeze(e)}}))}import{jsx as d,jsxs as h}from"react/jsx-runtime";import{rgba as u}from"polished";import f from"deep-equal";import{useEffect as p,useState as m,useCallback as y}from"react";import{ForceGraph2D as g}from"react-force-graph";import{v4 as b}from"uuid";import v from"bezier-easing";import{useUserColorManager as x,useClient as _,definePlugin as w}from"sanity";import{useRouter as k,route as O}from"sanity/router";import j from"styled-components";import{black as M,gray as P,white as I,hues as S,COLOR_HUES as q}from"@sanity/color";import{useTheme as A}from"@sanity/ui";function D(t){return null===t?0:"object"==typeof t?Object.entries(t).reduce(((t,e)=>{let[n,r]=e;return t+D(n)+D(r)}),0):Array.isArray(t)?Object.entries(t).reduce(((t,e)=>t+D(e)),0):"string"==typeof t?t.length:1}const T=j.div(t||(t=l(["\n font-family: ",";\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: ",";\n"])),(t=>{let{theme:e}=t;return e.fonts.text.family}),M.hex),C=j.div(e||(e=l(["\n position: relative;\n width: 100%;\n height: 100%;\n"]))),V=j.div(n||(n=l(["\n font-family: ",";\n display: none;\n position: absolute;\n bottom: ","px;\n left: 50%;\n transform: translate3d(-50%, 0, 0);\n background: var(--component-bg);\n border-radius: ","px;\n padding: ","px;\n z-index: 1000;\n\n &:empty {\n display: none;\n }\n"])),(t=>{let{theme:e}=t;return e.fonts.text.family}),(t=>{let{theme:e}=t;return e.space[0]}),(t=>{let{theme:e}=t;return e.radius[2]}),(t=>{let{theme:e}=t;return e.space[2]})),W=j.div(r||(r=l(["\n color: #ccc;\n position: absolute;\n top: ","px;\n left: ","px;\n\n & > div {\n margin: 5px 0;\n }\n"])),(t=>{let{theme:e}=t;return e.space[4]}),(t=>{let{theme:e}=t;return e.space[4]})),z=j.div(o||(o=l(["\n display: flex;\n"]))),B=j.div(i||(i=l(["\n width: 1.25em;\n height: 1.25em;\n background: currentColor;\n border-radius: 50%;\n margin-right: ","px;\n"])),(t=>{let{theme:e}=t;return e.space[2]})),N=v(0,.9,1,1),E=v(.25,.1,0,1);function H(t){return(t.substring(0,1).toUpperCase()+t.substring(1)).replace(/\./g," ").replace(/[A-Z]/g," $&").trim()}function R(t){const e={};for(const n of t)e[n._type]=(e[n._type]||0)+1;return e}function U(t){return"".concat(t.title||t.name||t._id).trim()}function F(t,e){return 5+D(t)/e*100}function G(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[];if(null!==t)if("object"==typeof t)for(const[n,r]of Object.entries(t))"_ref"===n&&"string"==typeof r&&r.length>0&&e.push(L(r)),G(r,e);else if(Array.isArray(t))for(const n of t)G(n,e);return e}function L(t){return t.replace(/^drafts\./,"")}class Z{constructor(){this.id=null,this.user=null,this.doc=null,this.lastActive=0,this.startTime=0,this.angle=0}}class ${constructor(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.sessions=[];const e={};for(const n of t)e[n._id]=n;this.data={nodes:t.map((t=>Object.assign({id:t._id,type:"document",doc:t}))),links:t.flatMap((t=>G(t).map((e=>({source:t._id,target:e}))))).filter((t=>e[t.source]&&e[t.target]))}}setSession(t,e){let n=this.sessions.find((n=>{var r;return n.user.id===t.id&&(null==(r=n.doc)?void 0:r._id)===e.doc._id}));n||(n=new Z,n.id=b(),n.user=t,n.startTime=Date.now(),n.doc=e.doc,n.angle=2*Math.random()*Math.PI,this.sessions.push(n)),n.lastActive=Date.now()}reapSessions(){for(let t=0;t<this.sessions.length;t++){const e=this.sessions[t];Date.now()-e.lastActive>1e4&&(this.sessions=[...this.sessions.slice(0,t),...this.sessions.slice(t+1)],t--)}}clone(){const t=new $;return Object.assign(t,this),t.data={nodes:[...this.data.nodes],links:[...this.data.links]},t}}const J=new class{constructor(){this._users=[]}async getById(t,e){let n=this._users.find((e=>e._id===t));var r,o,i;return n||(n=await e.users.getById(t),this._users.push(n),n.image=await(r=n.imageUrl||"https://raw.githubusercontent.com/sanity-io/sanity-plugin-graph-view/main/assets/head-silhouette.jpg",o=40,i=40,new Promise((t=>{const e=new Image(o,i);e.onload=()=>{t(e)},e.onerror=e=>{console.log("Image error",e),t(null)},e.src=r})))),n}};function K(t){var e;const n=t.query||'\n *[\n !(_id in path("_.*")) &&\n !(_type match "system.*") &&\n !(_type match "sanity.*")\n ]\n',r=null!=(e=t.apiVersion)?e:"2022-09-01",o=x(),[i,s]=m(0),[a,c]=m(null),[l,b]=m([]),[v,w]=m({}),[O,j]=m((()=>new $)),S=k(),q=_({apiVersion:r}),Z=y((t=>{const e=function(t){const e={};for(const n of t)/^drafts\./.test(n._id)||(e[n._id]=n);for(const n of t)if(/^drafts\./.test(n._id)){const t=L(n._id);e[t]=Object.assign(n,{_id:t})}return Object.values(e)}(t);s(Math.max(...e.map(D))),b(e),w(R(e)),j(new $(e))}),[]),K=y((async t=>{const e=t.result;if(e){e._id=L(e._id);const n={};for(const t of l)n[t._id]=t;let r;const o=[...l],i=l.findIndex((t=>t._id===e._id));i>=0?(r=o[i],o[i]=e):o.push(e),b(o),w(R(o)),s(Math.max(...o.map(D)));const a=O.clone(),c=G(r||{}).filter((t=>t===e._id||null!==n[t])),d=G(e).filter((t=>t===e._id||null!==n[t]));let h,u=!f(c,d);u&&(a.data.links=a.data.links.filter((t=>t.source.id!==e._id)).concat(d.map((t=>({source:e._id,target:t})))));const p=O.data.nodes.findIndex((t=>t.doc&&t.doc._id===e._id));p>=0?(h=O.data.nodes[p],h.doc=e):(h={id:e._id,type:"document",doc:e},a.data.nodes.push(h),u=!0),u&&j(a);const m=await J.getById(t.identity,q);O.setSession(m,h)}else if("disappear"===t.transition){const e=L(t.documentId),n=l.filter((t=>t._id!==e));b(n),w(R(n)),s(Math.max(...n.map(D)));const r=O.clone();r.data.links=r.data.links.filter((t=>t.source.id!==e&&t.target.id!==e)),r.data.nodes=r.data.nodes.filter((t=>t.id!==e)),j(r)}}),[l,O,q]);!function(t,e,n,r){p((()=>{r.fetch(t).then((t=>{e(t)}))}),[...n,r])}(n,Z,[],q),function(t,e,n,r,o,i){p((()=>{const o=i.listen(t,e,n).subscribe((t=>{r(t)}));return()=>{o.unsubscribe()}}),[...o,i])}(n,{},{},K,[l,O],q),p((()=>{const t=setInterval((()=>O.reapSessions()),1e3);return()=>clearInterval(t)}),[O]);const Q=A().sanity;return d(C,{theme:Q,children:h(T,{theme:Q,children:[d(W,{theme:Q,children:(X=v,(tt=Object.keys(X),et=t=>X[t]||0,tt.sort(((t,e)=>{const n=et(t),r=et(e);return n<r?-1:n>r?1:0}))).reverse().slice(0,10)).map((t=>h(z,{className:"legend__row",style:{color:Y(t).fill},children:[d(B,{theme:Q}),d("div",{children:H(t)})]},t)))}),a&&d(V,{theme:Q,children:U(a.doc)}),d(g,{graphData:O.data,nodeAutoColorBy:"group",enableNodeDrag:!1,onNodeHover:t=>c(t),onNodeClick:t=>{S.navigateIntent("edit",{id:t.doc._id,documentType:t.doc._type})},linkColor:()=>u(P[500].hex,.25),nodeLabel:()=>"",nodeRelSize:1,nodeVal:t=>F(t.doc,i),onRenderFramePost:(t,e)=>{for(const n of O.sessions){const r=O.data.nodes.find((t=>{var e;return t.doc&&t.doc._id===(null==(e=null==n?void 0:n.doc)?void 0:e._id)}));if(r){const s=1e4,a=n.angle,c=Math.sqrt(F(r.doc,i)),l=n.user.image,d=o.get(n.user.displayName).tints[400].hex,h=c*e+40,f=l?l.width:0,p=l?l.height:0,m=r.x+Math.sin(a)*h/e,y=r.y+Math.cos(a)*h/e;t.save();try{if(t.globalAlpha=N(1-Math.min(s,Date.now()-n.lastActive)/s),t.font="bold ".concat(Math.round(12/e),"px sans-serif"),t.beginPath(),t.strokeStyle=u(I.hex,1),t.lineWidth=2/e,t.moveTo(r.x+Math.sin(a)*(h-f/2)/e,r.y+Math.cos(a)*(h-p/2)/e),t.lineTo(r.x+Math.sin(a)*c,r.y+Math.cos(a)*c),t.stroke(),t.beginPath(),t.strokeStyle=u(I.hex,1),t.lineWidth=2/e,t.arc(r.x,r.y,c,0,2*Math.PI,!1),t.stroke(),l){t.save();try{const r=700,o=E(Math.max(0,(r-(Date.now()-n.startTime))/r));o>0&&(t.beginPath(),t.fillStyle=u(d,o),t.arc(m,y,(f/2+10)/e,0,2*Math.PI,!1),t.fill()),t.beginPath(),t.fillStyle=u(I.hex,1),t.arc(m,y,f/e/2,0,2*Math.PI,!1),t.clip(),t.drawImage(l,m-f/e/2,y-p/e/2,f/e,p/e),t.strokeStyle=M.hex,t.lineWidth=6/e,t.stroke(),t.strokeStyle=d,t.lineWidth=4/e,t.stroke()}finally{t.restore()}}t.beginPath(),t.strokeStyle=u(M.hex,1),t.lineWidth=.5/e,t.arc(m,y,f/e/2,0,2*Math.PI,!1),t.stroke();const o=a>=Math.PI/2&&a<1.5*Math.PI,i=o?y-(p/2+5)/e:y+(p/2+5)/e;t.fillStyle=u(I.hex,1),t.textAlign="center",t.textBaseline=o?"bottom":"top",t.fillText(n.user.displayName,m,i)}finally{t.restore()}}}},nodeCanvasObject:(t,e,n)=>{if("document"===t.type){const s=Y(t.doc._type),c=Math.sqrt(F(t.doc,i)),l=Math.min(100,10/n);if(e.beginPath(),e.fillStyle=null!==a&&t.doc._id===a.doc._id?u(P[500].hex,.8):s.fill,e.strokeStyle=s.border,e.lineWidth=.5,e.arc(t.x,t.y,c,0,2*Math.PI,!1),e.stroke(),e.fill(),c*n>10){e.font="".concat(l,"px sans-serif");const i=2*c+30/n;for(let s=50;s>=5;s/=1.2){const a=(r=U(t.doc),o=Math.round(s),r.length>o?"".concat(r.substring(0,o),"…"):r);if(e.measureText(a).width<i){e.textAlign="center",e.textBaseline="top",e.strokeStyle=u(M.hex,.5),e.lineWidth=2/n,e.strokeText(a,t.x,t.y+c+5/n),e.fillText(a,t.x,t.y+c+5/n);break}}}}var r,o},linkCanvasObject:(t,e,n)=>{e.beginPath(),e.strokeStyle=u(P[500].hex,.125),e.lineWidth=2/n,e.moveTo(t.source.x,t.source.y),e.lineTo(t.target.x,t.target.y),e.stroke()}})]})});var X,tt,et}const Q={};let X=0;function Y(t){if(Q[t])return Q[t];const e=q[X%q.length];return X+=1,Q[t]={fill:S[e][400].hex,border:u(M.hex,.5)},Q[t]}const tt=()=>d("svg",{xmlns:"http://www.w3.org/2000/svg",width:"1em",height:"1em",viewBox:"0 0 46.063 46.063",children:d("path",{fill:"currentColor",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"})}),et=w((function(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{name:"@sanity/content-graph-view",tools:e=>[...e,{name:"graph-your-content",title:"Graph",icon:tt,component:function(){return d(K,a({},t))},router:O.create("/:selectedDocumentId")}]}}));export{et as contentGraphView};
|
|
2
|
-
//# sourceMappingURL=index.esm.js.map
|
package/lib/index.esm.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":["../src/tool/utils.ts","../src/tool/GraphViewStyle.tsx","../src/tool/GraphView.tsx","../src/tool/hooks.ts","../src/tool/GraphViewIcon.tsx","../src/plugin.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types\nexport 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 // eslint-disable-next-line no-console\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 // eslint-disable-next-line no-nested-ternary\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 styled from 'styled-components'\nimport {Theme} from '@sanity/ui'\nimport {black} from '@sanity/color'\nimport React, {PropsWithChildren} from 'react'\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 {rgba} from 'polished'\nimport deepEqual from 'deep-equal'\nimport React, {useCallback, useEffect, useState} from 'react'\nimport {ForceGraph2D} from 'react-force-graph'\nimport {v4 as uuidv4} from 'uuid'\nimport BezierEasing from 'bezier-easing'\nimport {useFetchDocuments, useListen} from './hooks'\nimport {sortBy, loadImage, sizeOf, truncate} from './utils'\nimport {SanityDocument, SanityClient} from '@sanity/client'\nimport {useClient, useUserColorManager} from 'sanity'\nimport {useRouter} from 'sanity/router'\nimport {GraphRoot, GraphWrapper, HoverNode, Legend, LegendBadge, LegendRow} from './GraphViewStyle'\nimport {useTheme} from '@sanity/ui'\nimport {black, COLOR_HUES, gray, white, hues} from '@sanity/color'\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 (!/^drafts\\./.test(doc._id)) {\n deduped[doc._id] = doc\n }\n }\n for (const doc of docs) {\n if (/^drafts\\./.test(doc._id)) {\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, docNode) {\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) => {\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) => {\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) => 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) => 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) => l.source.id !== docId && l.target.id !== docId\n )\n newGraph.data.nodes = newGraph.data.nodes.filter((n) => n.id !== docId)\n setGraph(newGraph)\n }\n },\n [documents, graph, client]\n )\n useFetchDocuments(query, fetchCallback, [], client)\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 <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.doc && n.doc._id === session?.doc?._id)\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 - 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 // eslint-disable-next-line max-depth\n try {\n const dur = 700\n const f = softEasing(\n Math.max(0, (dur - (Date.now() - session.startTime)) / dur)\n )\n // eslint-disable-next-line max-depth\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 // eslint-disable-next-line default-case\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 = {}\nlet typeColorNum = 0\n\nfunction getDocTypeColor(docType) {\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 {useEffect} from 'react'\nimport {ListenEvent, ListenOptions, SanityClient} from '@sanity/client'\n\n// eslint-disable-next-line max-params\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 // eslint-disable-next-line react-hooks/exhaustive-deps\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 client.fetch(query).then((result) => {\n onFetch(result)\n })\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [...dependencies, client])\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 {GraphView} from './tool/GraphView'\nimport {GraphViewIcon} from './tool/GraphViewIcon'\nimport {definePlugin} from 'sanity'\nimport {route} from 'sanity/router'\nimport React from 'react'\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":["sizeOf","value","Object","entries","reduce","total","k","v","_ref","Array","isArray","length","GraphRoot","styled","div","_templateObject","_taggedTemplateLiteral","_ref2","theme","fonts","text","family","black","hex","GraphWrapper","_templateObject2","HoverNode","_templateObject3","_ref3","_ref4","space","_ref5","radius","_ref6","Legend","_templateObject4","_ref7","_ref8","LegendRow","_templateObject5","LegendBadge","_templateObject6","_ref9","fadeEasing","BezierEasing","softEasing","formatDocType","docType","substring","toUpperCase","replace","trim","getDocTypeCounts","docs","types","doc","_type","labelFor","title","name","_id","valueFor","maxSize","findRefs","obj","dest","push","stripDraftId","id","Session","constructor","this","user","lastActive","startTime","angle","GraphData","sessions","docsById","data","nodes","map","d","assign","type","links","flatMap","ref","source","target","filter","link","setSession","docNode","session","find","s","_a","uuidv4","Date","now","Math","random","PI","reapSessions","i","slice","clone","copy","users","_users","async","client","u","url","w","h","getById","image","imageUrl","Promise","resolve","img","Image","onload","onerror","event","console","log","src","GraphView","props","query","apiVersion","userColorManager","useUserColorManager","setMaxSize","useState","hoverNode","setHoverNode","documents","setDocuments","docTypes","setDocTypes","graph","setGraph","router","useRouter","useClient","fetchCallback","useCallback","_docs","deduped","test","values","deduplicateDrafts","max","listenCallback","update","result","oldDoc","idx","findIndex","newGraph","oldRefs","newRefs","graphChanged","deepEqual","l","concat","nodeIdx","n","identity","transition","docId","documentId","onFetch","dependencies","useEffect","fetch","then","useFetchDocuments","params","options","onUpdate","subscription","listen","subscribe","unsubscribe","useListen","interval","setInterval","clearInterval","useTheme","sanity","jsx","children","jsxs","counts","array","keys","f","sort","a","b","va","vb","reverse","className","style","color","getDocTypeColor","fill","ForceGraph2D","graphData","nodeAutoColorBy","enableNodeDrag","onNodeHover","node","onNodeClick","navigateIntent","documentType","linkColor","rgba","gray","nodeLabel","nodeRelSize","nodeVal","onRenderFramePost","ctx","globalScale","idleFactorRange","sqrt","userColor","get","displayName","tints","distance","imgW","width","imgH","height","x","sin","y","cos","save","globalAlpha","min","font","round","beginPath","strokeStyle","white","lineWidth","moveTo","lineTo","stroke","arc","dur","fillStyle","clip","drawImage","restore","above","textY","textAlign","textBaseline","fillText","nodeCanvasObject","nodeColor","fontSize","border","len","label","limit","measureText","strokeText","linkCanvasObject","colorCache","typeColorNum","hue","COLOR_HUES","hues","GraphViewIcon","xmlns","viewBox","contentGraphView","definePlugin","config","arguments","undefined","tools","prev","icon","component","_objectSpread","route","create"],"mappings":"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"}
|
package/lib/index.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";var e,t,n,r,s,i;function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function a(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?o(Object(n),!0).forEach((function(t){l(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):o(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function c(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}Object.defineProperty(exports,"__esModule",{value:!0});var d=require("react/jsx-runtime"),u=require("polished"),h=require("deep-equal"),f=require("react"),p=require("react-force-graph"),g=require("uuid"),y=require("bezier-easing"),b=require("sanity"),m=require("sanity/router"),x=require("styled-components"),v=require("@sanity/color"),_=require("@sanity/ui");function w(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var j=w(h),k=w(y),O=w(x);function q(e){return null===e?0:"object"==typeof e?Object.entries(e).reduce(((e,t)=>{let[n,r]=t;return e+q(n)+q(r)}),0):Array.isArray(e)?Object.entries(e).reduce(((e,t)=>e+q(t)),0):"string"==typeof e?e.length:1}const M=O.default.div(e||(e=c(["\n font-family: ",";\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: ",";\n"])),(e=>{let{theme:t}=e;return t.fonts.text.family}),v.black.hex),P=O.default.div(t||(t=c(["\n position: relative;\n width: 100%;\n height: 100%;\n"]))),S=O.default.div(n||(n=c(["\n font-family: ",";\n display: none;\n position: absolute;\n bottom: ","px;\n left: 50%;\n transform: translate3d(-50%, 0, 0);\n background: var(--component-bg);\n border-radius: ","px;\n padding: ","px;\n z-index: 1000;\n\n &:empty {\n display: none;\n }\n"])),(e=>{let{theme:t}=e;return t.fonts.text.family}),(e=>{let{theme:t}=e;return t.space[0]}),(e=>{let{theme:t}=e;return t.radius[2]}),(e=>{let{theme:t}=e;return t.space[2]})),I=O.default.div(r||(r=c(["\n color: #ccc;\n position: absolute;\n top: ","px;\n left: ","px;\n\n & > div {\n margin: 5px 0;\n }\n"])),(e=>{let{theme:t}=e;return t.space[4]}),(e=>{let{theme:t}=e;return t.space[4]})),C=O.default.div(s||(s=c(["\n display: flex;\n"]))),A=O.default.div(i||(i=c(["\n width: 1.25em;\n height: 1.25em;\n background: currentColor;\n border-radius: 50%;\n margin-right: ","px;\n"])),(e=>{let{theme:t}=e;return t.space[2]})),D=k.default(0,.9,1,1),T=k.default(.25,.1,0,1);function V(e){return(e.substring(0,1).toUpperCase()+e.substring(1)).replace(/\./g," ").replace(/[A-Z]/g," $&").trim()}function W(e){const t={};for(const n of e)t[n._type]=(t[n._type]||0)+1;return t}function z(e){return"".concat(e.title||e.name||e._id).trim()}function B(e,t){return 5+q(e)/t*100}function E(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[];if(null!==e)if("object"==typeof e)for(const[n,r]of Object.entries(e))"_ref"===n&&"string"==typeof r&&r.length>0&&t.push(N(r)),E(r,t);else if(Array.isArray(e))for(const n of e)E(n,t);return t}function N(e){return e.replace(/^drafts\./,"")}class R{constructor(){this.id=null,this.user=null,this.doc=null,this.lastActive=0,this.startTime=0,this.angle=0}}class U{constructor(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.sessions=[];const t={};for(const n of e)t[n._id]=n;this.data={nodes:e.map((e=>Object.assign({id:e._id,type:"document",doc:e}))),links:e.flatMap((e=>E(e).map((t=>({source:e._id,target:t}))))).filter((e=>t[e.source]&&t[e.target]))}}setSession(e,t){let n=this.sessions.find((n=>{var r;return n.user.id===e.id&&(null==(r=n.doc)?void 0:r._id)===t.doc._id}));n||(n=new R,n.id=g.v4(),n.user=e,n.startTime=Date.now(),n.doc=t.doc,n.angle=2*Math.random()*Math.PI,this.sessions.push(n)),n.lastActive=Date.now()}reapSessions(){for(let e=0;e<this.sessions.length;e++){const t=this.sessions[e];Date.now()-t.lastActive>1e4&&(this.sessions=[...this.sessions.slice(0,e),...this.sessions.slice(e+1)],e--)}}clone(){const e=new U;return Object.assign(e,this),e.data={nodes:[...this.data.nodes],links:[...this.data.links]},e}}const H=new class{constructor(){this._users=[]}async getById(e,t){let n=this._users.find((t=>t._id===e));var r,s,i;return n||(n=await t.users.getById(e),this._users.push(n),n.image=await(r=n.imageUrl||"https://raw.githubusercontent.com/sanity-io/sanity-plugin-graph-view/main/assets/head-silhouette.jpg",s=40,i=40,new Promise((e=>{const t=new Image(s,i);t.onload=()=>{e(t)},t.onerror=t=>{console.log("Image error",t),e(null)},t.src=r})))),n}};function G(e){var t;const n=e.query||'\n *[\n !(_id in path("_.*")) &&\n !(_type match "system.*") &&\n !(_type match "sanity.*")\n ]\n',r=null!=(t=e.apiVersion)?t:"2022-09-01",s=b.useUserColorManager(),[i,o]=f.useState(0),[a,l]=f.useState(null),[c,h]=f.useState([]),[g,y]=f.useState({}),[x,w]=f.useState((()=>new U)),k=m.useRouter(),O=b.useClient({apiVersion:r}),R=f.useCallback((e=>{const t=function(e){const t={};for(const n of e)/^drafts\./.test(n._id)||(t[n._id]=n);for(const n of e)if(/^drafts\./.test(n._id)){const e=N(n._id);t[e]=Object.assign(n,{_id:e})}return Object.values(t)}(e);o(Math.max(...t.map(q))),h(t),y(W(t)),w(new U(t))}),[]),G=f.useCallback((async e=>{const t=e.result;if(t){t._id=N(t._id);const n={};for(const e of c)n[e._id]=e;let r;const s=[...c],i=c.findIndex((e=>e._id===t._id));i>=0?(r=s[i],s[i]=t):s.push(t),h(s),y(W(s)),o(Math.max(...s.map(q)));const a=x.clone(),l=E(r||{}).filter((e=>e===t._id||null!==n[e])),d=E(t).filter((e=>e===t._id||null!==n[e]));let u,f=!j.default(l,d);f&&(a.data.links=a.data.links.filter((e=>e.source.id!==t._id)).concat(d.map((e=>({source:t._id,target:e})))));const p=x.data.nodes.findIndex((e=>e.doc&&e.doc._id===t._id));p>=0?(u=x.data.nodes[p],u.doc=t):(u={id:t._id,type:"document",doc:t},a.data.nodes.push(u),f=!0),f&&w(a);const g=await H.getById(e.identity,O);x.setSession(g,u)}else if("disappear"===e.transition){const t=N(e.documentId),n=c.filter((e=>e._id!==t));h(n),y(W(n)),o(Math.max(...n.map(q)));const r=x.clone();r.data.links=r.data.links.filter((e=>e.source.id!==t&&e.target.id!==t)),r.data.nodes=r.data.nodes.filter((e=>e.id!==t)),w(r)}}),[c,x,O]);!function(e,t,n,r){f.useEffect((()=>{r.fetch(e).then((e=>{t(e)}))}),[...n,r])}(n,R,[],O),function(e,t,n,r,s,i){f.useEffect((()=>{const s=i.listen(e,t,n).subscribe((e=>{r(e)}));return()=>{s.unsubscribe()}}),[...s,i])}(n,{},{},G,[c,x],O),f.useEffect((()=>{const e=setInterval((()=>x.reapSessions()),1e3);return()=>clearInterval(e)}),[x]);const L=_.useTheme().sanity;return d.jsx(P,{theme:L,children:d.jsxs(M,{theme:L,children:[d.jsx(I,{theme:L,children:(F=g,($=Object.keys(F),J=e=>F[e]||0,$.sort(((e,t)=>{const n=J(e),r=J(t);return n<r?-1:n>r?1:0}))).reverse().slice(0,10)).map((e=>d.jsxs(C,{className:"legend__row",style:{color:Z(e).fill},children:[d.jsx(A,{theme:L}),d.jsx("div",{children:V(e)})]},e)))}),a&&d.jsx(S,{theme:L,children:z(a.doc)}),d.jsx(p.ForceGraph2D,{graphData:x.data,nodeAutoColorBy:"group",enableNodeDrag:!1,onNodeHover:e=>l(e),onNodeClick:e=>{k.navigateIntent("edit",{id:e.doc._id,documentType:e.doc._type})},linkColor:()=>u.rgba(v.gray[500].hex,.25),nodeLabel:()=>"",nodeRelSize:1,nodeVal:e=>B(e.doc,i),onRenderFramePost:(e,t)=>{for(const n of x.sessions){const r=x.data.nodes.find((e=>{var t;return e.doc&&e.doc._id===(null==(t=null==n?void 0:n.doc)?void 0:t._id)}));if(r){const o=1e4,a=n.angle,l=Math.sqrt(B(r.doc,i)),c=n.user.image,d=s.get(n.user.displayName).tints[400].hex,h=l*t+40,f=c?c.width:0,p=c?c.height:0,g=r.x+Math.sin(a)*h/t,y=r.y+Math.cos(a)*h/t;e.save();try{if(e.globalAlpha=D(1-Math.min(o,Date.now()-n.lastActive)/o),e.font="bold ".concat(Math.round(12/t),"px sans-serif"),e.beginPath(),e.strokeStyle=u.rgba(v.white.hex,1),e.lineWidth=2/t,e.moveTo(r.x+Math.sin(a)*(h-f/2)/t,r.y+Math.cos(a)*(h-p/2)/t),e.lineTo(r.x+Math.sin(a)*l,r.y+Math.cos(a)*l),e.stroke(),e.beginPath(),e.strokeStyle=u.rgba(v.white.hex,1),e.lineWidth=2/t,e.arc(r.x,r.y,l,0,2*Math.PI,!1),e.stroke(),c){e.save();try{const r=700,s=T(Math.max(0,(r-(Date.now()-n.startTime))/r));s>0&&(e.beginPath(),e.fillStyle=u.rgba(d,s),e.arc(g,y,(f/2+10)/t,0,2*Math.PI,!1),e.fill()),e.beginPath(),e.fillStyle=u.rgba(v.white.hex,1),e.arc(g,y,f/t/2,0,2*Math.PI,!1),e.clip(),e.drawImage(c,g-f/t/2,y-p/t/2,f/t,p/t),e.strokeStyle=v.black.hex,e.lineWidth=6/t,e.stroke(),e.strokeStyle=d,e.lineWidth=4/t,e.stroke()}finally{e.restore()}}e.beginPath(),e.strokeStyle=u.rgba(v.black.hex,1),e.lineWidth=.5/t,e.arc(g,y,f/t/2,0,2*Math.PI,!1),e.stroke();const s=a>=Math.PI/2&&a<1.5*Math.PI,i=s?y-(p/2+5)/t:y+(p/2+5)/t;e.fillStyle=u.rgba(v.white.hex,1),e.textAlign="center",e.textBaseline=s?"bottom":"top",e.fillText(n.user.displayName,g,i)}finally{e.restore()}}}},nodeCanvasObject:(e,t,n)=>{if("document"===e.type){const o=Z(e.doc._type),l=Math.sqrt(B(e.doc,i)),c=Math.min(100,10/n);if(t.beginPath(),t.fillStyle=null!==a&&e.doc._id===a.doc._id?u.rgba(v.gray[500].hex,.8):o.fill,t.strokeStyle=o.border,t.lineWidth=.5,t.arc(e.x,e.y,l,0,2*Math.PI,!1),t.stroke(),t.fill(),l*n>10){t.font="".concat(c,"px sans-serif");const i=2*l+30/n;for(let o=50;o>=5;o/=1.2){const a=(r=z(e.doc),s=Math.round(o),r.length>s?"".concat(r.substring(0,s),"…"):r);if(t.measureText(a).width<i){t.textAlign="center",t.textBaseline="top",t.strokeStyle=u.rgba(v.black.hex,.5),t.lineWidth=2/n,t.strokeText(a,e.x,e.y+l+5/n),t.fillText(a,e.x,e.y+l+5/n);break}}}}var r,s},linkCanvasObject:(e,t,n)=>{t.beginPath(),t.strokeStyle=u.rgba(v.gray[500].hex,.125),t.lineWidth=2/n,t.moveTo(e.source.x,e.source.y),t.lineTo(e.target.x,e.target.y),t.stroke()}})]})});var F,$,J}const L={};let F=0;function Z(e){if(L[e])return L[e];const t=v.COLOR_HUES[F%v.COLOR_HUES.length];return F+=1,L[e]={fill:v.hues[t][400].hex,border:u.rgba(v.black.hex,.5)},L[e]}const $=()=>d.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",width:"1em",height:"1em",viewBox:"0 0 46.063 46.063",children:d.jsx("path",{fill:"currentColor",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"})}),J=b.definePlugin((function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{name:"@sanity/content-graph-view",tools:t=>[...t,{name:"graph-your-content",title:"Graph",icon:$,component:function(){return d.jsx(G,a({},e))},router:m.route.create("/:selectedDocumentId")}]}}));exports.contentGraphView=J;
|
|
2
|
-
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/tool/utils.ts","../src/tool/GraphViewStyle.tsx","../src/tool/GraphView.tsx","../src/tool/hooks.ts","../src/tool/GraphViewIcon.tsx","../src/plugin.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types\nexport 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 // eslint-disable-next-line no-console\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 // eslint-disable-next-line no-nested-ternary\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 styled from 'styled-components'\nimport {Theme} from '@sanity/ui'\nimport {black} from '@sanity/color'\nimport React, {PropsWithChildren} from 'react'\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 {rgba} from 'polished'\nimport deepEqual from 'deep-equal'\nimport React, {useCallback, useEffect, useState} from 'react'\nimport {ForceGraph2D} from 'react-force-graph'\nimport {v4 as uuidv4} from 'uuid'\nimport BezierEasing from 'bezier-easing'\nimport {useFetchDocuments, useListen} from './hooks'\nimport {sortBy, loadImage, sizeOf, truncate} from './utils'\nimport {SanityDocument, SanityClient} from '@sanity/client'\nimport {useClient, useUserColorManager} from 'sanity'\nimport {useRouter} from 'sanity/router'\nimport {GraphRoot, GraphWrapper, HoverNode, Legend, LegendBadge, LegendRow} from './GraphViewStyle'\nimport {useTheme} from '@sanity/ui'\nimport {black, COLOR_HUES, gray, white, hues} from '@sanity/color'\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 (!/^drafts\\./.test(doc._id)) {\n deduped[doc._id] = doc\n }\n }\n for (const doc of docs) {\n if (/^drafts\\./.test(doc._id)) {\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, docNode) {\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) => {\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) => {\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) => 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) => 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) => l.source.id !== docId && l.target.id !== docId\n )\n newGraph.data.nodes = newGraph.data.nodes.filter((n) => n.id !== docId)\n setGraph(newGraph)\n }\n },\n [documents, graph, client]\n )\n useFetchDocuments(query, fetchCallback, [], client)\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 <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.doc && n.doc._id === session?.doc?._id)\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 - 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 // eslint-disable-next-line max-depth\n try {\n const dur = 700\n const f = softEasing(\n Math.max(0, (dur - (Date.now() - session.startTime)) / dur)\n )\n // eslint-disable-next-line max-depth\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 // eslint-disable-next-line default-case\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 = {}\nlet typeColorNum = 0\n\nfunction getDocTypeColor(docType) {\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 {useEffect} from 'react'\nimport {ListenEvent, ListenOptions, SanityClient} from '@sanity/client'\n\n// eslint-disable-next-line max-params\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 // eslint-disable-next-line react-hooks/exhaustive-deps\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 client.fetch(query).then((result) => {\n onFetch(result)\n })\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [...dependencies, client])\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 {GraphView} from './tool/GraphView'\nimport {GraphViewIcon} from './tool/GraphViewIcon'\nimport {definePlugin} from 'sanity'\nimport {route} from 'sanity/router'\nimport React from 'react'\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":["sizeOf","value","Object","entries","reduce","total","k","v","_ref","Array","isArray","length","GraphRoot","styled","div","_templateObject","_taggedTemplateLiteral","_ref2","theme","fonts","text","family","black","hex","GraphWrapper","_templateObject2","HoverNode","_templateObject3","_ref3","_ref4","space","_ref5","radius","_ref6","Legend","_templateObject4","_ref7","_ref8","LegendRow","_templateObject5","LegendBadge","_templateObject6","_ref9","fadeEasing","BezierEasing","softEasing","formatDocType","docType","substring","toUpperCase","replace","trim","getDocTypeCounts","docs","types","doc","_type","labelFor","title","name","_id","valueFor","maxSize","findRefs","obj","dest","push","stripDraftId","id","Session","constructor","this","user","lastActive","startTime","angle","GraphData","sessions","docsById","data","nodes","map","d","assign","type","links","flatMap","ref","source","target","filter","link","setSession","docNode","session","find","s","_a","uuidv4","Date","now","Math","random","PI","reapSessions","i","slice","clone","copy","users","_users","async","client","u","url","w","h","getById","image","imageUrl","Promise","resolve","img","Image","onload","onerror","event","console","log","src","GraphView","props","query","apiVersion","userColorManager","useUserColorManager","setMaxSize","useState","hoverNode","setHoverNode","documents","setDocuments","docTypes","setDocTypes","graph","setGraph","router","useRouter","useClient","fetchCallback","useCallback","_docs","deduped","test","values","deduplicateDrafts","max","listenCallback","update","result","oldDoc","idx","findIndex","newGraph","oldRefs","newRefs","graphChanged","deepEqual","l","concat","nodeIdx","n","identity","transition","docId","documentId","onFetch","dependencies","useEffect","fetch","then","useFetchDocuments","params","options","onUpdate","subscription","listen","subscribe","unsubscribe","useListen","interval","setInterval","clearInterval","useTheme","sanity","jsx","children","jsxs","counts","array","keys","f","sort","a","b","va","vb","reverse","className","style","color","getDocTypeColor","fill","ForceGraph2D","graphData","nodeAutoColorBy","enableNodeDrag","onNodeHover","node","onNodeClick","navigateIntent","documentType","linkColor","rgba","gray","nodeLabel","nodeRelSize","nodeVal","onRenderFramePost","ctx","globalScale","idleFactorRange","sqrt","userColor","get","displayName","tints","distance","imgW","width","imgH","height","x","sin","y","cos","save","globalAlpha","min","font","round","beginPath","strokeStyle","white","lineWidth","moveTo","lineTo","stroke","arc","dur","fillStyle","clip","drawImage","restore","above","textY","textAlign","textBaseline","fillText","nodeCanvasObject","nodeColor","fontSize","border","len","label","limit","measureText","strokeText","linkCanvasObject","colorCache","typeColorNum","hue","COLOR_HUES","hues","GraphViewIcon","xmlns","viewBox","contentGraphView","definePlugin","config","arguments","undefined","tools","prev","icon","component","_objectSpread","route","create"],"mappings":"+wCACO,SAASA,EAAOC,GACrB,OAAc,OAAVA,EACK,EAGY,iBAAVA,EACFC,OAAOC,QAAQF,GAAOG,QAAO,CAACC,WAAQC,EAAGC,GAACC,EAAA,OAAMH,EAAQL,EAAOM,GAAKN,EAAOO,KAAI,GAGpFE,MAAMC,QAAQT,GACTC,OAAOC,QAAQF,GAAOG,QAAO,CAACC,EAAOE,IAAMF,EAAQL,EAAOO,IAAI,GAGlD,iBAAVN,EACFA,EAAMU,OAGR,CACT,CCXO,MAAMC,EAA6BC,EAAO,QAAAC,IAChCC,IAAAA,EAAAC,EAAA,CAAA,oBAAA,mGAAA,UAAAC,IAAA,IAACC,MAACA,GAAkBD,EAAA,OAAAC,EAAMC,MAAMC,KAAKC,MAAA,GAMtCC,EAAAA,MAAMC,KAGTC,EAAgCX,EAAO,QAAAC,IAAAW,IAAAA,EAAAT,EAAA,CAAA,iEAMvCU,EAA6Bb,EAAO,QAAAC,IAChCa,IAAAA,EAAAX,EAAA,CAAA,oBAAA,yDAAA,kHAAA,mBAAA,sEAAAY,IAAA,IAACV,MAACA,GAAkBU,EAAA,OAAAV,EAAMC,MAAMC,KAAKC,MAAA,IAG1CQ,IAAA,IAACX,MAACA,GAAKW,EAAA,OAAaX,EAAMY,MAAM,EAAA,IAIzBC,IAAA,IAACb,MAACA,GAAKa,EAAA,OAAab,EAAMc,OAAO,EAAA,IACvCC,IAAA,IAACf,MAACA,GAAKe,EAAA,OAAaf,EAAMY,MAAM,EAAA,IAQhCI,EAA0BrB,EAAO,QAAAC,IAGrCqB,IAAAA,EAAAnB,EAAA,CAAA,mDAAA,gBAAA,oDAAAoB,IAAA,IAAClB,MAACA,GAAKkB,EAAA,OAAalB,EAAMY,MAAM,EAAA,IAC/BO,IAAA,IAACnB,MAACA,GAAKmB,EAAA,OAAanB,EAAMY,MAAM,EAAA,IAO7BQ,EAAYzB,EAAO,QAAAC,IAAAyB,IAAAA,EAAAvB,EAAA,CAAA,2BAInBwB,EAA+B3B,EAAO,QAAAC,IAKjC2B,IAAAA,EAAAzB,EAAA,CAAA,8GAAA,YAAA0B,IAAA,IAACxB,MAACA,GAAKwB,EAAA,OAAaxB,EAAMY,MAAM,EAAA,ICtC5Ca,EAAaC,EAAA,QAAa,EAAG,GAAK,EAAG,GACrCC,EAAaD,EAAA,QAAa,IAAM,GAAK,EAAK,GAUhD,SAASE,EAAcC,GACrB,OAAQA,EAAQC,UAAU,EAAG,GAAGC,cAAgBF,EAAQC,UAAU,IAC/DE,QAAQ,MAAO,KACfA,QAAQ,SAAU,OAClBC,MACL,CAEA,SAASC,EAAiBC,GACxB,MAAMC,EAAgC,CAAA,EACtC,IAAA,MAAWC,KAAOF,EAChBC,EAAMC,EAAIC,QAAUF,EAAMC,EAAIC,QAAU,GAAK,EAExC,OAAAF,CACT,CAEA,SAASG,EAASF,GAChB,MAAO,GAAGA,OAAAA,EAAIG,OAASH,EAAII,MAAQJ,EAAIK,KAAMT,MAC/C,CAEA,SAASU,EAASN,EAAUO,GAC1B,OAAO,EAAW9D,EAAOuD,GAAOO,EAArB,GACb,CAEA,SAASC,EAASC,GAA4B,IAAlBC,yDAAc,GACxC,GAAY,OAARD,EACE,GAAe,iBAARA,EACT,IAAA,MAAY1D,EAAGC,KAAML,OAAOC,QAAQ6D,GACxB,SAAN1D,GAA6B,iBAANC,GAAkBA,EAAEI,OAAS,GACjDsD,EAAAC,KAAKC,EAAa5D,IAEzBwD,EAASxD,EAAG0D,QAEL,GAAAxD,MAAMC,QAAQsD,GACvB,IAAA,MAAWzD,KAAKyD,EACdD,EAASxD,EAAG0D,GAIX,OAAAA,CACT,CAEA,SAASE,EAAaC,GACb,OAAAA,EAAGlB,QAAQ,YAAa,GACjC,CAqCA,MAAMmB,EAANC,cACsBC,KAAAH,GAAA,KACRG,KAAAC,KAAA,KACiBD,KAAAhB,IAAA,KACRgB,KAAAE,WAAA,EACDF,KAAAG,UAAA,EACJH,KAAAI,MAAA,CAAA,EAGlB,MAAMC,EAIJN,cAAyC,IAA7BjB,yDAAyB,GAHrCkB,KAAAM,SAAsB,GAIpB,MAAMC,EAA2C,CAAA,EACjD,IAAA,MAAWvB,KAAOF,EAChByB,EAASvB,EAAIK,KAAOL,EAGtBgB,KAAKQ,KAAO,CACVC,MAAO3B,EAAK4B,KAAKC,GAAMhF,OAAOiF,OAAO,CAACf,GAAIc,EAAEtB,IAAKwB,KAAM,WAAY7B,IAAK2B,MACxEG,MAAOhC,EACJiC,SAAS/B,GAAQQ,EAASR,GAAK0B,KAAKM,IAAS,CAACC,OAAQjC,EAAIK,IAAK6B,OAAQF,QACvEG,QAAQC,GAASb,EAASa,EAAKH,SAAWV,EAASa,EAAKF,UAE/D,CAEAG,WAAWpB,EAAMqB,GACf,IAAIC,EAAUvB,KAAKM,SAASkB,MAAMC,IA9ItC,IAAAC,EA8I8C,OAAAD,EAAAxB,KAAKJ,KAAOI,EAAKJ,KAAM,OAAA6B,IAAE1C,UAAF,EAAA0C,EAAOrC,OAAQiC,EAAQtC,IAAIK,GAAA,IACvFkC,IACHA,EAAU,IAAIzB,EACdyB,EAAQ1B,GAAK8B,EAAAA,KACbJ,EAAQtB,KAAOA,EACPsB,EAAApB,UAAYyB,KAAKC,MACzBN,EAAQvC,IAAMsC,EAAQtC,IACtBuC,EAAQnB,MAAwB,EAAhB0B,KAAKC,SAAeD,KAAKE,GACpChC,KAAAM,SAASX,KAAK4B,IAEbA,EAAArB,WAAa0B,KAAKC,KAC5B,CAEAI,eACE,IAAA,IAASC,EAAI,EAAGA,EAAIlC,KAAKM,SAASlE,OAAQ8F,IAAK,CACvC,MAAAX,EAAUvB,KAAKM,SAAS4B,GAC1BN,KAAKC,MAAQN,EAAQrB,WArIX,MAsIZF,KAAKM,SAAW,IAAIN,KAAKM,SAAS6B,MAAM,EAAGD,MAAOlC,KAAKM,SAAS6B,MAAMD,EAAI,IAC1EA,IAEJ,CACF,CAEAE,QACQ,MAAAC,EAAO,IAAIhC,EAMV,OALA1E,OAAAiF,OAAOyB,EAAMrC,MACpBqC,EAAK7B,KAAO,CACVC,MAAO,IAAIT,KAAKQ,KAAKC,OACrBK,MAAO,IAAId,KAAKQ,KAAKM,QAEhBuB,CACT,EAGF,MAAMC,EAAQ,IAjFd,MAAAvC,cACEC,KAAAuC,OAAgB,EAAC,CAEjBC,cAAc3C,EAAY4C,GACpB,IAAAxC,EAAOD,KAAKuC,OAAOf,MAAMkB,GAAMA,EAAErD,MAAQQ,IF9EjC,IAAU8C,EAAaC,EAAWC,EEyFvC,OAVF5C,IACHA,QAAawC,EAAOH,MAAMQ,QAAQjD,GAC7BG,KAAAuC,OAAO5C,KAAKM,GACjBA,EAAK8C,YFlFeJ,EEmFlB1C,EAAK+C,UACH,uGFpF6BJ,EEKrB,GFLgCC,EEKhC,GFJT,IAAII,SAASC,IAClB,MAAMC,EAAM,IAAIC,MAAMR,EAAGC,GACzBM,EAAIE,OAAS,KACXH,EAAQC,EAAG,EAETA,EAAAG,QAAWC,IAELC,QAAAC,IAAI,cAAeF,GAC3BL,EAAQ,KAAI,EAEdC,EAAIO,IAAMf,CAAA,ME8EH1C,CACT,GAyEK,SAAS0D,EAAUC,GAxL1B,IAAAlC,EAyLQ,MAAAmC,EAAQD,EAAMC,OA1KA,+GA2KdC,EAAa,OAAApC,EAAMkC,EAAAE,YAAcpC,EAAA,aAEjCqC,EAAmBC,EAAAA,uBAClBzE,EAAS0E,GAAcC,WAAS,IAChCC,EAAWC,GAAgBF,WAAc,OACzCG,EAAWC,GAAgBJ,EAAAA,SAA2B,KACtDK,EAAUC,GAAeN,EAAAA,SAAiC,CAAE,IAC5DO,EAAOC,GAAYR,YAAS,IAAM,IAAI7D,IACvCsE,EAASC,EAAAA,YACTnC,EAASoC,EAAAA,UAAU,CAACf,eAEpBgB,EAAgBC,eAAaC,IAC3B,MAAAlG,EAvHV,SAA2BA,GACzB,MAAMmG,EAA0C,CAAA,EAChD,IAAA,MAAWjG,KAAOF,EACX,YAAYoG,KAAKlG,EAAIK,OACxB4F,EAAQjG,EAAIK,KAAOL,GAGvB,IAAA,MAAWA,KAAOF,EAChB,GAAI,YAAYoG,KAAKlG,EAAIK,KAAM,CACvB,MAAAQ,EAAKD,EAAaZ,EAAIK,KAC5B4F,EAAQpF,GAAMlE,OAAOiF,OAAO5B,EAAK,CAACK,IAAKQ,GACzC,CAEK,OAAAlE,OAAOwJ,OAAOF,EACvB,CAyGiBG,CAAkBJ,GAC/Bf,EAAWnC,KAAKuD,OAAOvG,EAAK4B,IAAIjF,KAChC6I,EAAaxF,GACD0F,EAAA3F,EAAiBC,IACpB4F,EAAA,IAAIrE,EAAUvB,GAAK,GAC3B,IAEGwG,EAAiBP,EAAAA,aACrBvC,UACE,MAAMxD,EAAMuG,EAAOC,OACnB,GAAIxG,EAAK,CACHA,EAAAK,IAAMO,EAAaZ,EAAIK,KAE3B,MAAMkB,EAA2C,CAAA,EACjD,IAAA,MAAWI,KAAK0D,EACd9D,EAASI,EAAEtB,KAAOsB,EAGhB,IAAA8E,EACE,MAAA3G,EAAO,IAAIuF,GACXqB,EAAMrB,EAAUsB,WAAWhF,GAAMA,EAAEtB,MAAQL,EAAIK,MACjDqG,GAAO,GACTD,EAAS3G,EAAK4G,GACd5G,EAAK4G,GAAO1G,GAEZF,EAAKa,KAAKX,GAEZsF,EAAaxF,GACD0F,EAAA3F,EAAiBC,IAC7BmF,EAAWnC,KAAKuD,OAAOvG,EAAK4B,IAAIjF,KAE1B,MAAAmK,EAAWnB,EAAMrC,QAEjByD,EAAUrG,EAASiG,GAAU,CAAA,GAAItE,QACpCtB,GAAOA,IAAOb,EAAIK,KAAwB,OAAjBkB,EAASV,KAE/BiG,EAAUtG,EAASR,GAAKmC,QAAQtB,GAAOA,IAAOb,EAAIK,KAAwB,OAAjBkB,EAASV,KAExE,IAOIyB,EAPAyE,GAAgBC,EAAAA,QAAUH,EAASC,GACnCC,IACOH,EAAApF,KAAKM,MAAQ8E,EAASpF,KAAKM,MACjCK,QAAQ8E,GAAMA,EAAEhF,OAAOpB,KAAOb,EAAIK,MAClC6G,OAAOJ,EAAQpF,KAAKM,IAAS,CAACC,OAAQjC,EAAIK,IAAK6B,OAAQF,QAI5D,MAAMmF,EAAU1B,EAAMjE,KAAKC,MAAMkF,WAAWS,GAAMA,EAAEpH,KAAOoH,EAAEpH,IAAIK,MAAQL,EAAIK,MACzE8G,GAAW,GACH7E,EAAAmD,EAAMjE,KAAKC,MAAM0F,GAC3B7E,EAAQtC,IAAMA,IAEdsC,EAAU,CAACzB,GAAIb,EAAIK,IAAKwB,KAAM,WAAY7B,OACjC4G,EAAApF,KAAKC,MAAMd,KAAK2B,GACVyE,GAAA,GAEbA,GACFrB,EAASkB,GAGX,MAAM3F,QAAaqC,EAAMQ,QAAQyC,EAAOc,SAAU5D,GAC5CgC,EAAApD,WAAWpB,EAAMqB,EAAO,MAChC,GAAiC,cAAtBiE,EAAOe,WAA4B,CACtC,MAAAC,EAAQ3G,EAAa2F,EAAOiB,YAE5B1H,EAAOuF,EAAUlD,QAAQR,GAAMA,EAAEtB,MAAQkH,IAC/CjC,EAAaxF,GACD0F,EAAA3F,EAAiBC,IAC7BmF,EAAWnC,KAAKuD,OAAOvG,EAAK4B,IAAIjF,KAE1B,MAAAmK,EAAWnB,EAAMrC,QACvBwD,EAASpF,KAAKM,MAAQ8E,EAASpF,KAAKM,MAAMK,QACvC8E,GAAMA,EAAEhF,OAAOpB,KAAO0G,GAASN,EAAE/E,OAAOrB,KAAO0G,IAEzCX,EAAApF,KAAKC,MAAQmF,EAASpF,KAAKC,MAAMU,QAAQiF,GAAMA,EAAEvG,KAAO0G,IACjE7B,EAASkB,EACX,IAEF,CAACvB,EAAWI,EAAOhC,KC5PhB,SACLoB,EACA4C,EACAC,EACAjE,GAEAkE,EAAAA,WAAU,KACRlE,EAAOmE,MAAM/C,GAAOgD,MAAMrB,IACxBiB,EAAQjB,EAAM,GACf,GAEA,IAAIkB,EAAcjE,GACvB,CDkPEqE,CAAkBjD,EAAOiB,EAAe,GAAIrC,GCjRvC,SACLoB,EACAkD,EACAC,EACAC,EACAP,EACAjE,GAEAkE,EAAAA,WAAU,KACF,MAAAO,EAAezE,EAAO0E,OAAOtD,EAAOkD,EAAQC,GAASI,WAAW7B,IACpE0B,EAAS1B,EAAM,IAEjB,MAAO,KACL2B,EAAaG,aAAY,CAC3B,GAEC,IAAIX,EAAcjE,GACvB,CDiQY6E,CAAAzD,EAAO,CAAC,EAAG,GAAIyB,EAAgB,CAACjB,EAAWI,GAAQhC,GAC7DkE,EAAAA,WAAU,KACR,MAAMY,EAAWC,aAAY,IAAM/C,EAAMxC,gBAAgB,KAClD,MAAA,IAAMwF,cAAcF,EAAQ,GAClC,CAAC9C,IAEE,MAAA9H,EAAQ+K,aAAWC,OAEzB,OACGC,EAAAA,IAAA3K,EAAA,CAAaN,QACZkL,SAACC,EAAAA,KAAAzL,EAAA,CAAUM,QACTkL,SAAA,CAACD,EAAAA,IAAAjK,EAAA,CAAOhB,QACLkL,UAtQaE,EAsQExD,GF9PAyD,EEPVrM,OAAOsM,KAAKF,GFOUG,EEPA1J,GAAYuJ,EAAOvJ,IAAY,EFQ5DwJ,EAAMG,MAAK,CAACC,EAAGC,KACd,MAAAC,EAAKJ,EAAEE,GACPG,EAAKL,EAAEG,GAEb,OAAOC,EAAKC,GAAU,EAAAD,EAAKC,EAAK,EAAI,CAAA,KEXnCC,UACArG,MAAM,EAAG,KAmQsBzB,KAAKlC,GAC5BsJ,EAAAA,KAAA/J,EAAA,CACC0K,UAAW,cAEXC,MAAO,CAACC,MAAOC,EAAgBpK,GAASqK,MAExChB,SAAA,CAACD,EAAAA,IAAA3J,EAAA,CAAYtB,UACZiL,EAAAA,IAAA,MAAA,CAAKC,WAAcrJ,OAJfA,OAQV2F,GAAcyD,EAAAA,IAAAzK,EAAA,CAAUR,QAAekL,SAAA3I,EAASiF,EAAUnF,OAE1D4I,EAAAA,IAAAkB,EAAAA,aAAA,CACCC,UAAWtE,EAAMjE,KACjBwI,gBAAgB,QAChBC,gBAAgB,EAChBC,YAAcC,GAAc/E,EAAa+E,GACzCC,YAAcD,IACLxE,EAAA0E,eAAe,OAAQ,CAACxJ,GAAIsJ,EAAKnK,IAAIK,IAAKiK,aAAcH,EAAKnK,IAAIC,OAAM,EAEhFsK,UAAW,IAAMC,EAAAA,KAAKC,EAAAA,KAAK,KAAKzM,IAAK,KACrC0M,UAAW,IAAM,GACjBC,YAAa,EACbC,QAAUT,GAAc7J,EAAS6J,EAAKnK,IAAKO,GAC3CsK,kBAAmB,CAACC,EAAKC,KACZ,IAAA,MAAAxI,KAAWkD,EAAMnE,SAAU,CACpC,MAAM6I,EAAO1E,EAAMjE,KAAKC,MAAMe,MAAM4E,IA7TlD1E,IAAAA,EA6T0D,OAAA0E,EAAApH,KAAOoH,EAAEpH,IAAIK,OAAQ,OAAAqC,EAAS,MAATH,OAAS,EAAAA,EAAAvC,YAAT0C,EAAcrC,IAAA,IAC/E,GAAI8J,EAAM,CACR,MAAMa,EAtSF,IAuSE5J,EAAQmB,EAAQnB,MAChB3C,EAASqE,KAAKmI,KAAK3K,EAAS6J,EAAKnK,IAAKO,IACtCwD,EAAQxB,EAAQtB,KAAK8C,MACrBmH,EAAYnG,EAAiBoG,IAAI5I,EAAQtB,KAAKmK,aAAaC,MAAM,KAAKrN,IACtEsN,EAAW7M,EAASsM,EAAc,GAClCQ,EAAOxH,EAAQA,EAAMyH,MAAQ,EAC7BC,EAAO1H,EAAQA,EAAM2H,OAAS,EAC9BC,EAAIxB,EAAKwB,EAAK7I,KAAK8I,IAAIxK,GAASkK,EAAYP,EAC5Cc,EAAI1B,EAAK0B,EAAK/I,KAAKgJ,IAAI1K,GAASkK,EAAYP,EAElDD,EAAIiB,OACA,IAsBF,GArBAjB,EAAIkB,YAAc5M,EAChB,EAAI0D,KAAKmJ,IAAIjB,EAAiBpI,KAAKC,MAAQN,EAAQrB,YAAc8J,GAEnEF,EAAIoB,KAAO,QAAAhF,OAAQpE,KAAKqJ,MAAM,GAAKpB,GAAW,iBAE9CD,EAAIsB,YACJtB,EAAIuB,YAAc7B,EAAAA,KAAK8B,EAAMA,MAAAtO,IAAK,GAClC8M,EAAIyB,UAAY,EAAIxB,EAChBD,EAAA0B,OACFrC,EAAKwB,EAAK7I,KAAK8I,IAAIxK,IAAUkK,EAAWC,EAAO,GAAMR,EACrDZ,EAAK0B,EAAK/I,KAAKgJ,IAAI1K,IAAUkK,EAAWG,EAAO,GAAMV,GAEvDD,EAAI2B,OAAOtC,EAAKwB,EAAI7I,KAAK8I,IAAIxK,GAAS3C,EAAQ0L,EAAK0B,EAAI/I,KAAKgJ,IAAI1K,GAAS3C,GACzEqM,EAAI4B,SAEJ5B,EAAIsB,YACJtB,EAAIuB,YAAc7B,EAAAA,KAAK8B,EAAMA,MAAAtO,IAAK,GAClC8M,EAAIyB,UAAY,EAAIxB,EAChBD,EAAA6B,IAAIxC,EAAKwB,EAAGxB,EAAK0B,EAAGpN,EAAQ,EAAG,EAAIqE,KAAKE,IAAI,GAChD8H,EAAI4B,SAEA3I,EAAO,CACT+G,EAAIiB,OAEA,IACF,MAAMa,EAAM,IACN1D,EAAI5J,EACRwD,KAAKuD,IAAI,GAAIuG,GAAOhK,KAAKC,MAAQN,EAAQpB,YAAcyL,IAGrD1D,EAAI,IACN4B,EAAIsB,YACAtB,EAAA+B,UAAYrC,EAAAA,KAAKU,EAAWhC,GAC5B4B,EAAA6B,IAAIhB,EAAGE,GAAIN,EAAO,EAAI,IAAMR,EAAa,EAAG,EAAIjI,KAAKE,IAAI,GAC7D8H,EAAIjB,QAGNiB,EAAIsB,YACJtB,EAAI+B,UAAYrC,EAAAA,KAAK8B,EAAMA,MAAAtO,IAAK,GAC5B8M,EAAA6B,IAAIhB,EAAGE,EAAGN,EAAOR,EAAc,EAAG,EAAG,EAAIjI,KAAKE,IAAI,GACtD8H,EAAIgC,OAEAhC,EAAAiC,UACFhJ,EACA4H,EAAIJ,EAAOR,EAAc,EACzBc,EAAIJ,EAAOV,EAAc,EACzBQ,EAAOR,EACPU,EAAOV,GAGTD,EAAIuB,YAActO,EAAMA,MAAAC,IACxB8M,EAAIyB,UAAY,EAAIxB,EACpBD,EAAI4B,SAEJ5B,EAAIuB,YAAcnB,EAClBJ,EAAIyB,UAAY,EAAIxB,EACpBD,EAAI4B,QAGN,CAFE,QACA5B,EAAIkC,SACN,CACF,CAEAlC,EAAIsB,YACJtB,EAAIuB,YAAc7B,EAAAA,KAAKzM,EAAMA,MAAAC,IAAK,GAClC8M,EAAIyB,UAAY,GAAMxB,EAClBD,EAAA6B,IAAIhB,EAAGE,EAAGN,EAAOR,EAAc,EAAG,EAAG,EAAIjI,KAAKE,IAAI,GACtD8H,EAAI4B,SAEJ,MAAMO,EAAQ7L,GAAS0B,KAAKE,GAAK,GAAK5B,EAAkB,IAAV0B,KAAKE,GAC7CkK,EAAQD,EACVpB,GAAKJ,EAAO,EAAI,GAAKV,EACrBc,GAAKJ,EAAO,EAAI,GAAKV,EACzBD,EAAI+B,UAAYrC,EAAAA,KAAK8B,EAAMA,MAAAtO,IAAK,GAChC8M,EAAIqC,UAAY,SACZrC,EAAAsC,aAAeH,EAAQ,SAAW,MACtCnC,EAAIuC,SAAS9K,EAAQtB,KAAKmK,YAAaO,EAAGuB,EAG5C,CAFE,QACApC,EAAIkC,SACN,CACF,CACF,GAEFM,iBAAkB,CAACnD,EAAWW,EAAKC,KAEjC,GACO,aADCZ,EAAKtI,KACM,CACf,MAAM0L,EAAY3D,EAAgBO,EAAKnK,IAAIC,OACrCxB,EAASqE,KAAKmI,KAAK3K,EAAS6J,EAAKnK,IAAKO,IACtCiN,EAAW1K,KAAKmJ,IAAI,IAAK,GAAOlB,GAalC,GAXJD,EAAIsB,YACJtB,EAAI+B,UACY,OAAd1H,GAAsBgF,EAAKnK,IAAIK,MAAQ8E,EAAUnF,IAAIK,IACjDmK,OAAKC,EAAKA,KAAA,KAAKzM,IAAK,IACpBuP,EAAU1D,KAChBiB,EAAIuB,YAAckB,EAAUE,OAC5B3C,EAAIyB,UAAY,GACZzB,EAAA6B,IAAIxC,EAAKwB,EAAGxB,EAAK0B,EAAGpN,EAAQ,EAAG,EAAIqE,KAAKE,IAAI,GAChD8H,EAAI4B,SACJ5B,EAAIjB,OAEApL,EAASsM,EAAc,GAAI,CAC7BD,EAAIoB,KAAUsB,GAAAA,OAAAA,EAAA,iBACR,MAAA5J,EAAa,EAATnF,EAAa,GAAKsM,EAC5B,IAAA,IAAS2C,EAAM,GAAIA,GAAO,EAAGA,GAAO,IAAK,CACjC,MAAAC,GFxYDlL,EEwYkBvC,EAASiK,EAAKnK,KFxYrB4N,EEwY2B9K,KAAKqJ,MAAMuB,GFvYpEjL,EAAErF,OAASwQ,EACb,GAAA1G,OAAUzE,EAAEhD,UAAU,EAAGmO,GAAK,KAEzBnL,GEsYe,GADgBqI,EAAI+C,YAAYF,GACpBnC,MAAQ5H,EAAG,CAEzBkH,EAAIqC,UAAY,SAChBrC,EAAIsC,aAAe,MAEnBtC,EAAIuB,YAAc7B,EAAAA,KAAKzM,EAAMA,MAAAC,IAAK,IAClC8M,EAAIyB,UAAY,EAAIxB,EAChBD,EAAAgD,WAAWH,EAAOxD,EAAKwB,EAAGxB,EAAK0B,EAAIpN,EAAS,EAAIsM,GAEhDD,EAAAuC,SAASM,EAAOxD,EAAKwB,EAAGxB,EAAK0B,EAAIpN,EAAS,EAAIsM,GAClD,KACF,CACF,CACF,CACF,CFxZE,IAAStI,EAAWmL,CEwZtB,EAGJG,iBAAkB,CAAC3L,EAAW0I,EAAKC,KACjCD,EAAIsB,YACJtB,EAAIuB,YAAc7B,OAAKC,EAAAA,KAAK,KAAKzM,IAAK,MACtC8M,EAAIyB,UAAY,EAAIxB,EACpBD,EAAI0B,OAAOpK,EAAKH,OAAO0J,EAAGvJ,EAAKH,OAAO4J,GACtCf,EAAI2B,OAAOrK,EAAKF,OAAOyJ,EAAGvJ,EAAKF,OAAO2J,GACtCf,EAAI4B,QAAO,SAlbvB,IAAwB3D,EFQEC,EAAYE,CEgbtC,CAEA,MAAM8E,EAAa,CAAA,EACnB,IAAIC,EAAe,EAEnB,SAASrE,EAAgBpK,GACvB,GAAIwO,EAAWxO,GACb,OAAOwO,EAAWxO,GAGd,MAAA0O,EAAMC,EAAWA,WAAAF,EAAeE,EAAWA,WAAA/Q,QASjD,OAPgB6Q,GAAA,EAEhBD,EAAWxO,GAAW,CACpBqK,KAAMuE,EAAAA,KAAKF,GAAK,KAAKlQ,IACrByP,OAAQjD,EAAAA,KAAKzM,QAAMC,IAAK,KAGnBgQ,EAAWxO,EACpB,CE9da,MAAA6O,EAAgB,IAC1BzF,EAAAA,IAAA,MAAA,CAAI0F,MAAM,6BAA6B9C,MAAM,MAAME,OAAO,MAAM6C,QAAQ,oBACvE1F,SAACD,EAAAA,IAAA,OAAA,CACCiB,KAAK,eACLlI,EAAE,g0BCJK6M,EAAmBC,EAAAA,cAA8B,WAAkC,IAAjCC,EAA0BC,UAAAvR,OAAA,QAAAwR,IAAAD,UAAA,GAAAA,UAAA,GAAA,GAChF,MAAA,CACLvO,KAAM,6BAENyO,MAAQC,GACC,IACFA,EACH,CACE1O,KAAM,qBACND,MAAO,QACP4O,KAAMV,EACNW,UAAW,WACT,OAAQpG,EAAAA,IAAAjE,EAAAsK,EAAA,CAAA,EAAcP,GACxB,EACA/I,OAAQuJ,EAAAA,MAAMC,OAAO,0BAK/B"}
|