将popup移植了过去

This commit is contained in:
xu_yanfeng 2024-01-08 19:50:06 +08:00
parent 64260e567d
commit fb028828f7
30 changed files with 711 additions and 680 deletions

View File

@ -49,10 +49,9 @@ const manifest: CocosPluginManifest = {
view_popup: "src/views/popup/index.ts",
script_background: "src/scripts/background.ts",
script_content: "src/scripts/content.ts",
script_inject: "src/scripts/inject.ts",
script_inject: "src/scripts/inject/index.ts",
},
};
// 这里的options变量名暂时不支持修改发布时会进行必要的修改
const options: CocosPluginOptions = {
server: {
enabled: true,

View File

@ -2,8 +2,12 @@
"author": "cc-plugin",
"description": "cocos creator plugin",
"devDependencies": {
"@types/chrome": "0.0.133",
"@types/fs-extra": "9.0.1",
"@types/kind-of": "^6.0.0",
"@types/lodash": "^4.14.176",
"@types/node": "16.11.12",
"@types/uuid": "^8.3.1",
"@xuyanfeng/cc-ui": "^0.2.14",
"cc-plugin": "file:.yalc/cc-plugin"
},
@ -19,5 +23,9 @@
"ccp-serve-web": "cc-plugin serve web",
"ccp-serve-chrome": "cc-plugin serve chrome"
},
"version": "1.0.0"
"version": "1.0.0",
"dependencies": {
"uuid": "^8.3.2",
"lodash": "^4.17.21"
}
}

View File

@ -1 +1,288 @@
console.log("background");
import { Msg, Page, PluginEvent } from "../core/types";
import { ChromeConst } from "cc-plugin/src/chrome/const";
// @ts-ignore
// import * as UA from "universal-analytics";
import { v4 } from "uuid";
import { FrameDetails } from "../views/devtools/data";
// 统计服务
const userID = localStorage.getItem("userID") || v4();
// UA("UA-134924925-3", userID);
console.log("on background");
class PortMan {
public currentUseContentFrameID = 0;
public content: Array<chrome.runtime.Port> = []; // 因为iframe的原因可能对应多个主iframe的id===0
public devtools: chrome.runtime.Port | null = null;
public id: number | null = null; // tab.id作为唯一标识
public title: string = "";
public url: string = "";
private mgr: PortManagement | null = null;
constructor(mgr: PortManagement, { id, url, title }: any) {
this.mgr = mgr;
this.id = id;
this.url = url;
this.title = title;
}
private onPortConnect(
port: chrome.runtime.Port,
onMsg: Function,
onDisconnect: Function
) {
console.log(`%c[Connect] ${port.name}`, "color:green");
port.onMessage.addListener((data: any, sender: any) => {
console.log(
`%c[Connect-Message] ${sender.name}\n${JSON.stringify(data)}`,
"color:blue;"
);
// 如果多个页面都监听 onMessage 事件,对于某一次事件只有第一次调用 sendResponse() 能成功发出回应,所有其他回应将被忽略。
// sender.postMessage(data);
onMsg && onMsg(data);
});
port.onDisconnect.addListener((port: chrome.runtime.Port) => {
console.log(`%c[Connect-Dis] ${port.name}`, "color:red");
onDisconnect && onDisconnect(port);
});
}
getCurrentUseContent(): chrome.runtime.Port | null {
return (
this.content.find(
(el) =>
el.sender?.frameId !== undefined &&
el.sender.frameId === this.currentUseContentFrameID
) || null
);
}
_updateFrames() {
let data: FrameDetails[] = this.content.map((item) => {
return {
url: item.sender?.url || "",
frameID: item.sender?.frameId || 0,
};
});
let event = new PluginEvent(
Page.Background,
Page.Devtools,
Msg.UpdateFrames,
data
);
this.sendDevtoolMsg(event);
}
dealConnect(port: chrome.runtime.Port) {
switch (port.name) {
case Page.Content: {
this.content.push(port);
this._updateFrames();
this.onPortConnect(
port,
(data: PluginEvent) => {
if (data.target === Page.Devtools) {
this.sendDevtoolMsg(data);
}
},
(disPort: chrome.runtime.Port) => {
const index = this.content.findIndex(
(el) =>
disPort.sender?.frameId !== undefined &&
el.sender?.frameId !== undefined &&
el.sender?.frameId === disPort.sender?.frameId
);
this.content.splice(index, 1);
this._updateFrames();
this.checkValid();
}
);
break;
}
case Page.Devtools: {
this.devtools = port;
this._updateFrames(); // 当devtools链接后主动派发frames数据
this.onPortConnect(
port,
(data: PluginEvent) => {
if (data.msg === Msg.UseFrame) {
this.currentUseContentFrameID = data.data;
// 更新这个frame的tree
this.updateCurrentFrameTree();
} else {
// 从devtools过来的消息统一派发到Content中
if (PluginEvent.check(data, Page.Devtools, Page.Background)) {
if (data.msg === Msg.TreeInfo) {
if (this.currentUseContentFrameID !== data.data) {
console.log(`frameID[${data.data}]不一致`);
}
}
PluginEvent.reset(data, Page.Background, Page.Content);
this.getCurrentUseContent()?.postMessage(data);
}
}
},
() => {
this.devtools = null;
this.checkValid();
}
);
break;
}
}
}
private updateCurrentFrameTree() {
const sendData = new PluginEvent(
Page.Background,
Page.Content,
Msg.Support
);
this.getCurrentUseContent()?.postMessage(sendData);
}
checkValid() {
if (!this.devtools && !this.content.length) {
this.mgr?.remove(this);
}
}
sendContentMsg(data: PluginEvent) {
this.getCurrentUseContent()?.postMessage(data);
}
sendDevtoolMsg(data: PluginEvent) {
this.devtools?.postMessage(data);
}
}
class PortManagement {
port: Array<PortMan> = [];
constructor() {
this.initConnect();
chrome.runtime.onMessage.addListener(
(request: PluginEvent, sender: any, sendResponse: any) => {
const tabID = sender.tab.id;
const portMan: PortMan | undefined = this.find(tabID);
if (portMan) {
if (PluginEvent.check(request, Page.Content, Page.Background)) {
// 监听来自content.js发来的事件将消息转发到devtools
PluginEvent.reset(request, Page.Background, Page.Devtools);
console.log(
`%c[Message]url:${sender.url}]\n${JSON.stringify(request)}`,
"color:green"
);
portMan.sendDevtoolMsg(request);
}
}
}
);
chrome.tabs.onActivated.addListener(({ tabId, windowId }) => {});
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
// 页面发生刷新,通知重新生成数据
if (changeInfo.status === "complete") {
const { id } = tab;
// -1为自己
if (id && id > -1) {
let portMan = this.find(id);
if (portMan) {
let data = new PluginEvent(
Page.Background,
Page.Content,
Msg.Support
);
portMan.sendContentMsg(data);
}
}
}
});
}
isDevtools(port: chrome.runtime.Port) {
const devtoolsUrl = `chrome-extension://${port.sender?.id}/${ChromeConst.html.devtools}`;
if (port.sender?.url === devtoolsUrl) {
}
}
initConnect() {
chrome.runtime.onConnect.addListener((port: chrome.runtime.Port) => {
if (port.name === Page.Devtools) {
// devtool链接过来没有port.sender.tab
chrome.tabs.getSelected((tab: chrome.tabs.Tab) => {
this._onConnect(tab, port);
});
} else {
const tab: chrome.tabs.Tab | undefined = port.sender?.tab;
if (tab) {
this._onConnect(tab, port);
}
}
});
}
private _onConnect(tab: chrome.tabs.Tab, port: chrome.runtime.Port) {
const { id, title, url } = tab;
if (id !== undefined && id > -1) {
let portMan: PortMan | undefined = this.find(id);
if (!portMan) {
portMan = new PortMan(this, { id, title, url });
this.port.push(portMan);
}
portMan.dealConnect(port);
}
}
find(id: number): PortMan | undefined {
return this.port.find((el) => el.id === id);
}
remove(item: PortMan) {
let index = this.port.findIndex((el) => el === item);
if (index > -1) {
this.port.splice(index, 1);
}
}
}
(window as any).backgroundInstance = new PortManagement();
function createPluginMenus() {
const menus = [];
let parent = chrome.contextMenus.create({
id: "parent",
title: "CC-Inspector",
});
chrome.contextMenus.create({
id: "test",
title: "测试右键菜单",
parentId: parent,
// 上下文环境,可选:["all", "page", "frame", "selection", "link", "editable", "image", "video", "audio"]默认page
contexts: ["page"],
});
chrome.contextMenus.create({
id: "notify",
parentId: parent,
title: "通知",
});
chrome.contextMenus.onClicked.addListener(function (info, tab) {
if (info.menuItemId === "test") {
alert("您点击了右键菜单!");
} else if (info.menuItemId === "notify") {
chrome.notifications.create("null", {
type: "basic",
iconUrl: "icons/48.png",
title: "通知",
message: "测试通知",
});
}
});
}
chrome.contextMenus.removeAll(function () {
createPluginMenus();
});

