您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
JSONエディター画面を作る
// ==UserScript== // @name Scratch JSON Editor // @namespace https://scratch.mit.edu/ // @version 2025-09-09 // @description JSONエディター画面を作る // @author You // @match https://scratch.mit.edu/projects/* // @grant none // @run-at document-start // @license MIT // ==/UserScript== (async () => { 'use strict'; /** @param {() => unknown} listener */ const onLoaded = listener => { if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", listener); } else { listener(); } }; if (/^\/projects\/\d+\/json\/?/.test(location.pathname)) { const id = Number(location.pathname.match(/^\/projects\/(\d+)\/json\/?/)[1]); onLoaded(() => { /** @type {HTMLDivElement} */ const boxContent = document.querySelector('.box-content'); boxContent.innerHTML = 'Loading...'; }); const xtoken = await fetch('/session', { headers: { 'X-Requested-With': 'XMLHttpRequest' } }) .then(r => r.json()) .then(r => r.user?.token); const { title, project_token: ptoken } = await fetch(`https://api.scratch.mit.edu/projects/${id}`, { headers: { 'X-Token': xtoken } }) .then(r => r.json()); onLoaded(() => { /** @type {HTMLDivElement} */ const boxHead = document.querySelector('.box-head'); boxHead.style.paddingTop = '7px'; boxHead.innerHTML = `<h2><a href="/projects/${id}/"></a> » JSON Editor</h2>`; boxHead.querySelector('a').innerText = title; }); const json = await fetch(`https://projects.scratch.mit.edu/${id}?token=${ptoken}`) .then(r => r.text()); onLoaded(() => { /** @type {HTMLDivElement} */ const boxContent = document.querySelector('.box-content'); boxContent.innerHTML = ''; const textarea = document.createElement('textarea'); const button = document.createElement('button'); const status = document.createElement('span'); button.innerText = '保存'; textarea.value = json; Object.assign(textarea.style, { margin: '0 auto', boxSizing: 'border-box', width: 'calc(100% - 40px)', height: '30em', resize: 'vertical', fontFamily: 'monospace', }); Object.assign(button.style, { margin: '20px', }); Object.assign(status.style, { display: 'inline-block', width: '10em', }); boxContent.append(textarea, button, status); button.addEventListener('click', () => { status.innerText = ''; fetch(`https://projects.scratch.mit.edu/${id}`, { method: 'PUT', body: textarea.value, credentials: 'include', headers: { 'Content-Type': 'application/json' }, }).then(r => { status.innerText = String(r.status); }).catch(() => { status.innerText = 'Error'; }); }); }); } })();