Append __wsmode__=9 to relay*.jd.com/file/design?xxx URLs
// ==UserScript==
// @name JD Relay URL Modifier
// @namespace http://tampermonkey.net/
// @version 0.3.7
// @description Append __wsmode__=9 to relay*.jd.com/file/design?xxx URLs
// @author Your Name
// @match https://relay.jd.com/file/*
// @match *://relay-test.jd.com/file/*
// @match *://relay0.jd.com/file/*
// @match *://ling.jd.com/file/*
// @match *://ling-design.jd.com/file/*
// @match *://ling-test.jd.com/file/*
// @match *://ling-pre.jd.com/file/*
// @grant none
// @license MIT
// ==/UserScript==
; (function () {
"use strict"
const inFile = /\/file\/(design|chat)/.test(window.location.pathname)
// 从 localStorage 获取状态
let isEnabled = localStorage.getItem("wsmodeEnabled") === "true"
// 创建按钮容器
let container = document.createElement("div")
container.id = "wsmode-container"
container.style.position = "fixed"
container.style.bottom = localStorage.getItem("buttonBottom") || "60px"
container.style.right = localStorage.getItem("buttonRight") || "10px"
container.style.zIndex = "1000"
container.style.display = "flex"
container.style.gap = "10px"
document.body.appendChild(container)
// 创建隐身按钮
let button = document.createElement("button")
button.id = "wsmode-button"
// 幽灵图标
const ghostIconEnabled = '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 16 16"><path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0-1A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM6 3.5a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-1 0V4a.5.5 0 0 1 .5-.5zm4 0a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-1 0V4a.5.5 0 0 1 .5-.5zM8 7a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5z"/></svg>'
const ghostIconDisabled = '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 16 16"><path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0-1A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM6 3.5a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-1 0V4a.5.5 0 0 1 .5-.5zm4 0a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-1 0V4a.5.5 0 0 1 .5-.5zM8 7a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5z" opacity="0.5"/></svg>'
const debugIcon = '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 16 16"><path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0-1A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM4 8a.5.5 0 0 1 .5-.5h3v-3a.5.5 0 0 1 1 0v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1z"/></svg>'
button.innerHTML = isEnabled ? `${ghostIconEnabled}` : `${ghostIconDisabled}`
button.style.padding = "10px 15px"
button.style.border = "none"
button.style.borderRadius = "5px"
button.style.color = "white"
button.style.cursor = "pointer"
button.style.display = "flex"
button.style.alignItems = "center"
button.style.gap = "6px"
button.style.fontSize = "14px"
button.style.fontWeight = "500"
button.style.transition = "background-color 0.2s ease, transform 0.1s ease"
container.appendChild(button)
// 创建调试按钮
let debugButton = document.createElement("button")
debugButton.id = "debug-button"
debugButton.innerHTML = debugIcon
debugButton.style.padding = "10px 15px"
debugButton.style.border = "none"
debugButton.style.borderRadius = "5px"
debugButton.style.color = "white"
debugButton.style.cursor = "pointer"
debugButton.style.display = "flex"
debugButton.style.alignItems = "center"
debugButton.style.gap = "6px"
debugButton.style.fontSize = "14px"
debugButton.style.fontWeight = "500"
debugButton.style.transition = "background-color 0.2s ease, transform 0.1s ease"
debugButton.style.backgroundColor = "#2196F3"
container.appendChild(debugButton)
// 调试按钮点击事件
debugButton.addEventListener("click", () => {
if (typeof window.__DEBUG === "function") {
window.__DEBUG()
} else {
console.warn("window.__DEBUG is not defined")
}
})
// 设置按钮初始状态
button.style.backgroundColor = isEnabled ? "#4CAF50" : "#9E9E9E"
// 确保按钮在窗口视野内
function ensureButtonInView() {
const rect = container.getBoundingClientRect()
const maxX = window.innerWidth - rect.width
const maxY = window.innerHeight - rect.height
let currentRight = parseFloat(container.style.right) || 0
let currentBottom = parseFloat(container.style.bottom) || 0
// 如果按钮超出右边界,吸附到右边
if (currentRight < 0) {
container.style.right = "10px"
}
// 如果按钮超出底边界,吸附到底边
if (currentBottom < 0) {
container.style.bottom = "60px"
}
// 保存调整后的位置
localStorage.setItem("buttonRight", container.style.right)
localStorage.setItem("buttonBottom", container.style.bottom)
}
// 窗口缩放时确保按钮在视野内
window.addEventListener("resize", ensureButtonInView)
// 页面加载时也检查一次
setTimeout(ensureButtonInView, 100)
// 按钮拖拽功能
let isDragging = false
let offsetX, offsetY
let dragStartTime = 0
let hasMoved = false
container.addEventListener("mousedown", (e) => {
isDragging = true
hasMoved = false
dragStartTime = Date.now()
offsetX = e.clientX - container.getBoundingClientRect().left
offsetY = e.clientY - container.getBoundingClientRect().top
container.style.transition = "none" // 禁用动画
container.style.opacity = "0.8" // 拖动时透明度变化
})
document.addEventListener("mousemove", (e) => {
if (isDragging) {
hasMoved = true
const newRight = window.innerWidth - e.clientX - (container.offsetWidth - offsetX)
const newBottom = window.innerHeight - e.clientY - (container.offsetHeight - offsetY)
container.style.right = `${Math.max(0, newRight)}px`
container.style.bottom = `${Math.max(0, newBottom)}px`
}
})
document.addEventListener("mouseup", (e) => {
if (isDragging) {
const dragEndTime = Date.now()
const dragDuration = dragEndTime - dragStartTime
container.style.opacity = "1" // 恢复透明度
container.style.transition = "background-color 0.2s ease" // 恢复动画
// 保存位置到 localStorage
localStorage.setItem("buttonRight", container.style.right)
localStorage.setItem("buttonBottom", container.style.bottom)
// 只有在非拖拽情况下才触发点击事件(短时间内没有移动鼠标)
if (!hasMoved && dragDuration < 200 && e.target === button) {
toggleMode()
}
isDragging = false
}
})
// 抽取切换模式的逻辑为单独的函数
function toggleMode() {
const previousState = isEnabled
isEnabled = !isEnabled
localStorage.setItem("wsmodeEnabled", isEnabled)
button.style.backgroundColor = isEnabled ? "#4CAF50" : "#9E9E9E"
button.innerHTML = isEnabled ? `${ghostIconEnabled}` : `${ghostIconDisabled}`
// 通知其他页面
localStorage.setItem("wsmodeChanged", Date.now())
// 如果在 inFile 页面并且 wsmode 状态有变化,刷新页面
if (inFile && previousState !== isEnabled) {
if (isEnabled && !url.searchParams.has("__wsmode__")) {
url.searchParams.append("__wsmode__", "9")
} else if (!isEnabled && url.searchParams.has("__wsmode__")) {
url.searchParams.delete("__wsmode__")
}
window.location.replace(url.toString())
}
}
// 获取当前 URL
let url = new URL(window.location.href)
// 检查是否已经有 __wsmode__ 参数并且功能开启
if (inFile) {
if (isEnabled && !url.searchParams.has("__wsmode__")) {
// 添加 __wsmode__ 参数
url.searchParams.append("__wsmode__", "9")
// 重定向到新的 URL
window.location.replace(url.toString())
}
}
// 监听新打开的 /file/design 页面
window.addEventListener("message", (event) => {
console.log("Received message from relay.jd.com:", event)
if (
event.origin === location.origin &&
event.data === "checkWsmode"
) {
event.source.postMessage({ wsmodeEnabled: isEnabled }, event.origin)
}
})
// 在 /file/design 页面中检查 __wsmode__ 参数
if (inFile) {
window.addEventListener("message", (event) => {
if (
event.origin === location.origin &&
typeof event.data.wsmodeEnabled !== "undefined"
) {
if (event.data.wsmodeEnabled) {
if (!url.searchParams.has("__wsmode__")) {
url.searchParams.append("__wsmode__", "9")
window.location.replace(url.toString())
}
} else {
if (url.searchParams.has("__wsmode__")) {
url.searchParams.delete("__wsmode__")
window.location.replace(url.toString())
}
}
}
})
}
// 监听 localStorage 变化
window.addEventListener("storage", (event) => {
if (event.key === "wsmodeChanged") {
isEnabled = localStorage.getItem("wsmodeEnabled") === "true"
button.style.backgroundColor = isEnabled ? "#4CAF50" : "#9E9E9E"
button.innerHTML = isEnabled ? `${ghostIconEnabled}` : `${ghostIconDisabled}`
if (inFile) {
if (isEnabled && !url.searchParams.has("__wsmode__")) {
url.searchParams.append("__wsmode__", "9")
window.location.replace(url.toString())
} else if (!isEnabled && url.searchParams.has("__wsmode__")) {
url.searchParams.delete("__wsmode__")
window.location.replace(url.toString())
}
}
}
})
})()