View File

@ -1 +1,61 @@
console.log("content.ts");
// content.js 和原始界面共享DOM具有操作dom的能力
// 但是不共享js,要想访问页面js,只能通过注入的方式
import {injectScript} from "../core/util";
import {Msg, Page, PluginEvent} from "../core/types";
injectScript("js/inject.js");
class Content {
private connect: chrome.runtime.Port | null = null;
constructor() {
// 接受来自inject.js的消息数据,然后中转到background.js
window.addEventListener("message", (event) => {
let data: PluginEvent = event.data;
if (PluginEvent.check(data, Page.Inject, Page.Content)) {
console.log("[Window-Message]: ", data);
PluginEvent.reset(data, Page.Content, Page.Devtools)
this.connect?.postMessage(data)
}
}, false);
}
// 和background.js保持长连接通讯background和content的交互也要通过这个链接进行通讯
private connectToBackground() {
this.connect = chrome.runtime.connect({name: Page.Content})
this.connect.onMessage.addListener((data: PluginEvent, sender) => {
if (PluginEvent.check(data, Page.Background, Page.Content)) {
// console.log(`%c[Connect-Message] ${JSON.stringify(data)}`, "color:green;")
console.log("[Connect-Message]: ", data);
PluginEvent.reset(data, Page.Content, Page.Inject)
window.postMessage(data, "*");
}
})
}
private sendMessageToBackground(data: PluginEvent) {
if (this.connect) {
this.connect.postMessage(data);
}
}
async run() {
this.connectToBackground();
this.checkGame();
}
private checkGame() {
let gameCanvas = document.querySelector("#GameCanvas");
if (!gameCanvas) {
let sendData = new PluginEvent(Page.Content, Page.Devtools, Msg.Support, {
support: false,
msg: "未发现GameCanvas,不支持调试游戏!"
})
this.sendMessageToBackground(sendData)
}
}
}
const content = new Content();
content.run();

View File

@ -1,3 +0,0 @@
console.log("injected");
const a = 1;
const b = 2;

View File

