|
import type { |
|
AppRecord, |
|
BackendContext, |
|
Plugin, |
|
} from '@vue-devtools/app-backend-api' |
|
import { |
|
BuiltinBackendFeature, |
|
createBackendContext, |
|
} from '@vue-devtools/app-backend-api' |
|
import type { |
|
Bridge, |
|
} from '@vue-devtools/shared-utils' |
|
import { |
|
BridgeEvents, |
|
BridgeSubscriptions, |
|
BuiltinTabs, |
|
HookEvents, |
|
SharedData, |
|
createThrottleQueue, |
|
getPluginSettings, |
|
initSharedData, |
|
isBrowser, |
|
parse, |
|
raf, |
|
revive, |
|
target, |
|
} from '@vue-devtools/shared-utils' |
|
import debounce from 'lodash/debounce' |
|
import type { CustomInspectorOptions, PluginDescriptor, SetupFunction, TimelineEventOptions, TimelineLayerOptions } from '@vue/devtools-api' |
|
import { Hooks, now } from '@vue/devtools-api' |
|
import { hook } from './global-hook' |
|
import { isSubscribed, subscribe, unsubscribe } from './util/subscriptions' |
|
import { highlight, unHighlight } from './highlighter' |
|
import { addTimelineEvent, clearTimeline, sendTimelineEventData, sendTimelineLayerEvents, sendTimelineLayers, setupTimeline } from './timeline' |
|
import ComponentPicker from './component-pick' |
|
import { |
|
editComponentState, |
|
getComponentId, |
|
getComponentInstance, |
|
refreshComponentTreeSearch, |
|
sendComponentTreeData, |
|
sendComponentUpdateTracking, |
|
sendEmptyComponentData, |
|
sendSelectedComponentData, |
|
} from './component' |
|
import { addPlugin, addPreviouslyRegisteredPlugins, addQueuedPlugins, sendPluginList } from './plugin' |
|
import { _legacy_getAndRegisterApps, getAppRecord, registerApp, removeApp, selectApp, sendApps, waitForAppsRegistration } from './app' |
|
import { editInspectorState, getInspector, getInspectorWithAppId, selectInspectorNode, sendCustomInspectors, sendInspectorState, sendInspectorTree } from './inspector' |
|
import { showScreenshot } from './timeline-screenshot' |
|
import { performanceMarkEnd, performanceMarkStart } from './perf' |
|
import { initOnPageConfig } from './page-config' |
|
import { addTimelineMarker, sendTimelineMarkers } from './timeline-marker' |
|
import { flashComponent } from './flash.js' |
|
|
|
let ctx: BackendContext = target.__vdevtools_ctx ?? null |
|
let connected = target.__vdevtools_connected ?? false |
|
|
|
let pageTitleObserver: MutationObserver |
|
|
|
export async function initBackend(bridge: Bridge) { |
|
await initSharedData({ |
|
bridge, |
|
persist: false, |
|
}) |
|
|
|
SharedData.isBrowser = isBrowser |
|
|
|
initOnPageConfig() |
|
|
|
if (!connected) { |
|
|
|
ctx = target.__vdevtools_ctx = createBackendContext({ |
|
bridge, |
|
hook, |
|
}) |
|
|
|
SharedData.legacyApps = false |
|
if (hook.Vue) { |
|
connect() |
|
_legacy_getAndRegisterApps(ctx, true) |
|
SharedData.legacyApps = true |
|
} |
|
hook.on(HookEvents.INIT, () => { |
|
_legacy_getAndRegisterApps(ctx, true) |
|
SharedData.legacyApps = true |
|
}) |
|
|
|
hook.on(HookEvents.APP_ADD, async (app) => { |
|
await registerApp(app, ctx) |
|
connect() |
|
}) |
|
|
|
|
|
if (hook.apps.length) { |
|
hook.apps.forEach((app) => { |
|
registerApp(app, ctx) |
|
connect() |
|
}) |
|
} |
|
} |
|
else { |
|
|
|
ctx.bridge = bridge |
|
connectBridge() |
|
ctx.bridge.send(BridgeEvents.TO_FRONT_RECONNECTED) |
|
} |
|
} |
|
|
|
async function connect() { |
|
if (connected) { |
|
return |
|
} |
|
connected = target.__vdevtools_connected = true |
|
|
|
await waitForAppsRegistration() |
|
|
|
connectBridge() |
|
|
|
ctx.currentTab = BuiltinTabs.COMPONENTS |
|
|
|
|
|
|
|
hook.on(HookEvents.APP_UNMOUNT, async (app) => { |
|
await removeApp(app, ctx) |
|
}) |
|
|
|
|
|
|
|
const throttleQueue = createThrottleQueue(500) |
|
|
|
hook.on(HookEvents.COMPONENT_UPDATED, async (app, uid, parentUid, component) => { |
|
try { |
|
if (!app || (typeof uid !== 'number' && !uid) || !component) { |
|
return |
|
} |
|
const now = Date.now() |
|
|
|
let id: string |
|
let appRecord: AppRecord |
|
if (app && uid != null) { |
|
id = await getComponentId(app, uid, component, ctx) |
|
appRecord = await getAppRecord(app, ctx) |
|
} |
|
else { |
|
id = ctx.currentInspectedComponentId |
|
appRecord = ctx.currentAppRecord |
|
} |
|
|
|
throttleQueue.add(`update:${id}`, async () => { |
|
try { |
|
if (SharedData.trackUpdates) { |
|
sendComponentUpdateTracking(id, now, ctx) |
|
} |
|
|
|
if (SharedData.flashUpdates) { |
|
await flashComponent(component, appRecord.backend) |
|
} |
|
|
|
|
|
if (ctx.currentInspectedComponentId === id) { |
|
await sendSelectedComponentData(appRecord, ctx.currentInspectedComponentId, ctx) |
|
} |
|
|
|
|
|
if (isSubscribed(BridgeSubscriptions.COMPONENT_TREE, id)) { |
|
await sendComponentTreeData(appRecord, id, appRecord.componentFilter, 0, false, ctx) |
|
} |
|
} |
|
catch (e) { |
|
if (SharedData.debugInfo) { |
|
console.error(e) |
|
} |
|
} |
|
}) |
|
} |
|
catch (e) { |
|
if (SharedData.debugInfo) { |
|
console.error(e) |
|
} |
|
} |
|
}) |
|
|
|
hook.on(HookEvents.COMPONENT_ADDED, async (app, uid, parentUid, component) => { |
|
try { |
|
if (!app || (typeof uid !== 'number' && !uid) || !component) { |
|
return |
|
} |
|
const now = Date.now() |
|
const id = await getComponentId(app, uid, component, ctx) |
|
|
|
throttleQueue.add(`add:${id}`, async () => { |
|
try { |
|
const appRecord = await getAppRecord(app, ctx) |
|
if (component) { |
|
if (component.__VUE_DEVTOOLS_UID__ == null) { |
|
component.__VUE_DEVTOOLS_UID__ = id |
|
} |
|
if (appRecord?.instanceMap) { |
|
if (!appRecord.instanceMap.has(id)) { |
|
appRecord.instanceMap.set(id, component) |
|
} |
|
} |
|
} |
|
|
|
if (parentUid != null && appRecord?.instanceMap) { |
|
const parentInstances = await appRecord.backend.api.walkComponentParents(component) |
|
if (parentInstances.length) { |
|
|
|
for (let i = 0; i < parentInstances.length; i++) { |
|
const parentId = await getComponentId(app, parentUid, parentInstances[i], ctx) |
|
if (i < 2 && isSubscribed(BridgeSubscriptions.COMPONENT_TREE, parentId)) { |
|
raf(() => { |
|
sendComponentTreeData(appRecord, parentId, appRecord.componentFilter, null, false, ctx) |
|
}) |
|
} |
|
|
|
if (SharedData.trackUpdates) { |
|
sendComponentUpdateTracking(parentId, now, ctx) |
|
} |
|
} |
|
} |
|
} |
|
|
|
if (ctx.currentInspectedComponentId === id) { |
|
await sendSelectedComponentData(appRecord, id, ctx) |
|
} |
|
|
|
if (SharedData.trackUpdates) { |
|
sendComponentUpdateTracking(id, now, ctx) |
|
} |
|
|
|
if (SharedData.flashUpdates) { |
|
await flashComponent(component, appRecord.backend) |
|
} |
|
|
|
await refreshComponentTreeSearch(ctx) |
|
} |
|
catch (e) { |
|
if (SharedData.debugInfo) { |
|
console.error(e) |
|
} |
|
} |
|
}) |
|
} |
|
catch (e) { |
|
if (SharedData.debugInfo) { |
|
console.error(e) |
|
} |
|
} |
|
}) |
|
|
|
hook.on(HookEvents.COMPONENT_REMOVED, async (app, uid, parentUid, component) => { |
|
try { |
|
if (!app || (typeof uid !== 'number' && !uid) || !component) { |
|
return |
|
} |
|
const id = await getComponentId(app, uid, component, ctx) |
|
|
|
throttleQueue.add(`remove:${id}`, async () => { |
|
try { |
|
const appRecord = await getAppRecord(app, ctx) |
|
if (parentUid != null && appRecord) { |
|
const parentInstances = await appRecord.backend.api.walkComponentParents(component) |
|
if (parentInstances.length) { |
|
const parentId = await getComponentId(app, parentUid, parentInstances[0], ctx) |
|
if (isSubscribed(BridgeSubscriptions.COMPONENT_TREE, parentId)) { |
|
raf(async () => { |
|
try { |
|
const appRecord = await getAppRecord(app, ctx) |
|
|
|
if (appRecord) { |
|
sendComponentTreeData(appRecord, parentId, appRecord.componentFilter, null, false, ctx) |
|
} |
|
} |
|
catch (e) { |
|
if (SharedData.debugInfo) { |
|
console.error(e) |
|
} |
|
} |
|
}) |
|
} |
|
} |
|
} |
|
|
|
if (isSubscribed(BridgeSubscriptions.SELECTED_COMPONENT_DATA, id)) { |
|
await sendEmptyComponentData(id, ctx) |
|
} |
|
|
|
if (appRecord) { |
|
appRecord.instanceMap.delete(id) |
|
} |
|
|
|
await refreshComponentTreeSearch(ctx) |
|
} |
|
catch (e) { |
|
if (SharedData.debugInfo) { |
|
console.error(e) |
|
} |
|
} |
|
}) |
|
} |
|
catch (e) { |
|
if (SharedData.debugInfo) { |
|
console.error(e) |
|
} |
|
} |
|
}) |
|
|
|
hook.on(HookEvents.TRACK_UPDATE, (id, ctx) => { |
|
sendComponentUpdateTracking(id, Date.now(), ctx) |
|
}) |
|
|
|
hook.on(HookEvents.FLASH_UPDATE, (instance, backend) => { |
|
flashComponent(instance, backend) |
|
}) |
|
|
|
|
|
|
|
hook.on(HookEvents.PERFORMANCE_START, (app, uid, vm, type, time) => { |
|
performanceMarkStart(app, uid, vm, type, time, ctx) |
|
}) |
|
|
|
hook.on(HookEvents.PERFORMANCE_END, (app, uid, vm, type, time) => { |
|
performanceMarkEnd(app, uid, vm, type, time, ctx) |
|
}) |
|
|
|
|
|
|
|
hook.on(HookEvents.COMPONENT_HIGHLIGHT, (instanceId) => { |
|
highlight(ctx.currentAppRecord.instanceMap.get(instanceId), ctx.currentAppRecord.backend, ctx) |
|
}) |
|
|
|
hook.on(HookEvents.COMPONENT_UNHIGHLIGHT, () => { |
|
unHighlight() |
|
}) |
|
|
|
|
|
|
|
setupTimeline(ctx) |
|
|
|
hook.on(HookEvents.TIMELINE_LAYER_ADDED, async (options: TimelineLayerOptions, plugin: Plugin) => { |
|
const appRecord = await getAppRecord(plugin.descriptor.app, ctx) |
|
if (appRecord) { |
|
ctx.timelineLayers.push({ |
|
...options, |
|
appRecord, |
|
plugin, |
|
events: [], |
|
}) |
|
ctx.bridge.send(BridgeEvents.TO_FRONT_TIMELINE_LAYER_ADD, {}) |
|
} |
|
}) |
|
|
|
hook.on(HookEvents.TIMELINE_EVENT_ADDED, async (options: TimelineEventOptions, plugin: Plugin) => { |
|
await addTimelineEvent(options, plugin.descriptor.app, ctx) |
|
}) |
|
|
|
|
|
|
|
hook.on(HookEvents.CUSTOM_INSPECTOR_ADD, async (options: CustomInspectorOptions, plugin: Plugin) => { |
|
const appRecord = await getAppRecord(plugin.descriptor.app, ctx) |
|
if (appRecord) { |
|
ctx.customInspectors.push({ |
|
...options, |
|
appRecord, |
|
plugin, |
|
treeFilter: '', |
|
selectedNodeId: null, |
|
}) |
|
ctx.bridge.send(BridgeEvents.TO_FRONT_CUSTOM_INSPECTOR_ADD, {}) |
|
} |
|
}) |
|
|
|
hook.on(HookEvents.CUSTOM_INSPECTOR_SEND_TREE, async (inspectorId: string, plugin: Plugin) => { |
|
const inspector = getInspector(inspectorId, plugin.descriptor.app, ctx) |
|
if (inspector) { |
|
await sendInspectorTree(inspector, ctx) |
|
} |
|
else if (SharedData.debugInfo) { |
|
console.warn(`Inspector ${inspectorId} not found`) |
|
} |
|
}) |
|
|
|
hook.on(HookEvents.CUSTOM_INSPECTOR_SEND_STATE, async (inspectorId: string, plugin: Plugin) => { |
|
const inspector = getInspector(inspectorId, plugin.descriptor.app, ctx) |
|
if (inspector) { |
|
await sendInspectorState(inspector, ctx) |
|
} |
|
else if (SharedData.debugInfo) { |
|
console.warn(`Inspector ${inspectorId} not found`) |
|
} |
|
}) |
|
|
|
hook.on(HookEvents.CUSTOM_INSPECTOR_SELECT_NODE, async (inspectorId: string, nodeId: string, plugin: Plugin) => { |
|
const inspector = getInspector(inspectorId, plugin.descriptor.app, ctx) |
|
if (inspector) { |
|
await selectInspectorNode(inspector, nodeId, ctx) |
|
} |
|
else if (SharedData.debugInfo) { |
|
console.warn(`Inspector ${inspectorId} not found`) |
|
} |
|
}) |
|
|
|
|
|
|
|
try { |
|
await addPreviouslyRegisteredPlugins(ctx) |
|
} |
|
catch (e) { |
|
if (SharedData.debugInfo) { |
|
console.error(`Error adding previously registered plugins:`) |
|
console.error(e) |
|
} |
|
} |
|
try { |
|
await addQueuedPlugins(ctx) |
|
} |
|
catch (e) { |
|
if (SharedData.debugInfo) { |
|
console.error(`Error adding queued plugins:`) |
|
console.error(e) |
|
} |
|
} |
|
|
|
hook.on(HookEvents.SETUP_DEVTOOLS_PLUGIN, async (pluginDescriptor: PluginDescriptor, setupFn: SetupFunction) => { |
|
await addPlugin({ pluginDescriptor, setupFn }, ctx) |
|
}) |
|
|
|
target.__VUE_DEVTOOLS_PLUGIN_API_AVAILABLE__ = true |
|
|
|
|
|
|
|
const handleFlush = debounce(async () => { |
|
if (ctx.currentAppRecord?.backend.options.features.includes(BuiltinBackendFeature.FLUSH)) { |
|
await sendComponentTreeData(ctx.currentAppRecord, '_root', ctx.currentAppRecord.componentFilter, null, false, ctx) |
|
if (ctx.currentInspectedComponentId) { |
|
await sendSelectedComponentData(ctx.currentAppRecord, ctx.currentInspectedComponentId, ctx) |
|
} |
|
} |
|
}, 500) |
|
|
|
hook.off(HookEvents.FLUSH) |
|
hook.on(HookEvents.FLUSH, handleFlush) |
|
|
|
|
|
|
|
try { |
|
await addTimelineMarker({ |
|
id: 'vue-devtools-init-backend', |
|
time: now(), |
|
label: 'Vue Devtools connected', |
|
color: 0x41B883, |
|
all: true, |
|
}, ctx) |
|
} |
|
catch (e) { |
|
if (SharedData.debugInfo) { |
|
console.error(`Error while adding devtools connected timeline marker:`) |
|
console.error(e) |
|
} |
|
} |
|
} |
|
|
|
function connectBridge() { |
|
|
|
|
|
ctx.bridge.on(BridgeEvents.TO_BACK_SUBSCRIBE, ({ type, key }) => { |
|
subscribe(type, key) |
|
}) |
|
|
|
ctx.bridge.on(BridgeEvents.TO_BACK_UNSUBSCRIBE, ({ type, key }) => { |
|
unsubscribe(type, key) |
|
}) |
|
|
|
|
|
|
|
ctx.bridge.on(BridgeEvents.TO_BACK_TAB_SWITCH, async (tab) => { |
|
ctx.currentTab = tab |
|
await unHighlight() |
|
}) |
|
|
|
|
|
|
|
ctx.bridge.on(BridgeEvents.TO_BACK_APP_LIST, async () => { |
|
await sendApps(ctx) |
|
}) |
|
|
|
ctx.bridge.on(BridgeEvents.TO_BACK_APP_SELECT, async (id) => { |
|
if (id == null) { |
|
return |
|
} |
|
const record = ctx.appRecords.find(r => r.id === id) |
|
if (record) { |
|
await selectApp(record, ctx) |
|
} |
|
else if (SharedData.debugInfo) { |
|
console.warn(`App with id ${id} not found`) |
|
} |
|
}) |
|
|
|
ctx.bridge.on(BridgeEvents.TO_BACK_SCAN_LEGACY_APPS, () => { |
|
if (hook.Vue) { |
|
_legacy_getAndRegisterApps(ctx) |
|
} |
|
}) |
|
|
|
|
|
|
|
ctx.bridge.on(BridgeEvents.TO_BACK_COMPONENT_TREE, async ({ instanceId, filter, recursively }) => { |
|
ctx.currentAppRecord.componentFilter = filter |
|
subscribe(BridgeSubscriptions.COMPONENT_TREE, instanceId) |
|
await sendComponentTreeData(ctx.currentAppRecord, instanceId, filter, null, recursively, ctx) |
|
}) |
|
|
|
ctx.bridge.on(BridgeEvents.TO_BACK_COMPONENT_SELECTED_DATA, async (instanceId) => { |
|
await sendSelectedComponentData(ctx.currentAppRecord, instanceId, ctx) |
|
}) |
|
|
|
ctx.bridge.on(BridgeEvents.TO_BACK_COMPONENT_EDIT_STATE, async ({ instanceId, dotPath, type, value, newKey, remove }) => { |
|
await editComponentState(instanceId, dotPath, type, { value, newKey, remove }, ctx) |
|
}) |
|
|
|
ctx.bridge.on(BridgeEvents.TO_BACK_COMPONENT_INSPECT_DOM, async ({ instanceId }) => { |
|
const instance = getComponentInstance(ctx.currentAppRecord, instanceId, ctx) |
|
if (instance) { |
|
const [el] = await ctx.currentAppRecord.backend.api.getComponentRootElements(instance) |
|
if (el) { |
|
target.__VUE_DEVTOOLS_INSPECT_TARGET__ = el |
|
ctx.bridge.send(BridgeEvents.TO_FRONT_COMPONENT_INSPECT_DOM, null) |
|
} |
|
} |
|
}) |
|
|
|
ctx.bridge.on(BridgeEvents.TO_BACK_COMPONENT_SCROLL_TO, async ({ instanceId }) => { |
|
if (!isBrowser) { |
|
return |
|
} |
|
const instance = getComponentInstance(ctx.currentAppRecord, instanceId, ctx) |
|
if (instance) { |
|
const [el] = await ctx.currentAppRecord.backend.api.getComponentRootElements(instance) |
|
if (el) { |
|
if (typeof el.scrollIntoView === 'function') { |
|
el.scrollIntoView({ |
|
behavior: 'smooth', |
|
block: 'center', |
|
inline: 'center', |
|
}) |
|
} |
|
else { |
|
|
|
const bounds = await ctx.currentAppRecord.backend.api.getComponentBounds(instance) |
|
const scrollTarget = document.createElement('div') |
|
scrollTarget.style.position = 'absolute' |
|
scrollTarget.style.width = `${bounds.width}px` |
|
scrollTarget.style.height = `${bounds.height}px` |
|
scrollTarget.style.top = `${bounds.top}px` |
|
scrollTarget.style.left = `${bounds.left}px` |
|
document.body.appendChild(scrollTarget) |
|
scrollTarget.scrollIntoView({ |
|
behavior: 'smooth', |
|
block: 'center', |
|
inline: 'center', |
|
}) |
|
setTimeout(() => { |
|
document.body.removeChild(scrollTarget) |
|
}, 2000) |
|
} |
|
highlight(instance, ctx.currentAppRecord.backend, ctx) |
|
setTimeout(() => { |
|
unHighlight() |
|
}, 2000) |
|
} |
|
} |
|
}) |
|
|
|
ctx.bridge.on(BridgeEvents.TO_BACK_COMPONENT_RENDER_CODE, async ({ instanceId }) => { |
|
if (!isBrowser) { |
|
return |
|
} |
|
const instance = getComponentInstance(ctx.currentAppRecord, instanceId, ctx) |
|
if (instance) { |
|
const { code } = await ctx.currentAppRecord.backend.api.getComponentRenderCode(instance) |
|
ctx.bridge.send(BridgeEvents.TO_FRONT_COMPONENT_RENDER_CODE, { |
|
instanceId, |
|
code, |
|
}) |
|
} |
|
}) |
|
|
|
ctx.bridge.on(BridgeEvents.TO_BACK_CUSTOM_STATE_ACTION, async ({ value, actionIndex }) => { |
|
const rawAction = value._custom.actions[actionIndex] |
|
const action = revive(rawAction?.action) |
|
if (action) { |
|
try { |
|
await action() |
|
} |
|
catch (e) { |
|
if (SharedData.debugInfo) { |
|
console.error(e) |
|
} |
|
} |
|
} |
|
else if (SharedData.debugInfo) { |
|
console.warn(`Couldn't revive action ${actionIndex} from`, value) |
|
} |
|
}) |
|
|
|
|
|
|
|
ctx.bridge.on(BridgeEvents.TO_BACK_COMPONENT_MOUSE_OVER, async (instanceId) => { |
|
await highlight(ctx.currentAppRecord.instanceMap.get(instanceId), ctx.currentAppRecord.backend, ctx) |
|
}) |
|
|
|
ctx.bridge.on(BridgeEvents.TO_BACK_COMPONENT_MOUSE_OUT, async () => { |
|
await unHighlight() |
|
}) |
|
|
|
|
|
|
|
const componentPicker = new ComponentPicker(ctx) |
|
|
|
ctx.bridge.on(BridgeEvents.TO_BACK_COMPONENT_PICK, () => { |
|
componentPicker.startSelecting() |
|
}) |
|
|
|
ctx.bridge.on(BridgeEvents.TO_BACK_COMPONENT_PICK_CANCELED, () => { |
|
componentPicker.stopSelecting() |
|
}) |
|
|
|
|
|
|
|
ctx.bridge.on(BridgeEvents.TO_BACK_TIMELINE_LAYER_LIST, async () => { |
|
await sendTimelineLayers(ctx) |
|
}) |
|
|
|
ctx.bridge.on(BridgeEvents.TO_BACK_TIMELINE_SHOW_SCREENSHOT, async ({ screenshot }) => { |
|
await showScreenshot(screenshot, ctx) |
|
}) |
|
|
|
ctx.bridge.on(BridgeEvents.TO_BACK_TIMELINE_CLEAR, async () => { |
|
await clearTimeline(ctx) |
|
}) |
|
|
|
ctx.bridge.on(BridgeEvents.TO_BACK_TIMELINE_EVENT_DATA, async ({ id }) => { |
|
await sendTimelineEventData(id, ctx) |
|
}) |
|
|
|
ctx.bridge.on(BridgeEvents.TO_BACK_TIMELINE_LAYER_LOAD_EVENTS, async ({ appId, layerId }) => { |
|
await sendTimelineLayerEvents(appId, layerId, ctx) |
|
}) |
|
|
|
ctx.bridge.on(BridgeEvents.TO_BACK_TIMELINE_LOAD_MARKERS, async () => { |
|
await sendTimelineMarkers(ctx) |
|
}) |
|
|
|
|
|
|
|
ctx.bridge.on(BridgeEvents.TO_BACK_CUSTOM_INSPECTOR_LIST, async () => { |
|
await sendCustomInspectors(ctx) |
|
}) |
|
|
|
ctx.bridge.on(BridgeEvents.TO_BACK_CUSTOM_INSPECTOR_TREE, async ({ inspectorId, appId, treeFilter }) => { |
|
const inspector = await getInspectorWithAppId(inspectorId, appId, ctx) |
|
if (inspector) { |
|
inspector.treeFilter = treeFilter |
|
sendInspectorTree(inspector, ctx) |
|
} |
|
else if (SharedData.debugInfo) { |
|
console.warn(`Inspector ${inspectorId} not found`) |
|
} |
|
}) |
|
|
|
ctx.bridge.on(BridgeEvents.TO_BACK_CUSTOM_INSPECTOR_STATE, async ({ inspectorId, appId, nodeId }) => { |
|
const inspector = await getInspectorWithAppId(inspectorId, appId, ctx) |
|
if (inspector) { |
|
inspector.selectedNodeId = nodeId |
|
sendInspectorState(inspector, ctx) |
|
} |
|
else if (SharedData.debugInfo) { |
|
console.warn(`Inspector ${inspectorId} not found`) |
|
} |
|
}) |
|
|
|
ctx.bridge.on(BridgeEvents.TO_BACK_CUSTOM_INSPECTOR_EDIT_STATE, async ({ inspectorId, appId, nodeId, path, type, payload }) => { |
|
const inspector = await getInspectorWithAppId(inspectorId, appId, ctx) |
|
if (inspector) { |
|
await editInspectorState(inspector, nodeId, path, type, payload, ctx) |
|
inspector.selectedNodeId = nodeId |
|
await sendInspectorState(inspector, ctx) |
|
} |
|
else if (SharedData.debugInfo) { |
|
console.warn(`Inspector ${inspectorId} not found`) |
|
} |
|
}) |
|
|
|
ctx.bridge.on(BridgeEvents.TO_BACK_CUSTOM_INSPECTOR_ACTION, async ({ inspectorId, appId, actionIndex, actionType, args }) => { |
|
const inspector = await getInspectorWithAppId(inspectorId, appId, ctx) |
|
if (inspector) { |
|
const action = inspector[actionType ?? 'actions'][actionIndex] |
|
try { |
|
await action.action(...(args ?? [])) |
|
} |
|
catch (e) { |
|
if (SharedData.debugInfo) { |
|
console.error(e) |
|
} |
|
} |
|
} |
|
else if (SharedData.debugInfo) { |
|
console.warn(`Inspector ${inspectorId} not found`) |
|
} |
|
}) |
|
|
|
|
|
|
|
ctx.bridge.on(BridgeEvents.TO_BACK_LOG, (payload: { level: string, value: any, serialized?: boolean, revive?: boolean }) => { |
|
let value = payload.value |
|
if (payload.serialized) { |
|
value = parse(value, payload.revive) |
|
} |
|
else if (payload.revive) { |
|
value = revive(value) |
|
} |
|
|
|
console[payload.level](value) |
|
}) |
|
|
|
|
|
|
|
ctx.bridge.on(BridgeEvents.TO_BACK_DEVTOOLS_PLUGIN_LIST, async () => { |
|
await sendPluginList(ctx) |
|
}) |
|
|
|
ctx.bridge.on(BridgeEvents.TO_BACK_DEVTOOLS_PLUGIN_SETTING_UPDATED, ({ pluginId, key, newValue, oldValue }) => { |
|
const settings = getPluginSettings(pluginId) |
|
ctx.hook.emit(HookEvents.PLUGIN_SETTINGS_SET, pluginId, settings) |
|
ctx.currentAppRecord.backend.api.callHook(Hooks.SET_PLUGIN_SETTINGS, { |
|
app: ctx.currentAppRecord.options.app, |
|
pluginId, |
|
key, |
|
newValue, |
|
oldValue, |
|
settings, |
|
}) |
|
}) |
|
|
|
ctx.bridge.send(BridgeEvents.TO_FRONT_TITLE, { title: document.title }) |
|
|
|
const titleEl = document.querySelector('title') |
|
if (titleEl && typeof MutationObserver !== 'undefined') { |
|
if (pageTitleObserver) { |
|
pageTitleObserver.disconnect() |
|
} |
|
pageTitleObserver = new MutationObserver((mutations) => { |
|
const title = mutations[0].target as HTMLTitleElement |
|
ctx.bridge.send(BridgeEvents.TO_FRONT_TITLE, { title: title.textContent }) |
|
}) |
|
pageTitleObserver.observe(titleEl, { subtree: true, characterData: true, childList: true }) |
|
} |
|
} |
|
|