您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
gitea stl preview with threejs
// ==UserScript== // @name gitea stl preview // @namespace http://tampermonkey.net/ // @version 2025-04-12.2 // @description gitea stl preview with threejs // @author You // @match https://gitea/** // @icon https://www.google.com/s2/favicons?sz=64&domain=undefined.gitea // @grant unsafeWindow // @license MIT // ==/UserScript== if(!(location.href.match(/.*\.stl$/)||location.href.match(/\/commit\//))){ return } const url=new URL(location.href) const s = document.createElement('script'); s.type="importmap"; const threeVersion = '0.160.1'; s.textContent = ` { "imports": { "three": "https://cdn.jsdelivr.net/npm/three@${threeVersion}/build/three.module.js", "three/examples/jsm/loaders/STLLoader.js": "https://cdn.jsdelivr.net/npm/three@${threeVersion}/examples/jsm/loaders/STLLoader.js", "three/examples/jsm/controls/OrbitControls.js": "https://cdn.jsdelivr.net/npm/three@${threeVersion}/examples/jsm/controls/OrbitControls.js" } } `; document.head.appendChild(s); const loadThree = () => new Promise((resolve, reject) => { const s = document.createElement('script'); s.type="module"; unsafeWindow.threeRes=resolve s.textContent = ` import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; import * as THREE from 'three'; import { STLLoader } from 'three/examples/jsm/loaders/STLLoader.js'; threeRes({ OrbitControls, THREE, STLLoader }) `; s.onerror = reject; document.head.appendChild(s); }); function setupScene({THREE,OrbitControls},parent=document.body){ const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ); const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); parent.appendChild(renderer.domElement); const ambient = new THREE.AmbientLight(0x404040); const directionalLight = new THREE.DirectionalLight(0xffffff, 1); // Directional light directionalLight.position.set(1, 1, 1).normalize(); // Position the light source scene.add(ambient, directionalLight); camera.position.z = 5; // Animation loop function animate() { requestAnimationFrame(animate); //cube.rotation.x += 0.01; // cube.rotation.y += 0.01; renderer.render(scene, camera); } animate(); camera.position.set(0, 0, 100); const controls = new OrbitControls(camera, renderer.domElement); controls.target.set(0, 0, 0); controls.update(); // Handle window resize window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); return {scene,controls}; } async function diffViews(){ while(!document.body){ await new Promise(res=>setTimeout(res,10)); } const fileBoxes=[...document.querySelectorAll("#diff-file-boxes .diff-file-box")].filter(b=>b.getAttribute("data-old-filename").endsWith(".stl")); const parentCommitLink=[...document.querySelectorAll('a[href*=commit]')].filter(a=>!a.href.includes(url.pathname.split("commit/")[1]))[0] const parentCommmit=parentCommitLink.href.split("commit/")[1] if(fileBoxes.length){ const threePr=loadThree() for(const filebox of fileBoxes){ //https://gitea/admin-user-account/model-test/src/commit/9169b6c3f531871969463ef76ee7602204baedd9/reduced-hook.stl const urlOld=new URL(url) urlOld.pathname=urlOld.pathname.replace(/\/commit\/.*(\/|$)/,`/raw/commit/${parentCommmit}`) urlOld.pathname+="/"+filebox.getAttribute("data-old-filename") const newUrl=new URL(location.href.replace(/\/commit\//,`/raw/commit/`)) newUrl.pathname+="/"+filebox.getAttribute("data-new-filename") threePr.then(async (api)=>{ const {scene,controls}= setupScene(api,filebox) const THREE=api.THREE; const loader = new api.STLLoader(); let oldRes const oldPr=new Promise(res=>{ oldRes=res; }) let newRes const newPr=new Promise(res=>{ newRes=res; }) loader.load(urlOld.href, function (geometry) { const material = new THREE.MeshPhongMaterial({ color: "red" }); const mesh = new THREE.Mesh(geometry, material); geometry.computeBoundingBox(); const center = geometry.boundingBox.getCenter(new THREE.Vector3()); mesh.geometry.center(); // Center the model mesh.scale.set(0.5, 0.5, 0.5); const bbox = geometry.boundingBox; const size = new THREE.Vector3(); bbox.getSize(size); const maxDim = Math.max(size.x, size.y, size.z); // Position mesh in front of the camera mesh.position.z = -maxDim * -0.1; mesh.position.x = -80 scene.add(mesh); controls.target.copy(mesh.position); // Make controls orbit around the mesh controls.update(); oldRes(mesh.position); }); loader.load(newUrl.href, function (geometry) { const material = new THREE.MeshPhongMaterial({ color: "green" }); const mesh = new THREE.Mesh(geometry, material); geometry.computeBoundingBox(); const center = geometry.boundingBox.getCenter(new THREE.Vector3()); mesh.geometry.center(); // Center the model mesh.scale.set(0.5, 0.5, 0.5); const bbox = geometry.boundingBox; const size = new THREE.Vector3(); bbox.getSize(size); const maxDim = Math.max(size.x, size.y, size.z); // Position mesh in front of the camera mesh.position.z = -maxDim * -0.1; mesh.position.x = 80 scene.add(mesh); controls.target.copy(mesh.position); // Make controls orbit around the mesh controls.update(); newRes(mesh.position); }); const positions=await Promise.all([oldPr,newPr]) controls.target.copy(new THREE.Vector3().addVectors(...positions).multiplyScalar(0.5)); controls.update(); }) } } } if(location.href.match(/\/commit\//)){ diffViews(); return; }else{ loadThree().then(async (api)=>{ const {scene,controls}= setupScene(api) const THREE=api.THREE; const loader = new api.STLLoader(); loader.load(location.href.replace("/src/","/raw/"), function (geometry) { const material = new THREE.MeshPhongMaterial({ color: 0x2194ce }); const mesh = new THREE.Mesh(geometry, material); geometry.computeBoundingBox(); const center = geometry.boundingBox.getCenter(new THREE.Vector3()); mesh.geometry.center(); // Center the model mesh.scale.set(0.5, 0.5, 0.5); const bbox = geometry.boundingBox; const size = new THREE.Vector3(); bbox.getSize(size); const maxDim = Math.max(size.x, size.y, size.z); // Position mesh in front of the camera mesh.position.z = -maxDim * 0.6; scene.add(mesh); controls.target.copy(mesh.position); // Make controls orbit around the mesh controls.update(); }); }) } /* */