@ -8,39 +8,44 @@ import {
Group,
ImageData,
Info,
InvalidData, NodeInfoData,
InvalidData,
NodeInfoData,
NumberData,
ObjectData, ObjectItemRequestData,
ObjectData,
ObjectItemRequestData,
Property,
StringData,
TreeData,
Vec2Data,
Vec3Data
} from "@/devtools/data";
import {Msg, Page, PluginEvent} from "@/core/types"
import {BuildArrayOptions, BuildImageOptions, BuildObjectOptions, BuildVecOptions} from "@/inject/types";
// @ts-ignore
import {uniq} from "lodash"
import {trySetValueWithConfig, getValue} from "@/inject/setValue";
import {isHasProperty} from "@/inject/util";
Vec3Data,
} from "../../views/devtools/data";
import { Msg, Page, PluginEvent } from "../../core/types";
import {
BuildArrayOptions,
BuildImageOptions,
BuildObjectOptions,
BuildVecOptions,
} from "./types";
import { uniq } from "lodash";
import { trySetValueWithConfig, getValue } from "./setValue";
import { isHasProperty } from "./util";
declare const cc: any;
class CCInspector {
inspectorGameMemoryStorage: Record<string, any> = {}
inspectorGameMemoryStorage: Record<string, any> = {};
private watchIsCocosGame() {
const timer = setInterval(() => {
if (this._isCocosGame()) {
clearInterval(timer)
clearInterval(timer);
// @ts-ignore
cc.director.on(cc.Director.EVENT_AFTER_SCENE_LAUNCH, () => {
let isCocosGame = this._isCocosGame();
this.notifySupportGame(isCocosGame)
})
this.notifySupportGame(isCocosGame);
});
}
}, 300)
}, 300);
}
init() {
@ -49,16 +54,19 @@ class CCInspector {
window.addEventListener("message", (event) => {
// 接受来自content的事件有可能也会受到其他插件的
if (!event || !event.data) {
return
return;
}
let pluginEvent: PluginEvent = event.data;
if (PluginEvent.check(pluginEvent, Page.Content, Page.Inject)) {
console.log(`%c[Inject] ${JSON.stringify(pluginEvent)}`, "color:green;");
PluginEvent.finish(pluginEvent)
console.log(
`%c[Inject] ${JSON.stringify(pluginEvent)}`,
"color:green;"
);
PluginEvent.finish(pluginEvent);
switch (pluginEvent.msg) {
case Msg.Support: {
let isCocosGame = this._isCocosGame();
this.notifySupportGame(isCocosGame)
this.notifySupportGame(isCocosGame);
break;
}
case Msg.TreeInfo: {
@ -81,7 +89,7 @@ class CCInspector {
if (this.setValue(data.path, value)) {
this.sendMsgToContent(Msg.UpdateProperty, data);
} else {
console.warn(`设置失败:${data.path}`)
console.warn(`设置失败:${data.path}`);
}
break;
}
@ -106,7 +114,7 @@ class CCInspector {
let result: ObjectItemRequestData = {
id: data.id,
data: itemData,
}
};
this.sendMsgToContent(Msg.GetObjectItemData, result);
}
break;
@ -118,7 +126,10 @@ class CCInspector {
sendMsgToContent(msg: Msg, data: any) {
// 发送给content.js处理也会导致发送给了自身死循环
window.postMessage(new PluginEvent(Page.Inject, Page.Content, msg, data), "*");
window.postMessage(
new PluginEvent(Page.Inject, Page.Content, msg, data),
"*"
);
}
notifySupportGame(b: boolean) {
@ -132,18 +143,17 @@ class CCInspector {
let scene = cc.director.getScene();
if (scene) {
let treeData = new TreeData();
this.getNodeChildren(scene, treeData)
this.getNodeChildren(scene, treeData);
this.sendMsgToContent(Msg.TreeInfo, treeData);
} else {
console.warn("can't execute api : cc.director.getScene")
console.warn("can't execute api : cc.director.getScene");
this.notifySupportGame(false);
}
} else {
this.notifySupportGame(false)
this.notifySupportGame(false);
}
}
// @ts-ignore
draw: cc.Graphics = null;
@ -158,14 +168,14 @@ class CCInspector {
// @ts-ignore
draw = this.draw = node.addComponent(cc.Graphics);
}
draw.clear()
draw.clear();
draw.lineWidth = 10;
// @ts-ignore
draw.strokeColor = new cc.Color().fromHEX("#ff0000")
const {anchorX, anchorY, width, height, x, y} = node;
draw.strokeColor = new cc.Color().fromHEX("#ff0000");
const { anchorX, anchorY, width, height, x, y } = node;
let halfWidth = width / 2;
let halfHeight = height / 2;
let leftBottom = node.convertToWorldSpaceAR(cc.v2(-halfWidth, -halfHeight))
let leftBottom = node.convertToWorldSpaceAR(cc.v2(-halfWidth, -halfHeight));
let leftTop = node.convertToWorldSpaceAR(cc.v2(-halfWidth, halfHeight));
let rightBottom = node.convertToWorldSpaceAR(cc.v2(halfWidth, -halfHeight));
let rightTop = node.convertToWorldSpaceAR(cc.v2(halfWidth, halfHeight));
@ -175,10 +185,10 @@ class CCInspector {
draw.lineTo(end.x, end.y);
}
line(leftBottom, rightBottom)
line(rightBottom, rightTop)
line(rightTop, leftTop)
line(leftTop, leftBottom)
line(leftBottom, rightBottom);
line(rightBottom, rightTop);
line(rightTop, leftTop);
line(leftTop, leftBottom);
this.draw.stroke();
}
@ -198,7 +208,7 @@ class CCInspector {
let childItem = nodeChildren[i];
let treeData = new TreeData();
this.getNodeChildren(childItem, treeData);
data.children.push(treeData)
data.children.push(treeData);
}
}
@ -223,45 +233,61 @@ class CCInspector {
}
const proto = Object.getPrototypeOf(root);
if (proto) {
circle(proto)
circle(proto);
}
}
circle(obj)
circle(obj);
return keys;
}
_getNodeKeys(node: any) {
// 3.x变成了getter
let excludeProperty = [
"children", "quat", "node", "components", "parent",
"children",
"quat",
"node",
"components",
"parent",
// 生命周期函数
"onFocusInEditor", "onRestore", "start", "lateUpdate", "update", "resetInEditor", "onLostFocusInEditor",
"onEnable", "onDisable", "onDestroy", "onLoad",
"onFocusInEditor",
"onRestore",
"start",
"lateUpdate",
"update",
"resetInEditor",
"onLostFocusInEditor",
"onEnable",
"onDisable",
"onDestroy",
"onLoad",
];
const keyHidden = this.getAllPropertyDescriptors(node);
const keyVisible1 = Object.keys(node); // Object不走原型链
let keyVisible2: string[] = [];
for (let nodeKey in node) {// 走原型链
keyVisible2.push(nodeKey)
for (let nodeKey in node) {
// 走原型链
keyVisible2.push(nodeKey);
}
let allKeys: string[] = uniq(keyHidden.concat(keyVisible1, keyVisible2)).sort();
allKeys = allKeys.filter(key => {
let allKeys: string[] = uniq(
keyHidden.concat(keyVisible1, keyVisible2)
).sort();
allKeys = allKeys.filter((key) => {
return !key.startsWith("_") && !excludeProperty.includes(key);
});
allKeys = allKeys.filter(key => {
allKeys = allKeys.filter((key) => {
try {
return typeof node[key] !== "function"
return typeof node[key] !== "function";
} catch (e) {
// console.warn(`属性${key}出现异常:\n`, e);
return false;
}
})
});
return allKeys;
}
_getPairProperty(key: string): null | { key: string, values: string[] } {
_getPairProperty(key: string): null | { key: string; values: string[] } {
let pairProperty: Record<string, any> = {
rotation: ["rotationX", "rotationY"],
anchor: ["anchorX", "anchorY"],
@ -274,7 +300,7 @@ class CCInspector {
if (pairProperty.hasOwnProperty(pairPropertyKey)) {
let pair = pairProperty[pairPropertyKey];
if (pair.includes(key) || key === pairPropertyKey) {
return {key: pairPropertyKey, values: pair};
return { key: pairPropertyKey, values: pair };
}
}
}
@ -289,15 +315,15 @@ class CCInspector {
const path: Array<string> = options.path;
if (ctor && value instanceof ctor) {
let hasUnOwnProperty = keys.find(key => !value.hasOwnProperty(key))
let hasUnOwnProperty = keys.find((key) => !value.hasOwnProperty(key));
if (!hasUnOwnProperty) {
for (let key in keys) {
let propName = keys[key];
if (value.hasOwnProperty(propName)) {
let propPath = path.concat(propName);
let itemData = this._genInfoData(value, propName, propPath)
let itemData = this._genInfoData(value, propName, propPath);
if (itemData) {
data.add(new Property(propName, itemData))
data.add(new Property(propName, itemData));
}
}
}
@ -326,7 +352,12 @@ class CCInspector {
return null;
}
_genInfoData(node: any, key: string | number, path: Array<string>, filterKey = true): Info | null {
_genInfoData(
node: any,
key: string | number,
path: Array<string>,
filterKey = true
): Info | null {
let propertyValue = node[key];
let info = null;
let invalidType = this._isInvalidValue(propertyValue);
@ -358,38 +389,43 @@ class CCInspector {
path: path,
value: propertyValue,
keys: keys,
})
});
} else {
!info && (info = this._buildVecData({
// @ts-ignore
ctor: cc.Vec3,
path: path,
data: new Vec3Data(),
keys: ["x", "y", "z"],
value: propertyValue,
}))
!info && (info = this._buildVecData({
// @ts-ignore
ctor: cc.Vec2,
path: path,
data: new Vec2Data(),
keys: ["x", "y"],
value: propertyValue
}))
!info && (info = this._buildImageData({
//@ts-ignore
ctor: cc.SpriteFrame,
data: new ImageData(),
path: path,
value: propertyValue,
}))
!info &&
(info = this._buildVecData({
// @ts-ignore
ctor: cc.Vec3,
path: path,
data: new Vec3Data(),
keys: ["x", "y", "z"],
value: propertyValue,
}));
!info &&
(info = this._buildVecData({
// @ts-ignore
ctor: cc.Vec2,
path: path,
data: new Vec2Data(),
keys: ["x", "y"],
value: propertyValue,
}));
!info &&
(info = this._buildImageData({
//@ts-ignore
ctor: cc.SpriteFrame,
data: new ImageData(),
path: path,
value: propertyValue,
}));
if (!info) {
if (typeof propertyValue === "object") {
let ctorName = propertyValue.constructor?.name;
if (ctorName) {
if (ctorName.startsWith("cc_") ||
if (
ctorName.startsWith("cc_") ||
// 2.4.0
ctorName === "CCClass") {
ctorName === "CCClass"
) {
info = new EngineData();
info.engineType = ctorName;
info.engineName = propertyValue.name;
@ -404,7 +440,7 @@ class CCInspector {
path: path,
value: propertyValue,
filterKey: filterKey,
})
});
}
}
}
@ -413,7 +449,7 @@ class CCInspector {
}
}
if (info) {
info.readonly = this._isReadonly(node, key)
info.readonly = this._isReadonly(node, key);
info.path = path;
} else {
console.error(`暂不支持的属性值`, propertyValue);
@ -421,31 +457,36 @@ class CCInspector {
return info;
}
_buildArrayData({value, path, data, keys}: BuildArrayOptions) {
keys = keys.filter(key => !key.toString().startsWith("_"));
_buildArrayData({ value, path, data, keys }: BuildArrayOptions) {
keys = keys.filter((key) => !key.toString().startsWith("_"));
for (let i = 0; i < keys.length; i++) {
let key = keys[i];
let propPath = path.concat(key.toString());
let itemData = this._genInfoData(value, key, propPath);
if (itemData) {
data.add(new Property(key.toString(), itemData))
data.add(new Property(key.toString(), itemData));
}
}
return data;
}
_buildObjectItemData({value, path, data, filterKey}: BuildObjectOptions): Property[] {
_buildObjectItemData({
value,
path,
data,
filterKey,
}: BuildObjectOptions): Property[] {
let keys = Object.keys(value);
if (filterKey) {
keys = this.filterKeys(keys);// 不再进行开发者定义的数据
keys = this.filterKeys(keys); // 不再进行开发者定义的数据
}
let ret: Property[] = []
let ret: Property[] = [];
for (let i = 0; i < keys.length; i++) {
let key = keys[i];
let propPath = path.concat(key.toString());
let itemData = this._genInfoData(value, key, propPath, filterKey);
if (itemData) {
ret.push(new Property(key, itemData))
ret.push(new Property(key, itemData));
}
}
return ret;
@ -453,33 +494,38 @@ class CCInspector {
filterKeys(keys: string[]) {
// 剔除_开头的属性
return keys.filter(key => !key.toString().startsWith("_"));
return keys.filter((key) => !key.toString().startsWith("_"));
}
_isInvalidValue(value: any) {
// !!Infinity=true
if ((value && value !== Infinity) || value === 0 || value === false || value === "") {
if (
(value && value !== Infinity) ||
value === 0 ||
value === false ||
value === ""
) {
return false;
}
if (value === null) {
return "null"
return "null";
} else if (value === Infinity) {
return "Infinity"
return "Infinity";
} else if (value === undefined) {
return "undefined"
return "undefined";
} else if (Number.isNaN(value)) {
return "NaN";
} else {
debugger
debugger;
return false;
}
}
_buildObjectData({value, path, data, filterKey}: BuildObjectOptions) {
_buildObjectData({ value, path, data, filterKey }: BuildObjectOptions) {
let keys = Object.keys(value);
if (filterKey) {
keys = this.filterKeys(keys)
keys = this.filterKeys(keys);
}
// 只返回一级key更深层级的key需要的时候再获取防止circle object导致的死循环
let desc: Record<string, any> = {};
@ -490,11 +536,10 @@ class CCInspector {
let keyDesc = "";
if (Array.isArray(propValue)) {
// 只收集一级key
propValue.forEach(item => {
})
keyDesc = `(${propValue.length}) [...]`
} else if (this._isInvalidValue(propValue)) { // 不能改变顺序
propValue.forEach((item) => {});
keyDesc = `(${propValue.length}) [...]`;
} else if (this._isInvalidValue(propValue)) {
// 不能改变顺序
keyDesc = propValue;
} else if (typeof propValue === "object") {
keyDesc = `${propValue.constructor.name} {...}`;
@ -536,14 +581,14 @@ class CCInspector {
const name = this.getCompName(node);
let nodeGroup = new Group(name, node.uuid);
let keys = this._getNodeKeys(node);
for (let i = 0; i < keys.length;) {
for (let i = 0; i < keys.length; ) {
let key = keys[i];
let pair = this._getPairProperty(key);
if (pair && this._checkKeysValid(node, pair.values)) {
let bSplice = false;
// 把这个成对的属性剔除掉
pair.values.forEach((item: string) => {
let index = keys.findIndex(el => el === item);
let index = keys.findIndex((el) => el === item);
if (index !== -1) {
keys.splice(index, 1);
if (pair && item === pair.key) {
@ -607,7 +652,7 @@ class CCInspector {
const data: NodeInfoData = {
uuid: uuid,
group: groupData,
}
};
this.sendMsgToContent(Msg.NodeInfo, data);
} else {
// 未获取到节点数据
@ -623,13 +668,13 @@ class CCInspector {
}
_isReadonly(base: Object, key: string | number): boolean {
let ret = Object.getOwnPropertyDescriptor(base, key)
let ret = Object.getOwnPropertyDescriptor(base, key);
if (ret) {
return !(ret.set || ret.writable);
} else {
let proto = Object.getPrototypeOf(base);
if (proto) {
return this._isReadonly(proto, key)
return this._isReadonly(proto, key);
} else {
return false;
}
@ -664,8 +709,8 @@ class CCInspector {
return false;
}
onMemoryInfo() {
const memory = console["memory"];
this.sendMsgToContent(Msg.MemoryInfo, {
performance: {
// @ts-ignore
@ -675,10 +720,11 @@ class CCInspector {
// @ts-ignore
usedJSHeapSize: window.performance.memory.usedJSHeapSize,
},
console: {
jsHeapSizeLimit: console.memory.jsHeapSizeLimit,
totalJSHeapSize: console.memory.totalJSHeapSize,
usedJSHeapSize: console.memory.usedJSHeapSize,
jsHeapSizeLimit: memory.jsHeapSizeLimit,
totalJSHeapSize: memory.totalJSHeapSize,
usedJSHeapSize: memory.usedJSHeapSize,
},
});
}
@ -688,7 +734,3 @@ let inspector = new CCInspector();
inspector.init();
//@ts-ignore
window.CCInspector = inspector;

View File

@ -1,4 +1,4 @@
import {isVersion3} from "@/inject/util";
import {isVersion3} from "./util";
interface ConfigItem {
path: string[],

View File

@ -1,4 +1,4 @@
import {ArrayData, ImageData, ObjectData, Vec2Data, Vec3Data} from "@/devtools/data";
import {ArrayData, ImageData, ObjectData, Vec2Data, Vec3Data} from "../../views/devtools/data";
export interface BuildObjectOptions {
path: string[];

View File

@ -1,20 +1,151 @@
<template>
<div class="popup">popup</div>
</template>
<script lang="ts">
import { defineComponent, onMounted, ref, provide, nextTick } from "vue";
export default defineComponent({
name: "popup",
components: {},
setup(props, ctx) {
return {};
},
});
</script>
<style scoped lang="less">
.popup {
widows: 10px;
height: 10px;
background-color: rebeccapurple;
<div class="popup">
<div class="head">
<div class="name">{{ title }}</div>
<div style="flex: 1"></div>
<el-button
class="el-icon-setting btn"
@click="onClickOptions"
></el-button>
</div>
<div class="wechat">
<div class="money">
<img class="png" src="./res/money.png" alt="" />
<div class="tips">请我喝杯奶茶</div>
</div>
<div class="space"></div>
<div class="friends">
<img class="png" src="./res/friend.png" alt="" />
<div class="tips">交个朋友</div>
</div>
</div>
<div class="foot">
<a href="https://tidys.gitee.io/doc/#" target="_blank">
<img class="icon" src="./res/tiezi.png" alt="" />
</a>
<a
href="https://github.com/tidys/CocosCreatorPlugins/tree/master/CocosCreatorInspector"
target="_blank"
>
<img class="icon" src="./res/github.png" alt="" />
</a>
<a href="https://jq.qq.com/?_wv=1027&k=5SdPdy2" target="_blank">
<img class="icon" src="./res/qq.png" alt="" />
</a>
<div class="space"></div>
<div v-if="version">ver:{{ version }}</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, onMounted, ref, provide, nextTick } from "vue";
import CCP from "cc-plugin/src/ccp/entry-render";
import { ChromeConst } from "cc-plugin/src/chrome/const";
export default defineComponent({
name: "popup",
components: {},
setup(props, ctx) {
const title = ref(CCP.manifest.name);
const version = ref(CCP.manifest.version);
let longConn: chrome.runtime.Port | null = null;
function _initLongConn() {
if (!longConn) {
console.log("[popup] 初始化长连接");
if (chrome && chrome.runtime) {
longConn = chrome.runtime.connect({ name: "popup" });
longConn.onMessage.addListener((data: any, sender: any) => {
_onLongConnMsg(data, sender);
});
}
}
}
function _onLongConnMsg(data: string, sender: any) {
// console.log( title);
}
onMounted(() => {
_initLongConn();
});
return {
title,
version,
onClickOptions() {
if (chrome && chrome.tabs) {
chrome.tabs.create({ url: ChromeConst.html.popup });
}
},
onBtnClickGitHub() {
console.log("onBtnClickGitHub");
},
};
},
});
</script>
<style scoped lang="less">
.popup {
widows: 10px;
height: 10px;
background-color: rebeccapurple;
width: 300px;
display: flex;
flex-direction: column;
padding: 10px;
.head {
display: flex;
flex-direction: row;
align-items: center;
.name {
user-select: none;
font-size: 18px;
font-weight: bold;
}
.btn {
}
}
</style>
.wechat {
margin: 10px 0;
display: flex;
flex-direction: row;
.space {
flex: 1;
}
.png {
width: auto;
height: 130px;
}
.tips {
font-size: 15px;
user-select: none;
text-align: center;
width: 100%;
color: #6d6d6d;
}
}
.foot {
display: flex;
flex-direction: row;
height: 30px;
align-items: center;
.space {
flex: 1;
}
.icon {
margin: 0 3px;
width: auto;
height: 20px;
}
}
}
</style>

View File

Before

Width:  |  Height:  |  Size: 621 KiB

After

Width:  |  Height:  |  Size: 621 KiB

View File

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

Before

Width:  |  Height:  |  Size: 167 KiB

After

Width:  |  Height:  |  Size: 167 KiB

View File

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -275,6 +275,14 @@
dependencies:
"@types/node" "*"
"@types/chrome@0.0.133":
version "0.0.133"
resolved "https://registry.yarnpkg.com/@types/chrome/-/chrome-0.0.133.tgz#9e1d55441584ba2d5274ca84db36427da9c5dc6e"
integrity sha512-G8uIUdaCTBILprQvQXBWGXZxjAWbkCkFQit17cdH3zYQEwU8f/etNl8+M7e8MRz9Xj8daHaVpysneMZMx8/ldQ==
dependencies:
"@types/filesystem" "*"
"@types/har-format" "*"
"@types/connect-history-api-fallback@^1.3.5":
version "1.5.4"
resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz#7de71645a103056b48ac3ce07b3520b819c1d5b3"
@ -331,6 +339,18 @@
"@types/qs" "*"
"@types/serve-static" "*"
"@types/filesystem@*":
version "0.0.35"
resolved "https://registry.yarnpkg.com/@types/filesystem/-/filesystem-0.0.35.tgz#6d6766626083e2b397c09bdc57092827120db11d"
integrity sha512-1eKvCaIBdrD2mmMgy5dwh564rVvfEhZTWVQQGRNn0Nt4ZEnJ0C8oSUCzvMKRA4lGde5oEVo+q2MrTTbV/GHDCQ==
dependencies:
"@types/filewriter" "*"
"@types/filewriter@*":
version "0.0.32"
resolved "https://registry.yarnpkg.com/@types/filewriter/-/filewriter-0.0.32.tgz#3cf7e0f870e54e60ed1bbd9280fa24a9444d3b48"
integrity sha512-Kpi2GXQyYJdjL8mFclL1eDgihn1SIzorMZjD94kdPZh9E4VxGOeyjPxi5LpsM4Zku7P0reqegZTt2GxhmA9VBg==
"@types/fs-extra@9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.1.tgz#91c8fc4c51f6d5dbe44c2ca9ab09310bd00c7918"
@ -346,6 +366,11 @@
"@types/minimatch" "*"
"@types/node" "*"
"@types/har-format@*":
version "1.2.15"
resolved "https://registry.yarnpkg.com/@types/har-format/-/har-format-1.2.15.tgz#f352493638c2f89d706438a19a9eb300b493b506"
integrity sha512-RpQH4rXLuvTXKR0zqHq3go0RVXYv/YVqv4TnPH95VbwUxZdQlK1EtcMvQvMpDngHbt13Csh9Z4qT9AbkiQH5BA==
"@types/html-minifier-terser@^6.0.0":
version "6.1.0"
resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35"
@ -368,11 +393,23 @@
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
"@types/kind-of@^6.0.0":
version "6.0.3"
resolved "https://registry.yarnpkg.com/@types/kind-of/-/kind-of-6.0.3.tgz#9951fdf237298edf705839ba376054c8a5f55eae"
integrity sha512-JEwIR1oOC99PjUTd1LUHxq3gd6w9GGJ8nYUTHMfwOLszw6GuQB4ezsnk3WEeMOtOFx12Jk2JWrGttSnwUD4hrQ==
dependencies:
"@types/node" "*"
"@types/lodash@4.14.185":
version "4.14.185"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.185.tgz#c9843f5a40703a8f5edfd53358a58ae729816908"
integrity sha512-evMDG1bC4rgQg4ku9tKpuMh5iBNEwNa3tf9zRHdP1qlv+1WUg44xat4IxCE14gIpZRGUUWAx2VhItCZc25NfMA==
"@types/lodash@^4.14.176":
version "4.14.202"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.202.tgz#f09dbd2fb082d507178b2f2a5c7e74bd72ff98f8"
integrity sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==
"@types/mime@*":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.4.tgz#2198ac274de6017b44d941e00261d5bc6a0e0a45"
@ -478,6 +515,11 @@
dependencies:
"@types/node" "*"
"@types/uuid@^8.3.1":
version "8.3.4"
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc"
integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==
"@types/ws@^8.5.5":
version "8.5.10"
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.10.tgz#4acfb517970853fa6574a3a6886791d04a396787"

View File

@ -14,21 +14,16 @@
"fs-extra": "^9.1.0",
"less": "^4.1.1",
"less-loader": "^7.3.0",
"lodash": "^4.17.21",
"universal-analytics": "^0.4.23",
"uuid": "^8.3.2",
"vue": "^2.6.11",
"vue-class-component": "^7.2.3",
"vue-property-decorator": "^9.1.2"
},
"devDependencies": {
"@types/kind-of": "^6.0.0",
"@types/lodash": "^4.14.176",
"babel-eslint": "^10.1.0",
"@types/fs-extra": "^9.0.9",
"@types/node": "^14.14.37",
"@types/uuid": "^8.3.1",
"@types/chrome": "0.0.133",
"@typescript-eslint/eslint-plugin": "^4.18.0",
"@typescript-eslint/parser": "^4.18.0",
"@vue/cli-plugin-babel": "~4.5.0",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -1,252 +0,0 @@
import {Msg, Page, PluginEvent} from "@/core/types";
// @ts-ignore
import * as UA from "universal-analytics"
import {v4} from "uuid"
import {devtools_page} from "./manifest.json"
import {FrameDetails} from "@/devtools/data";
// 统计服务
const userID = localStorage.getItem("userID") || v4()
UA("UA-134924925-3", userID);
console.log("on background")
class PortMan {
public currentUseContentFrameID = 0;
public content: Array<chrome.runtime.Port> = []; // 因为iframe的原因可能对应多个主iframe的id===0
public devtools: chrome.runtime.Port | null = null;
public id: number | null = null;// tab.id作为唯一标识
public title: string = "";
public url: string = "";
private mgr: PortManagement | null = null;
constructor(mgr: PortManagement, {id, url, title}: any) {
this.mgr = mgr;
this.id = id;
this.url = url;
this.title = title;
}
private onPortConnect(port: chrome.runtime.Port, onMsg: Function, onDisconnect: Function) {
console.log(`%c[Connect] ${port.name}`, "color:green");
port.onMessage.addListener((data: any, sender: any) => {
console.log(`%c[Connect-Message] ${sender.name}\n${JSON.stringify(data)}`, "color:blue;")
// 如果多个页面都监听 onMessage 事件,对于某一次事件只有第一次调用 sendResponse() 能成功发出回应,所有其他回应将被忽略。
// sender.postMessage(data);
onMsg && onMsg(data);
});
port.onDisconnect.addListener((port: chrome.runtime.Port) => {
console.log(`%c[Connect-Dis] ${port.name}`, "color:red");
onDisconnect && onDisconnect(port)
});
}
getCurrentUseContent(): chrome.runtime.Port | null {
return this.content.find(el => el.sender?.frameId !== undefined && el.sender.frameId === this.currentUseContentFrameID) || null;
}
_updateFrames() {
let data: FrameDetails[] = this.content.map(item => {
return {
url: item.sender?.url || "",
frameID: item.sender?.frameId || 0,
}
})
let event = new PluginEvent(Page.Background, Page.Devtools, Msg.UpdateFrames, data)
this.sendDevtoolMsg(event)
}
dealConnect(port: chrome.runtime.Port) {
switch (port.name) {
case Page.Content: {
this.content.push(port);
this._updateFrames();
this.onPortConnect(port,
(data: PluginEvent) => {
if (data.target === Page.Devtools) {
this.sendDevtoolMsg(data);
}
},
(disPort: chrome.runtime.Port) => {
const index = this.content.findIndex(el =>
disPort.sender?.frameId !== undefined
&& el.sender?.frameId !== undefined
&& el.sender?.frameId === disPort.sender?.frameId
);
this.content.splice(index, 1);
this._updateFrames();
this.checkValid();
})
break;
}
case Page.Devtools: {
this.devtools = port;
this._updateFrames(); // 当devtools链接后主动派发frames数据
this.onPortConnect(port,
(data: PluginEvent) => {
if (data.msg === Msg.UseFrame) {
this.currentUseContentFrameID = data.data;
// 更新这个frame的tree
this.updateCurrentFrameTree();
} else {
// 从devtools过来的消息统一派发到Content中
if (PluginEvent.check(data, Page.Devtools, Page.Background)) {
if (data.msg === Msg.TreeInfo) {
if (this.currentUseContentFrameID !== data.data) {
console.log(`frameID[${data.data}]不一致`);
}
}
PluginEvent.reset(data, Page.Background, Page.Content);
this.getCurrentUseContent()?.postMessage(data)
}
}
},
() => {
this.devtools = null;
this.checkValid();
})
break
}
}
}
private updateCurrentFrameTree() {
const sendData = new PluginEvent(Page.Background, Page.Content, Msg.Support);
this.getCurrentUseContent()?.postMessage(sendData);
}
checkValid() {
if (!this.devtools && !this.content.length) {
this.mgr?.remove(this);
}
}
sendContentMsg(data: PluginEvent) {
this.getCurrentUseContent()?.postMessage(data);
}
sendDevtoolMsg(data: PluginEvent) {
this.devtools?.postMessage(data)
}
}
class PortManagement {
port: Array<PortMan> = [];
constructor() {
this.initConnect();
chrome.runtime.onMessage.addListener((request: PluginEvent, sender: any, sendResponse: any) => {
const tabID = sender.tab.id;
const portMan: PortMan | undefined = this.find(tabID);
if (portMan) {
if (PluginEvent.check(request, Page.Content, Page.Background)) {
// 监听来自content.js发来的事件将消息转发到devtools
PluginEvent.reset(request, Page.Background, Page.Devtools)
console.log(`%c[Message]url:${sender.url}]\n${JSON.stringify(request)}`, "color:green")
portMan.sendDevtoolMsg(request);
}
}
})
chrome.tabs.onActivated.addListener(({tabId, windowId}) => {
})
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
// 页面发生刷新,通知重新生成数据
if (changeInfo.status === "complete") {
const {id} = tab;
// -1为自己
if (id && id > -1) {
let portMan = this.find(id);
if (portMan) {
let data = new PluginEvent(Page.Background, Page.Content, Msg.Support);
portMan.sendContentMsg(data);
}
}
}
})
}
isDevtools(port: chrome.runtime.Port) {
const devtoolsUrl = `chrome-extension://${port.sender?.id}/${devtools_page}`
if (port.sender?.url === devtoolsUrl) {
}
}
initConnect() {
chrome.runtime.onConnect.addListener((port: chrome.runtime.Port) => {
if (port.name === Page.Devtools) {
// devtool链接过来没有port.sender.tab
chrome.tabs.getSelected((tab: chrome.tabs.Tab) => {
this._onConnect(tab, port)
})
} else {
const tab: chrome.tabs.Tab | undefined = port.sender?.tab;
if (tab) {
this._onConnect(tab, port)
}
}
})
}
private _onConnect(tab: chrome.tabs.Tab, port: chrome.runtime.Port) {
const {id, title, url} = tab;
if (id !== undefined && id > -1) {
let portMan: PortMan | undefined = this.find(id)
if (!portMan) {
portMan = new PortMan(this, {id, title, url});
this.port.push(portMan);
}
portMan.dealConnect(port);
}
}
find(id: number): PortMan | undefined {
return this.port.find(el => el.id === id)
}
remove(item: PortMan) {
let index = this.port.findIndex(el => el === item)
if (index > -1) {
this.port.splice(index, 1)
}
}
}
(window as any).backgroundInstance = new PortManagement();
function createPluginMenus() {
const menus = [];
let parent = chrome.contextMenus.create({id: "parent", title: "CC-Inspector"});
chrome.contextMenus.create({
id: "test",
title: "测试右键菜单",
parentId: parent,
// 上下文环境,可选:["all", "page", "frame", "selection", "link", "editable", "image", "video", "audio"]默认page
contexts: ["page"],
});
chrome.contextMenus.create({
id: "notify",
parentId: parent,
title: "通知"
})
chrome.contextMenus.onClicked.addListener(function (info, tab) {
if (info.menuItemId === "test") {
alert("您点击了右键菜单!");
} else if (info.menuItemId === "notify") {
chrome.notifications.create("null", {
type: "basic",
iconUrl: "icons/48.png",
title: "通知",
message: "测试通知",
})
}
})
}
chrome.contextMenus.removeAll(function () {
createPluginMenus();
});

View File

@ -1,61 +0,0 @@
// content.js 和原始界面共享DOM具有操作dom的能力
// 但是不共享js,要想访问页面js,只能通过注入的方式
import {injectScript} from "@/core/util";
import {Msg, Page, PluginEvent} from "@/core/types";
injectScript("js/inject.js");
class Content {
private connect: chrome.runtime.Port | null = null;
constructor() {
// 接受来自inject.js的消息数据,然后中转到background.js
window.addEventListener("message", (event) => {
let data: PluginEvent = event.data;
if (PluginEvent.check(data, Page.Inject, Page.Content)) {
console.log("[Window-Message]: ", data);
PluginEvent.reset(data, Page.Content, Page.Devtools)
this.connect?.postMessage(data)
}
}, false);
}
// 和background.js保持长连接通讯background和content的交互也要通过这个链接进行通讯
private connectToBackground() {
this.connect = chrome.runtime.connect({name: Page.Content})
this.connect.onMessage.addListener((data: PluginEvent, sender) => {
if (PluginEvent.check(data, Page.Background, Page.Content)) {
// console.log(`%c[Connect-Message] ${JSON.stringify(data)}`, "color:green;")
console.log("[Connect-Message]: ", data);
PluginEvent.reset(data, Page.Content, Page.Inject)
window.postMessage(data, "*");
}
})
}
private sendMessageToBackground(data: PluginEvent) {
if (this.connect) {
this.connect.postMessage(data);
}
}
async run() {
this.connectToBackground();
this.checkGame();
}
private checkGame() {
let gameCanvas = document.querySelector("#GameCanvas");
if (!gameCanvas) {
let sendData = new PluginEvent(Page.Content, Page.Devtools, Msg.Support, {
support: false,
msg: "未发现GameCanvas,不支持调试游戏!"
})
this.sendMessageToBackground(sendData)
}
}
}
const content = new Content();
content.run();

View File

@ -1,10 +0,0 @@
import Vue from "vue";
import App from "./index.vue";
import "element-ui/lib/theme-chalk/index.css"
import ElementUI from "element-ui";
Vue.config.productionTip = false;
Vue.use(ElementUI, {size: "mini"});
new Vue({
render: (h) => h(App),
}).$mount("#app");

View File

@ -1,159 +0,0 @@
<template>
<div id="popup">
<div class="head">
<div class="name">{{ title }}</div>
<div style="flex: 1"></div>
<el-button class="el-icon-setting btn" @click="onClickOptions"></el-button>
</div>
<div class="wechat">
<div class="money">
<img class="png" src="./res/money.png" alt=""/>
<div class="tips">请我喝杯奶茶</div>
</div>
<div class="space"></div>
<div class="friends">
<img class="png" src="./res/friend.png" alt=""/>
<div class="tips">交个朋友</div>
</div>
</div>
<div class="foot">
<a href="https://tidys.gitee.io/doc/#" target="_blank">
<img class="icon" src="./res/tiezi.png" alt="">
</a>
<a href="https://github.com/tidys/CocosCreatorPlugins/tree/master/CocosCreatorInspector" target="_blank">
<img class="icon" src="./res/github.png" alt="">
</a>
<a href="https://jq.qq.com/?_wv=1027&k=5SdPdy2" target="_blank">
<img class="icon" src="./res/qq.png" alt="">
</a>
<div class="space"></div>
<div v-if="version">ver:{{ version }}</div>
</div>
</div>
</template>
<script lang="ts">
import {Component, Vue} from "vue-property-decorator";
import Manifest from "../manifest.json"
import {version} from "../../package.json"
@Component({
components: {},
})
export default class App extends Vue {
longConn: chrome.runtime.Port | null = null
data() {
return {
title: "cc-inspector",
version: version,
}
}
created() {
this._initLongConn();
}
onBtnClickGitHub() {
console.log("onBtnClickGitHub");
}
onClickOptions() {
if (chrome && chrome.tabs) {
let {page} = Manifest.options_ui;
if (page) {
chrome.tabs.create({url: page})
}
}
}
_initLongConn() {
if (!this.longConn) {
console.log("[popup] 初始化长连接");
if (chrome && chrome.runtime) {
this.longConn = chrome.runtime.connect({name: "popup"});
this.longConn.onMessage.addListener((data: any, sender: any) => {
this._onLongConnMsg(data, sender);
})
}
}
}
_onLongConnMsg(data: string, sender: any) {
// console.log(this.title);
}
}
</script>
<style scoped lang="less">
@import "../index.less";
#popup {
width: 300px;
display: flex;
flex-direction: column;
padding: 10px;
.head {
display: flex;
flex-direction: row;
align-items: center;
.name {
user-select: none;
font-size: 18px;
font-weight: bold;
}
.btn {
}
}
.wechat {
margin: 10px 0;
display: flex;
flex-direction: row;
.space {
flex: 1;
}
.png {
width: auto;
height: 130px;
}
.tips {
font-size: 15px;
user-select: none;
text-align: center;
width: 100%;
color: #6d6d6d
}
}
.foot {
display: flex;
flex-direction: row;
height: 30px;
align-items: center;
.space {
flex: 1;
}
.icon {
margin: 0 3px;
width: auto;
height: 20px;
}
}
}
</style>

View File

@ -1,4 +0,0 @@
declare module "*.chrome" {
import chrome from "chrome";
export default chrome;
}

View File

@ -1,13 +0,0 @@
import Vue, { VNode } from "vue";
declare global {
namespace JSX {
// tslint:disable no-empty-interface
interface Element extends VNode {}
// tslint:disable no-empty-interface
interface ElementClass extends Vue {}
interface IntrinsicElements {
[elem: string]: any;
}
}
}

View File

@ -1,4 +0,0 @@
declare module "*.vue" {
import Vue from "vue";
export default Vue;
}

View File

@ -1,8 +0,0 @@
import Vue from "vue";
import App from "./index.vue";
Vue.config.productionTip = false;
new Vue({
render: (h) => h(App),
}).$mount("#app");

View File

@ -1,19 +0,0 @@
<template>
<div id="app">test</div>
</template>
<script lang="ts">
import {Component, Vue} from "vue-property-decorator";
@Component({
components: {},
})
export default class Index extends Vue {
}
</script>
<style>
#app {
}
</style>