diff --git a/tools/devtools/src/devtools_app/devtools_window/components_tab/details_window/details_window.js b/tools/devtools/src/devtools_app/devtools_window/components_tab/details_window/details_window.js
index 9cca7882..1934c968 100644
--- a/tools/devtools/src/devtools_app/devtools_window/components_tab/details_window/details_window.js
+++ b/tools/devtools/src/devtools_app/devtools_window/components_tab/details_window/details_window.js
@@ -1,11 +1,10 @@
const { Component, useRef, useEffect } = owl;
import { useStore } from "../../../store/store";
import { ObjectTreeElement } from "./object_tree_element/object_tree_element";
-import { Subscriptions } from "./subscriptions/subscriptions";
export class DetailsWindow extends Component {
static template = "devtools.DetailsWindow";
- static components = { ObjectTreeElement, Subscriptions };
+ static components = { ObjectTreeElement };
setup() {
this.store = useStore();
this.contextMenu = useRef("contextmenu");
diff --git a/tools/devtools/src/devtools_app/devtools_window/components_tab/details_window/details_window.xml b/tools/devtools/src/devtools_app/devtools_window/components_tab/details_window/details_window.xml
index 5fff5d1e..ba477b71 100644
--- a/tools/devtools/src/devtools_app/devtools_window/components_tab/details_window/details_window.xml
+++ b/tools/devtools/src/devtools_app/devtools_window/components_tab/details_window/details_window.xml
@@ -53,7 +53,11 @@
-
+
diff --git a/tools/devtools/src/devtools_app/devtools_window/components_tab/details_window/object_tree_element/object_tree_element.js b/tools/devtools/src/devtools_app/devtools_window/components_tab/details_window/object_tree_element/object_tree_element.js
index b38ec4ff..f659e45d 100644
--- a/tools/devtools/src/devtools_app/devtools_window/components_tab/details_window/object_tree_element/object_tree_element.js
+++ b/tools/devtools/src/devtools_app/devtools_window/components_tab/details_window/object_tree_element/object_tree_element.js
@@ -41,23 +41,22 @@ export class ObjectTreeElement extends Component {
return JSON.stringify(this.props.object.path);
}
- get objectName() {
- return this.props.object.name;
+ get keyChanges() {
+ return this.props.object.keys?.includes("Symbol(Key changes)");
}
- get objectLineClass() {
+ classFor(object) {
// Prototype items will be dyed down to appear less important
- if (this.pathAsString.includes('{"type":"prototype",')) {
- return { attenuate: true };
+ if (object.path.some((item) => item?.type === "prototype")) {
+ return "attenuate";
}
// Same for subscription items which are not present in the keys while the keys will be bold
- if (this.props.object.objectType === "subscription" && this.props.object.depth > 0) {
- if (this.props.keys.includes(this.props.object.name.toString())) {
- return { "fw-bolder": true };
+ if (object.objectType === "subscription" && object.depth > 0) {
+ if (this.props.object.keys?.includes(object.name.toString())) {
+ return "fw-bolder";
}
- return { attenuate: true };
+ return "attenuate";
}
- return {};
}
get objectPadding() {
diff --git a/tools/devtools/src/devtools_app/devtools_window/components_tab/details_window/object_tree_element/object_tree_element.xml b/tools/devtools/src/devtools_app/devtools_window/components_tab/details_window/object_tree_element/object_tree_element.xml
index ec84bd88..4e2121be 100644
--- a/tools/devtools/src/devtools_app/devtools_window/components_tab/details_window/object_tree_element/object_tree_element.xml
+++ b/tools/devtools/src/devtools_app/devtools_window/components_tab/details_window/object_tree_element/object_tree_element.xml
@@ -2,7 +2,7 @@
@@ -11,7 +11,7 @@
t-att-class="{'fa-caret-right': !props.object.toggled, 'fa-caret-down': props.object.toggled}"
t-attf-style="visibility: {{props.object.hasChildren ? '' : 'hidden'}};"
/>
-
+
:
@@ -28,6 +28,7 @@
+ +/-
-
-
+
diff --git a/tools/devtools/src/devtools_app/devtools_window/components_tab/details_window/subscriptions/subscriptions.js b/tools/devtools/src/devtools_app/devtools_window/components_tab/details_window/subscriptions/subscriptions.js
deleted file mode 100644
index bc8cb9ee..00000000
--- a/tools/devtools/src/devtools_app/devtools_window/components_tab/details_window/subscriptions/subscriptions.js
+++ /dev/null
@@ -1,30 +0,0 @@
-const { Component } = owl;
-import { useStore } from "../../../../store/store";
-import { ObjectTreeElement } from "../object_tree_element/object_tree_element";
-
-export class Subscriptions extends Component {
- static template = "devtools.Subscriptions";
-
- static components = { ObjectTreeElement };
-
- setup() {
- this.store = useStore();
- }
-
- // Used to display the keys in a compact way
- keysContent(index) {
- const keys = this.store.activeComponent.subscriptions.children[index].keys;
- let content = JSON.stringify(keys);
- const maxLength = 50;
- content = content.replace(/,/g, ", ");
- if (content.length > maxLength) {
- content = content.slice(0, content.lastIndexOf(",", maxLength - 5)) + ", ...]";
- }
- return content;
- }
-
- expandKeys(event, index) {
- this.store.activeComponent.subscriptions.children[index].keysExpanded =
- !this.store.activeComponent.subscriptions.children[index].keysExpanded;
- }
-}
diff --git a/tools/devtools/src/devtools_app/devtools_window/components_tab/details_window/subscriptions/subscriptions.xml b/tools/devtools/src/devtools_app/devtools_window/components_tab/details_window/subscriptions/subscriptions.xml
deleted file mode 100644
index 551f51c2..00000000
--- a/tools/devtools/src/devtools_app/devtools_window/components_tab/details_window/subscriptions/subscriptions.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/tools/devtools/src/main.css b/tools/devtools/src/main.css
index bb0c0748..5b8e6a5f 100644
--- a/tools/devtools/src/main.css
+++ b/tools/devtools/src/main.css
@@ -126,6 +126,10 @@
color: var(--prototype-color);
}
+.key-changes {
+ background-color: var(--version-bg);
+}
+
.event-container {
border-bottom: 1px solid rgb(240, 238, 238);
padding-top: 2px!important;
diff --git a/tools/devtools/src/page_scripts/owl_devtools_global_hook.js b/tools/devtools/src/page_scripts/owl_devtools_global_hook.js
index 49d88484..519f3b24 100644
--- a/tools/devtools/src/page_scripts/owl_devtools_global_hook.js
+++ b/tools/devtools/src/page_scripts/owl_devtools_global_hook.js
@@ -785,57 +785,47 @@
}
break;
}
- if (child.contentType) {
- if (child.toggled) {
- child.children = this.loadObjectChildren(
- child.path,
- child.depth,
- child.contentType,
- child.objectType,
- oldTree
- );
- }
- return child;
- }
- if (obj === null) {
- child.content = "null";
- child.contentType = "object";
- child.hasChildren = false;
- } else if (obj === undefined) {
- child.content = "undefined";
- child.contentType = "undefined";
- child.hasChildren = false;
- } else {
- obj = this.toRaw(obj);
- switch (true) {
- case obj instanceof Map:
- child.contentType = "map";
- child.hasChildren = true;
- break;
- case obj instanceof Set:
- child.contentType = "set";
- child.hasChildren = true;
- break;
- case obj instanceof Array:
- child.contentType = "array";
- child.hasChildren = obj.length > 0;
- break;
- case typeof obj === "function":
- child.contentType = "function";
- child.hasChildren = true;
- break;
- case obj instanceof Object:
- child.contentType = "object";
- child.hasChildren = Object.keys(obj).length > 0;
- break;
- default:
- child.contentType = typeof obj;
- child.hasChildren = false;
- }
- if (key.type === "set entry") {
- child.content = this.serializer.serializeItem(obj, true);
+ if (!child.contentType) {
+ if (obj === null) {
+ child.content = "null";
+ child.contentType = "object";
+ child.hasChildren = false;
+ } else if (obj === undefined) {
+ child.content = "undefined";
+ child.contentType = "undefined";
+ child.hasChildren = false;
} else {
- child.content = this.serializer.serializeContent(obj, child.contentType);
+ obj = this.toRaw(obj);
+ switch (true) {
+ case obj instanceof Map:
+ child.contentType = "map";
+ child.hasChildren = true;
+ break;
+ case obj instanceof Set:
+ child.contentType = "set";
+ child.hasChildren = true;
+ break;
+ case obj instanceof Array:
+ child.contentType = "array";
+ child.hasChildren = obj.length > 0;
+ break;
+ case typeof obj === "function":
+ child.contentType = "function";
+ child.hasChildren = true;
+ break;
+ case obj instanceof Object:
+ child.contentType = "object";
+ child.hasChildren = Object.keys(obj).length > 0;
+ break;
+ default:
+ child.contentType = typeof obj;
+ child.hasChildren = false;
+ }
+ if (key.type === "set entry") {
+ child.content = this.serializer.serializeItem(obj, true);
+ } else {
+ child.content = this.serializer.serializeContent(obj, child.contentType);
+ }
}
}
if (child.toggled) {
@@ -847,6 +837,7 @@
oldTree
);
}
+ this.addHighlightedKeys(child);
return child;
}
@@ -1269,16 +1260,15 @@
children: [],
};
} else {
- const rawSubscriptions = node.subscriptions;
+ const rawSubscriptions = this.topLevelSubscriptions(node);
component.subscriptions = {
toggled: oldTree ? oldTree.subscriptions.toggled : true,
children: [],
};
rawSubscriptions.forEach((rawSubscription, index) => {
let subscription = {
- keys: [],
target: {
- name: "target",
+ name: this.targetName(rawSubscription.target, node),
contentType:
typeof rawSubscription.target === "object"
? Array.isArray(rawSubscription.target)
@@ -1295,7 +1285,6 @@
toggled: false,
objectType: "subscription",
},
- keysExpanded: false,
};
if (
oldTree &&
@@ -1304,13 +1293,6 @@
) {
subscription.target.toggled = true;
}
- rawSubscription.keys.forEach((key) => {
- if (typeof key === "symbol") {
- subscription.keys.push(key.toString());
- } else {
- subscription.keys.push(key);
- }
- });
if (rawSubscription.target == null) {
if (subscription.target.contentType === "undefined") {
subscription.target.content = "undefined";
@@ -1340,6 +1322,7 @@
oldTree
);
}
+ this.addHighlightedKeys(subscription.target);
component.subscriptions.children.push(subscription);
});
}
@@ -1672,6 +1655,51 @@
inspect(obj);
}
}
+
+ targetName(target, node) {
+ // check on component
+ const { component } = node;
+ for (const [key, value] of Object.entries(component)) {
+ if (target === this.toRaw(value)) {
+ return key;
+ }
+ }
+ // check on props
+ for (const [key, value] of Object.entries(component.props)) {
+ if (target === this.toRaw(value)) {
+ return `props.${key}`;
+ }
+ }
+ return "[unknown]";
+ }
+
+ /**
+ * Removes subscriptions that are a direct child of another subscription:
+ * they will be reachable from the top level by expanding observed keys.
+ *
+ * @param {ComponentNode} node
+ * @returns {{ keys: PropertyKey[], target: unknown}[]} the top level
+ * subscriptions of the node
+ */
+ topLevelSubscriptions(node) {
+ const subscriptions = node.subscriptions;
+ const toOmit = new Set(
+ subscriptions.flatMap(({ keys, target }) => keys.map((k) => this.toRaw(target[k])))
+ );
+ return subscriptions.filter(({ target }) => !toOmit.has(target));
+ }
+
+ addHighlightedKeys(child) {
+ const { path } = child;
+ const subscriptionIndex = path.findIndex((item) => typeof item !== "string");
+ if (path[subscriptionIndex]?.value === "subscriptions") {
+ const node = this.getComponentNode(path.slice(0, subscriptionIndex));
+ // Add observed keys
+ const targetToKeys = new Map(node.subscriptions.map(({ keys, target }) => [target, keys]));
+ const target = this.getObjectProperty(child.path);
+ child.keys = targetToKeys.get(target)?.map((k) => String(k));
+ }
+ }
}
function checkOwlStatus() {