#1565 Bot State and Bot State Diff Pagination (#1658)

* Intitial commit for bot state diffing

* Transfered the bf-extension buttons from the botState to the json extension

* Progress on extension bot state diffing

* Lint

* Highlighting and pagination

* Fixed accessory button styling.

* Completed major work

* removed botState extension

* Unit tests

* added missing unit test

* Lint fixes

* Removed unused extension

* Removed commented code

* Removed unneeded code
This commit is contained in:
Justin Wilaby 2019-07-02 15:29:13 -07:00 коммит произвёл Chris Whitten
Родитель aa36bcacf6
Коммит 2247b4784e
70 изменённых файлов: 3274 добавлений и 13620 удалений

Просмотреть файл

@ -11,12 +11,9 @@
"packages/sdk/ui-react",
"packages/extensions/qnamaker",
"packages/extensions/qnamaker/client",
"packages/extensions/debug",
"packages/extensions/debug/client",
"packages/extensions/luis",
"packages/extensions/luis/client",
"packages/extensions/json",
"packages/extensions/botState"
"packages/extensions/json"
],
"version": "independent"
}

502
package-lock.json сгенерированный
Просмотреть файл

@ -2800,234 +2800,6 @@
"@types/node": "*"
}
},
"@types/d3": {
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/@types/d3/-/d3-5.7.2.tgz",
"integrity": "sha512-7/wClB8ycneWGy3jdvLfXKTd5SoTg9hji7IdJ0RuO9xTY54YpJ8zlcFADcXhY1J3kCBwxp+/1jeN6a5OMwgYOw==",
"requires": {
"@types/d3-array": "^1",
"@types/d3-axis": "*",
"@types/d3-brush": "*",
"@types/d3-chord": "*",
"@types/d3-collection": "*",
"@types/d3-color": "*",
"@types/d3-contour": "*",
"@types/d3-dispatch": "*",
"@types/d3-drag": "*",
"@types/d3-dsv": "*",
"@types/d3-ease": "*",
"@types/d3-fetch": "*",
"@types/d3-force": "*",
"@types/d3-format": "*",
"@types/d3-geo": "*",
"@types/d3-hierarchy": "*",
"@types/d3-interpolate": "*",
"@types/d3-path": "*",
"@types/d3-polygon": "*",
"@types/d3-quadtree": "*",
"@types/d3-random": "*",
"@types/d3-scale": "*",
"@types/d3-scale-chromatic": "*",
"@types/d3-selection": "*",
"@types/d3-shape": "*",
"@types/d3-time": "*",
"@types/d3-time-format": "*",
"@types/d3-timer": "*",
"@types/d3-transition": "*",
"@types/d3-voronoi": "*",
"@types/d3-zoom": "*"
}
},
"@types/d3-array": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-1.2.7.tgz",
"integrity": "sha512-51vHWuUyDOi+8XuwPrTw3cFqyh2Slg9y8COYkRfjCPG9TfYqY0hoNPzv/8BrcAy0FeQBzqEo/D/8Nk2caOQJnA=="
},
"@types/d3-axis": {
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-1.0.12.tgz",
"integrity": "sha512-BZISgSD5M8TgURyNtcPAmUB9sk490CO1Thb6/gIn0WZTt3Y50IssX+2Z0vTccoqZksUDTep0b+o4ofXslvNbqg==",
"requires": {
"@types/d3-selection": "*"
}
},
"@types/d3-brush": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-1.0.10.tgz",
"integrity": "sha512-J8jREATIrfJaAfhJivqaEKPnJsRlwwrOPje+ABqZFgamADjll+q9zaDXnYyjiGPPsiJEU+Qq9jQi5rECxIOfhg==",
"requires": {
"@types/d3-selection": "*"
}
},
"@types/d3-chord": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-1.0.9.tgz",
"integrity": "sha512-UA6lI9CVW5cT5Ku/RV4hxoFn4mKySHm7HEgodtfRthAj1lt9rKZEPon58vyYfk+HIAm33DtJJgZwMXy2QgyPXw=="
},
"@types/d3-collection": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/d3-collection/-/d3-collection-1.0.8.tgz",
"integrity": "sha512-y5lGlazdc0HNO0F3UUX2DPE7OmYvd9Kcym4hXwrJcNUkDaypR5pX+apuMikl9LfTxKItJsY9KYvzBulpCKyvuQ=="
},
"@types/d3-color": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-1.2.2.tgz",
"integrity": "sha512-6pBxzJ8ZP3dYEQ4YjQ+NVbQaOflfgXq/JbDiS99oLobM2o72uAST4q6yPxHv6FOTCRC/n35ktuo8pvw/S4M7sw=="
},
"@types/d3-contour": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-1.3.0.tgz",
"integrity": "sha512-AUCUIjEnC5lCGBM9hS+MryRaFLIrPls4Rbv6ktqbd+TK/RXZPwOy9rtBWmGpbeXcSOYCJTUDwNJuEnmYPJRxHQ==",
"requires": {
"@types/d3-array": "*",
"@types/geojson": "*"
}
},
"@types/d3-dispatch": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-1.0.7.tgz",
"integrity": "sha512-M+z84G7UKwK6hEPnGCSccOg8zJ3Nk2hgDQ9sCstHXgsFU0sMxlIZVKqKB5oxUDbALqQG6ucg0G9e8cmOSlishg=="
},
"@types/d3-drag": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-1.2.3.tgz",
"integrity": "sha512-rWB5SPvkYVxW3sqUxHOJUZwifD0KqvKwvt1bhNqcLpW6Azsd0BJgRNcyVW8GAferaAk5r8dzeZnf9zKlg9+xMQ==",
"requires": {
"@types/d3-selection": "*"
}
},
"@types/d3-dsv": {
"version": "1.0.36",
"resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-1.0.36.tgz",
"integrity": "sha512-jbIWQ27QJcBNMZbQv0NSQMHnBDCmxghAxePxgyiPH1XPCRkOsTBei7jcdi3fDrUCGpCV3lKrSZFSlOkhUQVClA=="
},
"@types/d3-ease": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-1.0.8.tgz",
"integrity": "sha512-VRf8czVWHSJPoUWxMunzpePK02//wHDAswknU8QWzcyrQn6pqe46bHRYi2smSpw5VjsT2CG8k/QeWIdWPS3Bmg=="
},
"@types/d3-fetch": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-1.1.5.tgz",
"integrity": "sha512-o9c0ItT5/Gl3wbNuVpzRnYX1t3RghzeWAjHUVLuyZJudiTxC4f/fC0ZPFWLQ2lVY8pAMmxpV8TJ6ETYCgPeI3A==",
"requires": {
"@types/d3-dsv": "*"
}
},
"@types/d3-force": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-1.2.1.tgz",
"integrity": "sha512-jqK+I36uz4kTBjyk39meed5y31Ab+tXYN/x1dn3nZEus9yOHCLc+VrcIYLc/aSQ0Y7tMPRlIhLetulME76EiiA=="
},
"@types/d3-format": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-1.3.1.tgz",
"integrity": "sha512-KAWvReOKMDreaAwOjdfQMm0HjcUMlQG47GwqdVKgmm20vTd2pucj0a70c3gUSHrnsmo6H2AMrkBsZU2UhJLq8A=="
},
"@types/d3-geo": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-1.11.1.tgz",
"integrity": "sha512-Ox8WWOG3igDRoep/dNsGbOiSJYdUG3ew/6z0ETvHyAtXZVBjOE0S96zSSmzgl0gqQ3RdZjn2eeJOj9oRcMZPkQ==",
"requires": {
"@types/geojson": "*"
}
},
"@types/d3-hierarchy": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-1.1.6.tgz",
"integrity": "sha512-vvSaIDf/Ov0o3KwMT+1M8+WbnnlRiGjlGD5uvk83a1mPCTd/E5x12bUJ/oP55+wUY/4Kb5kc67rVpVGJ2KUHxg=="
},
"@types/d3-interpolate": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-1.3.1.tgz",
"integrity": "sha512-z8Zmi08XVwe8e62vP6wcA+CNuRhpuUU5XPEfqpG0hRypDE5BWNthQHB1UNWWDB7ojCbGaN4qBdsWp5kWxhT1IQ==",
"requires": {
"@types/d3-color": "*"
}
},
"@types/d3-path": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-1.0.8.tgz",
"integrity": "sha512-AZGHWslq/oApTAHu9+yH/Bnk63y9oFOMROtqPAtxl5uB6qm1x2lueWdVEjsjjV3Qc2+QfuzKIwIR5MvVBakfzA=="
},
"@types/d3-polygon": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-1.0.7.tgz",
"integrity": "sha512-Xuw0eSjQQKs8jTiNbntWH0S+Xp+JyhqxmQ0YAQ3rDu6c3kKMFfgsaGN7Jv5u3zG6yVX/AsLP/Xs/QRjmi9g43Q=="
},
"@types/d3-quadtree": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-1.0.7.tgz",
"integrity": "sha512-0ajFawWicfjsaCLh6NzxOyVDYhQAmMFbsiI3MPGLInorauHFEh9/Cl6UHNf+kt/J1jfoxKY/ZJaKAoDpbvde5Q=="
},
"@types/d3-random": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-1.1.2.tgz",
"integrity": "sha512-Jui+Zn28pQw/3EayPKaN4c/PqTvqNbIPjHkgIIFnxne1FdwNjfHtAIsZIBMKlquQNrrMjFzCrlF2gPs3xckqaA=="
},
"@types/d3-scale": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-2.1.1.tgz",
"integrity": "sha512-kNTkbZQ+N/Ip8oX9PByXfDLoCSaZYm+VUOasbmsa6KD850/ziMdYepg/8kLg2plHzoLANdMqPoYQbvExevLUHg==",
"requires": {
"@types/d3-time": "*"
}
},
"@types/d3-scale-chromatic": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-1.3.1.tgz",
"integrity": "sha512-Ny3rLbV5tnmqgW7w/poCcef4kXP8mHPo/p8EjTS5d9OUk8MlqAeRaM8eF7Vyv7QMLiIXNE94Pa1cMLSPkXQBoQ=="
},
"@types/d3-selection": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-1.4.1.tgz",
"integrity": "sha512-bv8IfFYo/xG6dxri9OwDnK3yCagYPeRIjTlrcdYJSx+FDWlCeBDepIHUpqROmhPtZ53jyna0aUajZRk0I3rXNA=="
},
"@types/d3-shape": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-1.3.1.tgz",
"integrity": "sha512-usqdvUvPJ7AJNwpd2drOzRKs1ELie53p2m2GnPKr076/ADM579jVTJ5dPsoZ5E/CMNWk8lvPWYQSvilpp6jjwg==",
"requires": {
"@types/d3-path": "*"
}
},
"@types/d3-time": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-1.0.10.tgz",
"integrity": "sha512-aKf62rRQafDQmSiv1NylKhIMmznsjRN+MnXRXTqHoqm0U/UZzVpdrtRnSIfdiLS616OuC1soYeX1dBg2n1u8Xw=="
},
"@types/d3-time-format": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-2.1.1.tgz",
"integrity": "sha512-tJSyXta8ZyJ52wDDHA96JEsvkbL6jl7wowGmuf45+fAkj5Y+SQOnz0N7/H68OWmPshPsAaWMQh+GAws44IzH3g=="
},
"@types/d3-timer": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-1.0.9.tgz",
"integrity": "sha512-WvfJ3LFxBbWjqRGz9n7GJt08RrTHPJDVsIwwoCMROlqF+iDacYiAFjf9oqnq0mXpb2juA2N/qjKP+MKdal3YNQ=="
},
"@types/d3-transition": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-1.1.4.tgz",
"integrity": "sha512-/vsmKVUIXEyCcIXYAlw7bnYkIs9/J/nZbptRJFKUN3FdXq/dF6j9z9xXzerkyU6TDHLrMrwx9eGwdKyTIy/j9w==",
"requires": {
"@types/d3-selection": "*"
}
},
"@types/d3-voronoi": {
"version": "1.1.9",
"resolved": "https://registry.npmjs.org/@types/d3-voronoi/-/d3-voronoi-1.1.9.tgz",
"integrity": "sha512-DExNQkaHd1F3dFPvGA/Aw2NGyjMln6E9QzsiqOcBgnE+VInYnFBHBBySbZQts6z6xD+5jTfKCP7M4OqMyVjdwQ=="
},
"@types/d3-zoom": {
"version": "1.7.4",
"resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-1.7.4.tgz",
"integrity": "sha512-5jnFo/itYhJeB2khO/lKe730kW/h2EbKMOvY0uNp3+7NdPm4w63DwPEMxifQZ7n902xGYK5DdU67FmToSoy4VA==",
"requires": {
"@types/d3-interpolate": "*",
"@types/d3-selection": "*"
}
},
"@types/deep-diff": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/deep-diff/-/deep-diff-1.0.0.tgz",
@ -3091,11 +2863,6 @@
"@types/node": "*"
}
},
"@types/geojson": {
"version": "7946.0.7",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.7.tgz",
"integrity": "sha512-wE2v81i4C4Ol09RtsWFAqg3BUitWbHSpSlIo+bNdsCJijO9sjme+zm+73ZMCa/qMC8UEERxzGbvmr1cffo2SiQ=="
},
"@types/glob": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz",
@ -7617,270 +7384,6 @@
"resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz",
"integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA="
},
"d3": {
"version": "5.9.2",
"resolved": "https://registry.npmjs.org/d3/-/d3-5.9.2.tgz",
"integrity": "sha512-ydrPot6Lm3nTWH+gJ/Cxf3FcwuvesYQ5uk+j/kXEH/xbuYWYWTMAHTJQkyeuG8Y5WM5RSEYB41EctUrXQQytRQ==",
"requires": {
"d3-array": "1",
"d3-axis": "1",
"d3-brush": "1",
"d3-chord": "1",
"d3-collection": "1",
"d3-color": "1",
"d3-contour": "1",
"d3-dispatch": "1",
"d3-drag": "1",
"d3-dsv": "1",
"d3-ease": "1",
"d3-fetch": "1",
"d3-force": "1",
"d3-format": "1",
"d3-geo": "1",
"d3-hierarchy": "1",
"d3-interpolate": "1",
"d3-path": "1",
"d3-polygon": "1",
"d3-quadtree": "1",
"d3-random": "1",
"d3-scale": "2",
"d3-scale-chromatic": "1",
"d3-selection": "1",
"d3-shape": "1",
"d3-time": "1",
"d3-time-format": "2",
"d3-timer": "1",
"d3-transition": "1",
"d3-voronoi": "1",
"d3-zoom": "1"
}
},
"d3-array": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz",
"integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="
},
"d3-axis": {
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-1.0.12.tgz",
"integrity": "sha512-ejINPfPSNdGFKEOAtnBtdkpr24c4d4jsei6Lg98mxf424ivoDP2956/5HDpIAtmHo85lqT4pruy+zEgvRUBqaQ=="
},
"d3-brush": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.0.6.tgz",
"integrity": "sha512-lGSiF5SoSqO5/mYGD5FAeGKKS62JdA1EV7HPrU2b5rTX4qEJJtpjaGLJngjnkewQy7UnGstnFd3168wpf5z76w==",
"requires": {
"d3-dispatch": "1",
"d3-drag": "1",
"d3-interpolate": "1",
"d3-selection": "1",
"d3-transition": "1"
}
},
"d3-chord": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.6.tgz",
"integrity": "sha512-JXA2Dro1Fxw9rJe33Uv+Ckr5IrAa74TlfDEhE/jfLOaXegMQFQTAgAw9WnZL8+HxVBRXaRGCkrNU7pJeylRIuA==",
"requires": {
"d3-array": "1",
"d3-path": "1"
}
},
"d3-collection": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz",
"integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A=="
},
"d3-color": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.2.3.tgz",
"integrity": "sha512-x37qq3ChOTLd26hnps36lexMRhNXEtVxZ4B25rL0DVdDsGQIJGB18S7y9XDwlDD6MD/ZBzITCf4JjGMM10TZkw=="
},
"d3-contour": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-1.3.2.tgz",
"integrity": "sha512-hoPp4K/rJCu0ladiH6zmJUEz6+u3lgR+GSm/QdM2BBvDraU39Vr7YdDCicJcxP1z8i9B/2dJLgDC1NcvlF8WCg==",
"requires": {
"d3-array": "^1.1.1"
}
},
"d3-dispatch": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.5.tgz",
"integrity": "sha512-vwKx+lAqB1UuCeklr6Jh1bvC4SZgbSqbkGBLClItFBIYH4vqDJCA7qfoy14lXmJdnBOdxndAMxjCbImJYW7e6g=="
},
"d3-drag": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.3.tgz",
"integrity": "sha512-8S3HWCAg+ilzjJsNtWW1Mutl74Nmzhb9yU6igspilaJzeZVFktmY6oO9xOh5TDk+BM2KrNFjttZNoJJmDnkjkg==",
"requires": {
"d3-dispatch": "1",
"d3-selection": "1"
}
},
"d3-dsv": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.1.1.tgz",
"integrity": "sha512-1EH1oRGSkeDUlDRbhsFytAXU6cAmXFzc52YUe6MRlPClmWb85MP1J5x+YJRzya4ynZWnbELdSAvATFW/MbxaXw==",
"requires": {
"commander": "2",
"iconv-lite": "0.4",
"rw": "1"
}
},
"d3-ease": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.5.tgz",
"integrity": "sha512-Ct1O//ly5y5lFM9YTdu+ygq7LleSgSE4oj7vUt9tPLHUi8VCV7QoizGpdWRWAwCO9LdYzIrQDg97+hGVdsSGPQ=="
},
"d3-fetch": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-1.1.2.tgz",
"integrity": "sha512-S2loaQCV/ZeyTyIF2oP8D1K9Z4QizUzW7cWeAOAS4U88qOt3Ucf6GsmgthuYSdyB2HyEm4CeGvkQxWsmInsIVA==",
"requires": {
"d3-dsv": "1"
}
},
"d3-force": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.2.1.tgz",
"integrity": "sha512-HHvehyaiUlVo5CxBJ0yF/xny4xoaxFxDnBXNvNcfW9adORGZfyNF1dj6DGLKyk4Yh3brP/1h3rnDzdIAwL08zg==",
"requires": {
"d3-collection": "1",
"d3-dispatch": "1",
"d3-quadtree": "1",
"d3-timer": "1"
}
},
"d3-format": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.3.2.tgz",
"integrity": "sha512-Z18Dprj96ExragQ0DeGi+SYPQ7pPfRMtUXtsg/ChVIKNBCzjO8XYJvRTC1usblx52lqge56V5ect+frYTQc8WQ=="
},
"d3-geo": {
"version": "1.11.3",
"resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.11.3.tgz",
"integrity": "sha512-n30yN9qSKREvV2fxcrhmHUdXP9TNH7ZZj3C/qnaoU0cVf/Ea85+yT7HY7i8ySPwkwjCNYtmKqQFTvLFngfkItQ==",
"requires": {
"d3-array": "1"
}
},
"d3-hierarchy": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.8.tgz",
"integrity": "sha512-L+GHMSZNwTpiq4rt9GEsNcpLa4M96lXMR8M/nMG9p5hBE0jy6C+3hWtyZMenPQdwla249iJy7Nx0uKt3n+u9+w=="
},
"d3-interpolate": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.3.2.tgz",
"integrity": "sha512-NlNKGopqaz9qM1PXh9gBF1KSCVh+jSFErrSlD/4hybwoNX/gt1d8CDbDW+3i+5UOHhjC6s6nMvRxcuoMVNgL2w==",
"requires": {
"d3-color": "1"
}
},
"d3-path": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.7.tgz",
"integrity": "sha512-q0cW1RpvA5c5ma2rch62mX8AYaiLX0+bdaSM2wxSU9tXjU4DNvkx9qiUvjkuWCj3p22UO/hlPivujqMiR9PDzA=="
},
"d3-polygon": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.5.tgz",
"integrity": "sha512-RHhh1ZUJZfhgoqzWWuRhzQJvO7LavchhitSTHGu9oj6uuLFzYZVeBzaWTQ2qSO6bz2w55RMoOCf0MsLCDB6e0w=="
},
"d3-quadtree": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.6.tgz",
"integrity": "sha512-NUgeo9G+ENQCQ1LsRr2qJg3MQ4DJvxcDNCiohdJGHt5gRhBW6orIB5m5FJ9kK3HNL8g9F4ERVoBzcEwQBfXWVA=="
},
"d3-random": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/d3-random/-/d3-random-1.1.2.tgz",
"integrity": "sha512-6AK5BNpIFqP+cx/sreKzNjWbwZQCSUatxq+pPRmFIQaWuoD+NrbVWw7YWpHiXpCQ/NanKdtGDuB+VQcZDaEmYQ=="
},
"d3-scale": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-2.2.2.tgz",
"integrity": "sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw==",
"requires": {
"d3-array": "^1.2.0",
"d3-collection": "1",
"d3-format": "1",
"d3-interpolate": "1",
"d3-time": "1",
"d3-time-format": "2"
}
},
"d3-scale-chromatic": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-1.3.3.tgz",
"integrity": "sha512-BWTipif1CimXcYfT02LKjAyItX5gKiwxuPRgr4xM58JwlLocWbjPLI7aMEjkcoOQXMkYsmNsvv3d2yl/OKuHHw==",
"requires": {
"d3-color": "1",
"d3-interpolate": "1"
}
},
"d3-selection": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.4.0.tgz",
"integrity": "sha512-EYVwBxQGEjLCKF2pJ4+yrErskDnz5v403qvAid96cNdCMr8rmCYfY5RGzWz24mdIbxmDf6/4EAH+K9xperD5jg=="
},
"d3-shape": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.5.tgz",
"integrity": "sha512-VKazVR3phgD+MUCldapHD7P9kcrvPcexeX/PkMJmkUov4JM8IxsSg1DvbYoYich9AtdTsa5nNk2++ImPiDiSxg==",
"requires": {
"d3-path": "1"
}
},
"d3-time": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.0.11.tgz",
"integrity": "sha512-Z3wpvhPLW4vEScGeIMUckDW7+3hWKOQfAWg/U7PlWBnQmeKQ00gCUsTtWSYulrKNA7ta8hJ+xXc6MHrMuITwEw=="
},
"d3-time-format": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.1.3.tgz",
"integrity": "sha512-6k0a2rZryzGm5Ihx+aFMuO1GgelgIz+7HhB4PH4OEndD5q2zGn1mDfRdNrulspOfR6JXkb2sThhDK41CSK85QA==",
"requires": {
"d3-time": "1"
}
},
"d3-timer": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.9.tgz",
"integrity": "sha512-rT34J5HnQUHhcLvhSB9GjCkN0Ddd5Y8nCwDBG2u6wQEeYxT/Lf51fTFFkldeib/sE/J0clIe0pnCfs6g/lRbyg=="
},
"d3-transition": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.2.0.tgz",
"integrity": "sha512-VJ7cmX/FPIPJYuaL2r1o1EMHLttvoIuZhhuAlRoOxDzogV8iQS6jYulDm3xEU3TqL80IZIhI551/ebmCMrkvhw==",
"requires": {
"d3-color": "1",
"d3-dispatch": "1",
"d3-ease": "1",
"d3-interpolate": "1",
"d3-selection": "^1.1.0",
"d3-timer": "1"
}
},
"d3-voronoi": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.4.tgz",
"integrity": "sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg=="
},
"d3-zoom": {
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.7.3.tgz",
"integrity": "sha512-xEBSwFx5Z9T3/VrwDkMt+mr0HCzv7XjpGURJ8lWmIC8wxe32L39eWHIasEe/e7Ox8MPU4p1hvH8PKN2olLzIBg==",
"requires": {
"d3-dispatch": "1",
"d3-drag": "1",
"d3-interpolate": "1",
"d3-selection": "1",
"d3-transition": "1"
}
},
"dargs": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/dargs/-/dargs-4.1.0.tgz",
@ -19121,11 +18624,6 @@
"aproba": "^1.1.1"
}
},
"rw": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
"integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q="
},
"rx": {
"version": "2.3.24",
"resolved": "https://registry.npmjs.org/rx/-/rx-2.3.24.tgz",

Просмотреть файл

@ -50,7 +50,6 @@
"@babel/preset-env": "^7.1.0",
"@babel/preset-typescript": "^7.1.0",
"@babel/plugin-proposal-decorators": "^7.4.0",
"@types/deep-diff": "^1.0.0",
"@types/enzyme": "^3.1.10",
"@types/jest": "24.0.13",
"@types/react": "~16.3.2",
@ -111,7 +110,6 @@
"botframework-schema": "^4.3.4",
"botframework-webchat": "4.4.2",
"botframework-webchat-core": "4.4.2",
"deep-diff": "^1.0.2",
"eslint-plugin-react": "^7.12.3",
"markdown-it": "^8.4.2",
"react": "~16.3.2",

Просмотреть файл

@ -32,7 +32,7 @@
//
import { ClientAwareSettings, SharedConstants } from '@bfemulator/app-shared';
import { Command, CommandServiceImpl, CommandServiceInstance } from '@bfemulator/sdk-shared';
import { Command } from '@bfemulator/sdk-shared';
import { clientAwareSettingsChanged } from '../data/action/clientAwareSettingsActions';
import { store } from '../data/store';
@ -41,15 +41,8 @@ const { Settings } = SharedConstants.Commands;
/** Registers settings commands */
export class SettingsCommands {
@CommandServiceInstance()
private commandService: CommandServiceImpl;
@Command(Settings.ReceiveGlobalSettings)
protected async receiveGlobalSettings(settings: ClientAwareSettings) {
const state = store.getState();
store.dispatch(clientAwareSettingsChanged(settings));
if (state.clientAwareSettings.debugMode !== settings.debugMode) {
await this.commandService.call(SharedConstants.Commands.UI.SwitchDebugMode, settings.debugMode);
}
}
}

Просмотреть файл

@ -657,87 +657,6 @@ describe('The ChatSagas,', () => {
expect(clipboardSpy).toHaveBeenCalledWith(JSON.stringify(activity, null, 2));
});
it('should handle the "Compare with previous" selection', async () => {
const commandServiceSpy = jest.spyOn(commandService, 'remoteCall').mockResolvedValue({ id: 'diff' });
const activity = mockStoreState.chat.chats.doc1.log.entries[2].items[0].payload.obj;
mockStore.dispatch(showContextMenuForActivity(activity));
await Promise.resolve(true);
expect(commandServiceSpy).toHaveBeenCalled();
const state = mockStore.getState();
expect(JSON.stringify(state.chat.chats.doc1.inspectorObjects)).toEqual(
JSON.stringify([
{
channelId: 'emulator',
conversation: { conversationType: 'personal', id: '9fb93120-5713-11e9-a20f-e185020ba18b|livechat' },
from: {
aadObjectId: '8d81b1c4-a057-4d27-a41d-e40b3105e6ee',
id: '29:1roELw8-HUdxuNSlGwtGqacHW_y-tsmLhvs42duabIDv0JFovw3WX7QC-syrrAYRt0RHBqoS1i0Mt18un1YZmyw',
name: 'Justin Wilaby',
role: 'user',
},
id: 'a65d2c70-5713-11e9-a20f-e185020ba18b',
label: 'Bot State',
localTimestamp: '2019-04-04T12:55:51-07:00',
locale: 'en',
name: 'BotState',
recipient: { id: '28:825059e1-0dd5-4a90-9136-121a702c18ca', role: 'user' },
serviceUrl: 'http://localhost:9000',
timestamp: '2019-04-04T19:55:51.863Z',
type: 'trace',
value: {
conversationState: {
dialogState: {
conversationState: {},
dialogStack: {
'0': {
id: 'root',
state: {
options: {},
stepIndex: 0,
values: { instanceId: '6938f312-523a-2db2-92ba-9680f559dd2d' },
},
},
'1': {
id: 'slot-dialog',
state: {
values: {
fullname: {
slot: 'last',
values: { first: 'Justin ', last: 'Wilaby' },
},
},
'+slot': 'age',
'-slot': 'fullname',
},
},
'2': {
state: {
options: { prompt: 'Please enter your age.' },
state: {},
'-slot': 'last',
'-values': { first: 'Justin ' },
},
'+id': 'number',
'-id': 'fullname',
},
'-3': {
id: 'text',
state: { options: { prompt: 'Please enter your last name.' }, state: {} },
},
},
userState: {},
},
'+eTag': '4',
'-eTag': '3',
},
userState: {},
},
valueType: 'https://www.botframework.com/schemas/diff',
},
])
);
});
it('when closing a document it should notify the main process so it can remove the conversation', async () => {
const commandServiceSpy = jest.spyOn(commandService, 'remoteCall').mockResolvedValue(true);
mockStore.dispatch(closeConversation('doc1'));

Просмотреть файл

@ -34,15 +34,7 @@ import * as Electron from 'electron';
import { MenuItemConstructorOptions } from 'electron';
import { Activity } from 'botframework-schema';
import { SharedConstants, ValueTypes, newNotification } from '@bfemulator/app-shared';
import {
CommandServiceImpl,
CommandServiceInstance,
ConversationService,
InspectableObjectLogItem,
LogItem,
LogItemType,
} from '@bfemulator/sdk-shared';
import { diff } from 'deep-diff';
import { CommandServiceImpl, CommandServiceInstance, ConversationService } from '@bfemulator/sdk-shared';
import { IEndpointService } from 'botframework-config/lib/schema';
import { createCognitiveServicesBingSpeechPonyfillFactory } from 'botframework-webchat';
import { createStore as createWebChatStore } from 'botframework-webchat-core';
@ -54,8 +46,6 @@ import {
ClearLogPayload,
closeDocument,
DocumentIdPayload,
setHighlightedObjects,
setInspectorObjects,
updatePendingSpeechTokenRetrieval,
webChatStoreUpdated,
webSpeechFactoryUpdated,
@ -80,33 +70,6 @@ const getEndpointServiceByDocumentId = (state: RootState, documentId: string): I
) as IEndpointService;
};
const getCurrentDocumentId = (state: RootState): string => {
const { editors, activeEditor } = state.editor;
const { activeDocumentId } = editors[activeEditor];
return activeDocumentId;
};
const getPreviousBotState = (state: RootState, selectedTrace: Activity): Activity => {
const { editors, activeEditor } = state.editor;
const { activeDocumentId } = editors[activeEditor];
const chat = state.chat.chats[activeDocumentId];
const entries = chat.log.entries as any[];
const allEntries: LogItem<InspectableObjectLogItem>[] = entries.reduce(
(agg: LogItem[], entry) => agg.concat(entry.items),
[]
);
const filteredLogItems: LogItem<InspectableObjectLogItem>[] = allEntries.filter(
(item: LogItem<InspectableObjectLogItem>) => {
const activity = item.payload.obj as Activity;
return item.type === LogItemType.InspectableObject && activity.valueType === ValueTypes.BotState;
}
);
const index = filteredLogItems.findIndex(logItem => logItem.payload.obj.id === selectedTrace.id) - 1;
const targetLogEntry = filteredLogItems[index];
return targetLogEntry && (targetLogEntry.payload.obj as Activity);
};
const getChatFromDocumentId = (state: RootState, documentId: string): any => {
return state.chat.chats[documentId];
};
@ -117,13 +80,9 @@ export class ChatSagas {
public static *showContextMenuForActivity(action: ChatAction<Activity>): Iterable<any> {
const { payload: activity } = action;
const previousBotState = yield select(getPreviousBotState, activity);
const diffEnabled = activity.valueType.endsWith('botState') && !!previousBotState;
const menuItems = [
{ label: 'Copy text', id: 'copy' },
{ label: 'Copy json', id: 'json' },
{ type: 'separator' },
{ label: 'Compare with previous', id: 'diff', enabled: diffEnabled },
] as MenuItemConstructorOptions[];
const { DisplayContextMenu } = SharedConstants.Commands.Electron;
@ -144,7 +103,7 @@ export class ChatSagas {
return Electron.clipboard.writeText(JSON.stringify(activity, null, 2));
default:
yield* ChatSagas.diffWithPreviousBotState(activity);
return;
}
}
@ -221,70 +180,6 @@ export class ChatSagas {
}
}
public static *diffWithPreviousBotState(currentBotState: Activity): Iterable<any> {
const previousBotState: Activity = yield select(getPreviousBotState, currentBotState);
const lhs = [];
const rhs = [];
const deltas = diff(previousBotState.value, currentBotState.value);
(deltas || []).forEach(diff => {
switch (diff.kind) {
case 'A':
{
const { item, path } = diff;
path.push(diff.index);
if (item.kind === 'D') {
lhs.push(path);
} else if (item.kind === 'E') {
rhs.push(path);
lhs.push(path);
} else {
rhs.push(path);
}
}
break;
case 'D':
lhs.push(diff.path);
break;
case 'E':
rhs.push(diff.path);
lhs.push(diff.path);
break;
case 'N':
rhs.push(diff.path);
break;
}
});
// Clone the bot state and update the keys to show changes
const botStateClone: Activity = JSON.parse(
JSON.stringify(currentBotState, (key: string, value: any) => {
if (value instanceof Array) {
return Object.keys(value).reduce((conversion: any, key) => {
conversion['' + key] = value[key];
return conversion;
}, {});
}
return value;
})
);
botStateClone.valueType = ValueTypes.Diff;
// values that were added
rhs.forEach(path => {
ChatSagas.buildDiff('+', path, botStateClone.value, botStateClone.value);
});
// values that were removed
lhs.forEach(path => {
ChatSagas.buildDiff('-', path, botStateClone.value, previousBotState.value);
});
const documentId = yield select(getCurrentDocumentId);
yield put(setHighlightedObjects(documentId, [previousBotState, currentBotState]));
yield put(setInspectorObjects(documentId, botStateClone));
}
private static getTextFromActivity(activity: Activity): string {
if (activity.valueType === ValueTypes.Command) {
return activity.value;
@ -293,22 +188,6 @@ export class ChatSagas {
}
return activity.text || activity.label || '';
}
public static buildDiff(prependWith: string, path: (string | number)[], target: any, source: any): void {
let key;
for (let i = 0; i < path.length; i++) {
key = path[i];
if (key in target && target[key] !== null && typeof target[key] === 'object') {
target = target[key];
source = source[key];
} else {
break;
}
}
const value = source[key];
delete target[key];
target[prependWith + key] = value;
}
}
export function* chatSagas(): IterableIterator<ForkEffect> {

Просмотреть файл

@ -16,6 +16,7 @@
&:hover:not([disabled]), &[selected], &[aria-selected] {
background-color: transparent;
position: relative;
&:after {
content:'';
border: 1px solid var(--p-button-bg);
@ -37,6 +38,8 @@
.accessory-button-icon {
width: 30px;
height: 30px;
background-repeat: no-repeat;
}
}
}
@ -56,3 +59,23 @@
.nothing-inspected {
padding: 0 16px;
}
.left-arrow {
background-image: url('../../../../media/ic_next_copy.svg');
background-position: 50% 50%;
}
.left-arrow-selected {
background-image: url('../../../../media/ic_next_copy.svg');
background-position: 50% 50%;
}
.right-arrow {
background-image: url('../../../../media/ic_next.svg');
background-position: 50% 50%;
}
.right-arrow-selected {
background-image: url('../../../../media/ic_next.svg');
background-position: 50% 50%;
}

Просмотреть файл

@ -5,3 +5,7 @@ export const accessoryButton: string;
export const accessoryButtonIcon: string;
export const inspectorContainer: string;
export const nothingInspected: string;
export const leftArrow: string;
export const leftArrowSelected: string;
export const rightArrow: string;
export const rightArrowSelected: string;

Просмотреть файл

@ -34,17 +34,21 @@
// Cheating here and pulling in a module from node. Can be easily replaced if we ever move the emulator to the web.
// @ts-ignore
import {
EmulatorChannel,
ExtensionChannel,
ExtensionInspector,
InspectorAccessory,
logEntry,
LogEntry,
LogLevel,
luisEditorDeepLinkItem,
textItem,
} from '@bfemulator/sdk-shared';
import { PrimaryButton, Spinner } from '@bfemulator/ui-react';
import { IBotConfiguration } from 'botframework-config/lib/schema';
import { Activity } from 'botframework-schema';
import * as React from 'react';
import { LogEntry } from '@bfemulator/sdk-shared';
import { MouseEvent } from 'react';
import { ExtensionManager, GetInspectorResult, InspectorAPI } from '../../../../../extensions';
import { logService } from '../../../../../platform/log/logService';
@ -80,12 +84,15 @@ interface InspectorProps {
activeBot?: IBotConfiguration;
botHash?: string;
trackEvent?: (name: string, properties?: { [key: string]: any }) => void;
setHighlightedObjects: (documentId: string, objects: Activity[]) => void;
setInspectorObjects: (documentId: string, inspectorObjects: Activity[]) => void;
}
interface InspectorState {
titleOverride?: string;
activeBot?: IBotConfiguration;
logEntries: LogEntry[];
highlightedObjects?: Activity[] | LogEntry[];
botHash?: string;
inspectorSrc?: string;
inspectObj: InspectObject;
@ -105,7 +112,7 @@ export class Inspector extends React.Component<InspectorProps, InspectorState> {
if (icon === 'Spinner') {
return <Spinner segmentRadius={2} width={25} height={25} />;
} else if (icon) {
return <i className={`${styles.accessoryButtonIcon} ms-Icon ms-Icon--${icon}`} aria-hidden="true" />;
return <div className={`${styles.accessoryButtonIcon} ${styles[icon]}`} aria-hidden="true" />;
} else {
return false;
}
@ -132,28 +139,24 @@ export class Inspector extends React.Component<InspectorProps, InspectorState> {
const { document = {} as ChatDocument } = newProps;
const inspectorResult = Inspector.getInspector(document.inspectorObjects);
const { inspector = { name: '' } } = inspectorResult.response;
const buttons = Inspector.getButtons(inspector.accessories);
if (
newProps.botHash !== prevState.botHash ||
inspector.src !== prevState.inspectorSrc ||
newProps.document.log.entries.length !== prevState.logEntries.length ||
newProps.themeInfo.themeName !== prevState.themeInfo.themeName ||
JSON.stringify(inspectorResult.inspectObj) !== JSON.stringify(prevState.inspectObj)
) {
return {
...prevState,
activeBot: newProps.activeBot,
logEntries: [...newProps.document.log.entries],
botHash: newProps.botHash,
inspector,
inspectorSrc: inspector.src,
inspectObj: inspectorResult.inspectObj,
themeInfo: newProps.themeInfo,
title: inspector.name,
buttons: Inspector.getButtons(inspector.accessories),
};
if (prevState.buttons) {
Object.assign(buttons, prevState.buttons);
}
return null;
return {
...prevState,
activeBot: newProps.activeBot,
logEntries: newProps.document.log.entries,
highlightedObjects: newProps.document.highlightedObjects,
botHash: newProps.botHash,
inspector,
inspectorSrc: inspector.src,
inspectObj: inspectorResult.inspectObj,
themeInfo: newProps.themeInfo,
title: inspector.name,
buttons,
};
}
private static getInspector(inspectorObjects: InspectObject[] = []): GetInspectorResultInternal {
@ -230,7 +233,7 @@ export class Inspector extends React.Component<InspectorProps, InspectorState> {
}
}
private renderAccessoryButton(button: AccessoryButton, onClickHandler: (id: string) => void) {
private renderAccessoryButton(button: AccessoryButton) {
const { config, state, enabled } = button;
const currentState = config.states[state] || {};
const { icon, ...buttonAttrs } = currentState;
@ -240,7 +243,9 @@ export class Inspector extends React.Component<InspectorProps, InspectorState> {
className={styles.accessoryButton}
key={config.id}
disabled={!enabled}
onClick={() => onClickHandler(config.id)}
name={config.id}
data-current-state={state}
onClick={this.accessoryClick}
>
{Inspector.renderAccessoryIcon(icon)}
{currentState.label}
@ -252,12 +257,16 @@ export class Inspector extends React.Component<InspectorProps, InspectorState> {
private renderAccessoryButtons() {
return (
<PanelControls>
{this.state.buttons.map(accessoryButton => this.renderAccessoryButton(accessoryButton, this.accessoryClick))}
{this.state.buttons.map(accessoryButton => this.renderAccessoryButton(accessoryButton))}
</PanelControls>
);
}
private stateChanged(newState: InspectorState, oldState: InspectorState): void {
if (!this.canInspect) {
return;
}
if (oldState.botHash !== newState.botHash) {
this.botUpdated(newState.activeBot);
}
@ -266,16 +275,16 @@ export class Inspector extends React.Component<InspectorProps, InspectorState> {
this.updateInspector(this.state).catch();
}
if (oldState.logEntries !== newState.logEntries) {
this.chatLogUpdated(this.props.document.conversationId, newState.logEntries);
}
// Send these always
this.chatLogUpdated(this.props.document.documentId, newState.logEntries);
this.sendToExtension(ExtensionChannel.HighlightedObjectsUpdated, newState.highlightedObjects);
if (JSON.stringify(oldState.inspectObj) !== JSON.stringify(newState.inspectObj)) {
this.inspect(newState.inspectObj);
}
if ((oldState.themeInfo || { themeName: '' }).themeName !== newState.themeInfo.themeName) {
this.sendToInspector('theme', newState.themeInfo);
this.sendToExtension(ExtensionChannel.Theme, newState.themeInfo);
}
}
@ -353,12 +362,14 @@ export class Inspector extends React.Component<InspectorProps, InspectorState> {
}
};
private accessoryClick = (id: string): void => {
this.sendToInspector('accessory-click', id);
private accessoryClick = (event: MouseEvent<HTMLButtonElement>): void => {
const id = event.currentTarget.name;
const { currentState } = event.currentTarget.dataset;
this.sendToExtension(ExtensionChannel.AccessoryClick, id, currentState);
};
private toggleDevTools = (): void => {
this.sendToInspector('toggle-dev-tools');
this.sendToExtension(ExtensionChannel.ToggleDevTools);
};
private canInspect(inspectObj: InspectObject): boolean {
@ -376,21 +387,21 @@ export class Inspector extends React.Component<InspectorProps, InspectorState> {
// TODO - localization
const { channel } = event;
switch (channel) {
case 'enable-accessory':
case EmulatorChannel.EnableAccessory:
this.enableAccessory(event.args[0], event.args[1]);
break;
case 'set-accessory-state':
case EmulatorChannel.SetAccessoryState:
this.setAccessoryState(event.args[0], event.args[1]);
break;
case 'set-inspector-title':
case EmulatorChannel.SetInspectorTitle:
this.setState({ titleOverride: event.args[0] });
this.setInspectorTitle(event.args[0]);
break;
case 'logger.log':
case 'logger.error': {
case EmulatorChannel.Log:
case EmulatorChannel.LogError: {
const logLevel = channel === 'logger.log' ? LogLevel.Info : LogLevel.Error;
const { documentId } = this.props.document;
const inspectorName = this._state.titleOverride || this.state.inspector.name || 'inspector';
@ -399,7 +410,7 @@ export class Inspector extends React.Component<InspectorProps, InspectorState> {
break;
}
case 'logger.luis-editor-deep-link': {
case EmulatorChannel.LogLuisDeepLink: {
const { documentId } = this.props.document;
const inspectorName = this._state.titleOverride || this.state.inspector.name || 'inspector';
const text = `[${inspectorName}] ${event.args[0]}`;
@ -408,13 +419,25 @@ export class Inspector extends React.Component<InspectorProps, InspectorState> {
}
// record telemetry from extension
case 'track-event': {
case EmulatorChannel.TrackEvent: {
const eventName = event.args[0];
const eventProperties = event.args[1] || {};
this.props.trackEvent(eventName, eventProperties);
break;
}
case EmulatorChannel.SetHightlightedObjects: {
const [documentId, highlightedObjects] = event.args;
this.props.setHighlightedObjects(documentId, highlightedObjects);
break;
}
case EmulatorChannel.SetInspectorObjects: {
const [documentId, inspectorObjects] = event.args;
this.props.setInspectorObjects(documentId, inspectorObjects);
break;
}
default:
// eslint-disable-next-line no-console
console.warn('Unexpected message from inspector', event.channel, ...event.args);
@ -424,8 +447,8 @@ export class Inspector extends React.Component<InspectorProps, InspectorState> {
private sendInitializationStackToInspector(): void {
this.botUpdated(this.state.activeBot);
this.inspect(this.state.inspectObj);
this.sendToInspector('theme', this.state.themeInfo);
this.chatLogUpdated(this.props.document.conversationId, this.state.logEntries);
this.sendToExtension(ExtensionChannel.Theme, this.state.themeInfo);
this.chatLogUpdated(this.props.document.documentId, this.state.logEntries);
}
private inspect(obj: InspectObject) {
@ -433,19 +456,19 @@ export class Inspector extends React.Component<InspectorProps, InspectorState> {
// showInInspector is for internal bookkeeping and shouldn't make it to the view
// remove before rendering
delete obj.showInInspector;
this.sendToInspector('inspect', obj);
this.sendToExtension(ExtensionChannel.Inspect, obj);
}
}
private botUpdated(bot: IBotConfiguration) {
this.sendToInspector('bot-updated', bot);
this.sendToExtension(ExtensionChannel.BotUpdated, bot);
}
private chatLogUpdated(conversationId: string, logItems: LogEntry[]): void {
this.sendToInspector('chat-log-updated', conversationId, logItems);
private chatLogUpdated(documentId: string, logItems: LogEntry[]): void {
this.sendToExtension(ExtensionChannel.ChatLogUpdated, documentId, logItems);
}
private sendToInspector(channel: string, ...args: any[]) {
private sendToExtension(channel: ExtensionChannel, ...args: any[]) {
const { inspectorSrc } = this.state;
const inspector = this.webViewByLocation[encodeURI(inspectorSrc)];
if (!inspector || !this.domReadyByLocation[encodeURI(inspectorSrc)]) {

Просмотреть файл

@ -33,9 +33,11 @@
import { connect } from 'react-redux';
import { SharedConstants } from '@bfemulator/app-shared';
import { Activity } from 'botframework-schema';
import { RootState } from '../../../../../data/store';
import { executeCommand } from '../../../../../data/action/commandAction';
import { setHighlightedObjects, setInspectorObjects } from '../../../../../data/action/chatActions';
import { Inspector } from './inspector';
@ -55,6 +57,10 @@ const mapDispatchToProps = dispatch => {
trackEvent: (name: string, properties?: { [key: string]: any }) => {
dispatch(executeCommand(true, SharedConstants.Commands.Telemetry.TrackEvent, null, name, properties));
},
setHighlightedObjects: (documentId: string, objects: Activity[]) =>
dispatch(setHighlightedObjects(documentId, objects)),
setInspectorObjects: (documentId: string, inspectorObjects: Activity[]) =>
dispatch(setInspectorObjects(documentId, inspectorObjects)),
};
};

Просмотреть файл

@ -34,11 +34,13 @@
import * as React from 'react';
import { Activity } from 'botframework-schema';
import { ChatDocument } from '../../../../../data/reducer/chat';
import * as styles from './log.scss';
import { LogEntry } from './logEntryContainer';
export interface LogProps {
document: any;
document: ChatDocument;
}
export interface LogState {

Просмотреть файл

@ -13,16 +13,15 @@
color: var(--tab-color);
line-height: 36px;
min-height: 36px;
text-transform: uppercase;
padding-left: 16px;
display: flex;
white-space: nowrap;
justify-content: flex-end;
& > .accessories {
margin: 0 1px 0 auto;
width: auto;
display: flex;
align-items: center;
width: 100%;
& > button {
background-color: transparent;
@ -30,6 +29,38 @@
border: 0;
cursor: pointer;
white-space: nowrap;
&:hover:not([disabled]), &[selected], &[aria-selected] {
&:after {
border: none;
}
}
}
& > button[name="leftArrow"],
& > button[name="rightArrow"],
& > button[name="diff"] {
height: 30px;
width: auto;
min-width: auto;
padding: 0 6px;
&:hover:not([disabled]), &[selected], &[aria-selected] {
&:after {
border: none;
}
}
}
// left arrow diff button
& > button[name="leftArrow"] {
margin-left: auto;
}
// toggle diff button
& > button[name="diff"] {
padding-right: 12px; // items have 6 padding on each side, so right-most item should have 12 padding
}
}
}

Просмотреть файл

@ -53,7 +53,6 @@ export default class Panel extends React.Component<PanelProps, {}> {
return (
<div className={styles.panel}>
<div className={styles.panelHeader}>
{this.props.title}
<div className={styles.accessories}>
{filterChildren(this.props.children, child => hmrSafeNameComparison(child.type, PanelControls))}
</div>

Просмотреть файл

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="17px" height="16px" viewBox="0 0 17 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 55 (78076) - https://sketchapp.com -->
<title>Icons/Full MDL2 Assets/Next</title>
<desc>Created with Sketch.</desc>
<defs>
<filter x="-0.8%" y="-22.9%" width="101.6%" height="145.7%" filterUnits="objectBoundingBox" id="filter-1">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
<feMerge>
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
<polygon id="path-2" points="14.9619141 7.97314453 8.98876953 13.9462891 8.41748047 13.375 13.4130859 8.37939453 2 8.37939453 2 7.56689453 13.4130859 7.56689453 8.41748047 2.57128906 8.98876953 2"></polygon>
</defs>
<g id="SIDECAR" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="4.10-sidecar-07" transform="translate(-1322.000000, -219.000000)">
<g id="Group-2" transform="translate(100.000000, 100.000000)">
<g id="dark/tab-bar" filter="url(#filter-1)" transform="translate(371.000000, 107.000000)">
<g id="Group-3">
<g id="Icons/Full-MDL2-Assets/Next" transform="translate(851.000000, 10.000000)">
<mask id="mask-3" fill="white">
<use xlink:href="#path-2"></use>
</mask>
<use id="" fill="#000000" fill-rule="evenodd" xlink:href="#path-2"></use>
<g id="_color/Primary-1-(00BCF2)-sql-light" mask="url(#mask-3)" fill="#00BCF2" fill-rule="evenodd">
<rect id="Rectangle" x="0" y="0" width="16" height="16"></rect>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>

После

Ширина:  |  Высота:  |  Размер: 2.3 KiB

Просмотреть файл

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="17px" height="16px" viewBox="0 0 17 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 55 (78076) - https://sketchapp.com -->
<title>Icons/Full MDL2 Assets/Next Copy</title>
<desc>Created with Sketch.</desc>
<defs>
<filter x="-0.8%" y="-22.9%" width="101.6%" height="145.7%" filterUnits="objectBoundingBox" id="filter-1">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
<feMerge>
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
<polygon id="path-2" points="14.9619141 8.37939453 3.54882812 8.37939453 8.54443359 13.375 7.97314453 13.9462891 2 7.97314453 7.97314453 2 8.54443359 2.57128906 3.54882812 7.56689453 14.9619141 7.56689453"></polygon>
</defs>
<g id="SIDECAR" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="4.10-sidecar-07" transform="translate(-1290.000000, -219.000000)">
<g id="Group-2" transform="translate(100.000000, 100.000000)">
<g id="dark/tab-bar" filter="url(#filter-1)" transform="translate(371.000000, 107.000000)">
<g id="Group-3">
<g id="Icons/Full-MDL2-Assets/Previous" transform="translate(819.000000, 10.000000)">
<mask id="mask-3" fill="white">
<use xlink:href="#path-2"></use>
</mask>
<use id="" fill="#000000" fill-rule="evenodd" xlink:href="#path-2"></use>
<g id="_color/Primary-1-(00BCF2)-sql-light" mask="url(#mask-3)" fill="#00BCF2" fill-rule="evenodd">
<rect id="Rectangle" x="0" y="0" width="16" height="16"></rect>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>

После

Ширина:  |  Высота:  |  Размер: 2.3 KiB

Просмотреть файл

@ -125,8 +125,6 @@
"@bfemulator/app-shared": "^1.0.0",
"@bfemulator/client": "^1.0.0",
"@bfemulator/emulator-core": "^1.0.0-0",
"@bfemulator/extension-bot-state-visualizer": "^1.0.0",
"@bfemulator/extension-debug": "^1.0.0",
"@bfemulator/extension-json": "^1.0.0",
"@bfemulator/extension-luis": "^1.0.0",
"@bfemulator/extension-qnamaker": "^1.0.0",

Просмотреть файл

@ -1,3 +0,0 @@
{
"location": "../../node_modules/@bfemulator/extension-bot-state-visualizer"
}

Просмотреть файл

@ -47,12 +47,16 @@ ipcRenderer.on('chat-log-updated', (sender, conversationId, logEntries) => {
window.host.dispatch('chat-log-updated', conversationId, logEntries);
});
ipcRenderer.on('highlighted-objects-updated', (sender, highlightedObjects) => {
window.host.dispatch('highlighted-objects-updated', highlightedObjects);
});
ipcRenderer.on('toggle-dev-tools', () => {
remote.getCurrentWebContents().toggleDevTools();
});
ipcRenderer.on('accessory-click', (sender, id) => {
window.host.dispatch('accessory-click', id);
ipcRenderer.on('accessory-click', (sender, id, currentState) => {
window.host.dispatch('accessory-click', id, currentState);
});
ipcRenderer.on('theme', (sender, ...args) => {
@ -61,13 +65,17 @@ ipcRenderer.on('theme', (sender, ...args) => {
window.host = {
bot: {},
handlers: {
'accessory-click': [],
'bot-updated': [],
'chat-log-updated': [],
inspect: [],
theme: [],
},
handlers: new Proxy(
{},
{
get(target, p) {
if (!(p in target)) {
target[p] = [];
}
return target[p];
},
}
),
logger: {
error: function(message) {
ipcRenderer.sendToHost('logger.error', message);
@ -111,6 +119,14 @@ window.host = {
ipcRenderer.sendToHost('track-event', name, properties);
},
setHighlightedObjects(documentId, objects) {
ipcRenderer.sendToHost('set-highlighted-objects', documentId, objects);
},
setInspectorObjects(documentId, objects) {
ipcRenderer.sendToHost('set-inspector-objects', documentId, objects);
},
dispatch: function(event, ...args) {
this.handlers[event].forEach(handler => handler(...args));
},

Просмотреть файл

@ -1,29 +0,0 @@
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"chrome": "68",
"esmodules": true
}
}
],
"@babel/preset-typescript"
],
"ignore": [
"**/*.d.ts"
],
"sourceMaps": "inline",
"plugins": [
"@babel/plugin-transform-react-jsx",
[
"@babel/proposal-decorators",
{
"decoratorsBeforeExport": true
}
],
"@babel/plugin-proposal-class-properties",
"@babel/plugin-transform-runtime"
]
}

Просмотреть файл

@ -1,3 +0,0 @@
module.exports = {
extends: '../../../.eslintrc.react.js',
};

26
packages/extensions/botState/.gitignore поставляемый
Просмотреть файл

@ -1,26 +0,0 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
/node_modules
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# visual studio
/.vs
public/

Просмотреть файл

@ -1,31 +0,0 @@
{
"name": "Bot State Visualizer",
"client": {
"inspectors": [
{
"name": "Bot State Visualizer",
"src": "public/index.html",
"criteria": [
],
"summaryText": [
"label"
],
"accessories": [
{
"id": "json",
"states": {
"default": {
"label": "Json"
},
"selected": {
"label": "Json",
"aria-selected": true
}
}
}
]
}
]
}
}

117
packages/extensions/botState/package-lock.json сгенерированный
Просмотреть файл

@ -1,117 +0,0 @@
{
"name": "@bfemulator/extension-bot-state-visualizer",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"ajv": {
"version": "6.10.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz",
"integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==",
"dev": true,
"requires": {
"fast-deep-equal": "^2.0.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
}
},
"ajv-keywords": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.0.tgz",
"integrity": "sha512-aUjdRFISbuFOl0EIZc+9e4FfZp0bDZgAdOOf30bJmw8VM9v84SHyVyxDfbWxpGYbdZD/9XoKxfHVNmxPkhwyGw==",
"dev": true
},
"big.js": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
"integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
"dev": true
},
"emojis-list": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
"integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=",
"dev": true
},
"fast-deep-equal": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
"dev": true
},
"fast-json-stable-stringify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
"integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
"dev": true
},
"file-loader": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz",
"integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==",
"dev": true,
"requires": {
"loader-utils": "^1.0.2",
"schema-utils": "^0.4.5"
}
},
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
},
"json5": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
"dev": true,
"requires": {
"minimist": "^1.2.0"
}
},
"loader-utils": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz",
"integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==",
"dev": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^2.0.0",
"json5": "^1.0.1"
}
},
"minimist": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true
},
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
"dev": true
},
"schema-utils": {
"version": "0.4.7",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz",
"integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==",
"dev": true,
"requires": {
"ajv": "^6.1.0",
"ajv-keywords": "^3.1.0"
}
},
"uri-js": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
"integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
"dev": true,
"requires": {
"punycode": "^2.1.0"
}
}
}
}

Просмотреть файл

@ -1,75 +0,0 @@
{
"name": "@bfemulator/extension-bot-state-visualizer",
"version": "1.0.0",
"description": "",
"main": "index.js",
"author": "Microsoft",
"license": "ISC",
"scripts": {
"start": "webpack-dev-server --mode development --hot --inline --content-base ./public",
"build": "webpack --mode production",
"build:dev": "webpack --mode development",
"lint": "eslint --color --quiet --ext .js,.jsx,.ts,.tsx ./src",
"lint:fix": "npm run lint -- --fix",
"test": ""
},
"dependencies": {
"@bfemulator/app-shared": "^1.0.0",
"@bfemulator/sdk-client": "^1.0.0",
"@bfemulator/sdk-shared": "^1.0.0",
"d3": "^5.9.2"
},
"devDependencies": {
"@babel/cli": "^7.1.0",
"@babel/core": "^7.1.0",
"@babel/plugin-proposal-class-properties": "^7.1.0",
"@babel/plugin-proposal-decorators": "^7.4.0",
"@babel/plugin-proposal-object-rest-spread": "^7.0.0",
"@babel/plugin-transform-react-jsx": "^7.0.0",
"@babel/plugin-transform-runtime": "^7.4.4",
"@babel/preset-env": "^7.1.0",
"@babel/preset-typescript": "^7.1.0",
"@babel/runtime": "^7.1.5",
"@types/d3": "^5.7.1",
"@types/jest": "24.0.13",
"@types/node": "8.9.3",
"copy-webpack-plugin": "^4.5.1",
"css-loader": "^1.0.1",
"eslint": "^5.12.0",
"eslint-config-prettier": "^3.5.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-notice": "^0.7.7",
"eslint-plugin-prettier": "^3.0.1",
"eslint-plugin-typescript": "^1.0.0-rc.3",
"file-loader": "^1.1.11",
"hard-source-webpack-plugin": "^0.12.0",
"jest": "24.8.0",
"resolve-url-loader": "^2.3.0",
"sass-loader": "^7.1.0",
"style-loader": "^0.21.0",
"typescript": "3.1.1",
"typings-for-css-modules-loader": "^1.7.0",
"webpack": "^4.32.2",
"webpack-cli": "^3.3.2",
"webpack-dev-server": "^3.4.1"
},
"jest": {
"setupTestFrameworkScriptFile": "../../../../testSetup.js",
"transform": {
"^.+\\.(tsx?|jsx?)$": "babel-jest"
},
"testURL": "http://localhost",
"rootDir": "./src",
"testMatch": [
"**/?(*.)(spec|test).(ts)?(x)"
],
"moduleFileExtensions": [
"ts",
"tsx",
"js",
"jsx",
"json",
"node"
]
}
}

Просмотреть файл

@ -1,215 +0,0 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license.
//
// Microsoft Bot Framework: http://botframework.com
//
// Bot Framework Emulator Github:
// https://github.com/Microsoft/BotFramwork-Emulator
//
// Copyright (c) Microsoft Corporation
// All rights reserved.
//
// MIT License:
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import * as d3 from 'd3';
import { HierarchyPointNode } from 'd3';
// TODO: Revert import to `@bfemulator/sdk-shared` once issue #1333 (https://github.com/Microsoft/BotFramework-Emulator/issues/1333) is resolved.
import { json2HTML } from '@bfemulator/sdk-shared/build/utils/json2HTML';
import { BotState, HierarchicalData } from './types';
import { ViewState } from './ViewState';
import { buildHierarchicalData } from './utils';
export class BotStateVisualizer {
get dataProvider(): BotState {
return this._dataProvider;
}
set dataProvider(value: BotState) {
if (this._dataProvider === value) {
return;
}
this._dataProvider = value;
if (value) {
this.rebuildRootHierarchy();
}
this.render();
}
private readonly visualizerSelector: string;
private readonly jsonSelector: string;
private rootHierarchy: d3.HierarchyNode<{}>;
private _viewState: ViewState;
private _dataProvider: BotState;
public isDiff: boolean;
private static getNodeText(data: HierarchyPointNode<HierarchicalData>): string {
let { name } = data.data;
// filter out undefined only
if (data.data.value !== undefined) {
name += ': ' + data.data.value;
}
return name;
}
private resizeNodes(): d3.HierarchyPointNode<{}> {
const root = this.rootHierarchy;
return d3.cluster().nodeSize([15, (document.body.clientWidth - 250) / root.height])(root);
}
constructor(visualizerSelector: string, jsonSelector: string) {
this.visualizerSelector = visualizerSelector;
this.jsonSelector = jsonSelector;
window.addEventListener('resize', () => this.render());
}
public get viewState(): ViewState {
return this._viewState;
}
public set viewState(viewState: ViewState) {
if (this._viewState === viewState) {
return;
}
this._viewState = viewState;
this.render();
}
public render = (): void => {
const svg = d3.select(this.visualizerSelector);
svg.selectAll('g').remove();
const div = document.querySelector(this.jsonSelector);
div.innerHTML = '';
if (!this._dataProvider) {
return;
}
// Rendering JSON
if (this.viewState === ViewState.Json) {
div.innerHTML = json2HTML(this._dataProvider, this.isDiff);
return;
}
// Rendering Dendrogram
const root = this.resizeNodes();
const g = svg
.append('g')
.attr('font-family', 'Menlo')
.attr('font-size', 10);
g.append('g')
.attr('fill', 'none')
.attr('stroke', '#555')
.attr('stroke-opacity', 0.4)
.attr('stroke-width', 1.5)
.selectAll('path')
.data(root.links())
.join('path')
.attr(
'd',
d => `
M${d.target.y},${d.target.x}
C${d.source.y + 5},${d.target.x}
${d.source.y + 5},${d.source.x}
${d.source.y},${d.source.x}
`
);
const node = g
.append('g')
.attr('stroke-linejoin', 'round')
.attr('stroke-width', 3)
.selectAll('g')
.data(root.descendants().reverse())
.join('g')
.attr('transform', d => `translate(${d.y},${d.x})`);
node
.append('circle')
.attr('fill', d => (d.children ? '#555' : '#999'))
.attr('r', 2.5);
node
.append('text')
.attr('dy', '0.31em')
.attr('x', d => (d.children ? -6 : 6))
.text(BotStateVisualizer.getNodeText)
.attr('class', this.getClassNameFromValueType)
.filter((d: any) => d.children)
.attr('text-anchor', 'end')
.lower();
const gRect = (g.node() as SVGElement).getBoundingClientRect();
const svgRect = (svg.node() as SVGElement).getBoundingClientRect();
svg.style('height', gRect.height);
svg.style('width', gRect.width);
// attempt to center the svg within the window
const deltaY = (document.body.clientHeight - gRect.height) / 2;
svg.attr('transform', `translate(0, ${Math.max(deltaY, 0)})`);
// center the graphic tag within the svg
g.attr('transform', `translate(70, ${Math.abs(gRect.top - svgRect.top)})`);
};
private getClassNameFromValueType = (data: HierarchyPointNode<HierarchicalData>): string => {
const { name } = data.data;
if (this.isDiff) {
if (name.startsWith('+')) {
return 'added';
}
if (name.startsWith('-')) {
return 'removed';
}
return 'key';
}
const type = data.data.value ? typeof data.data.value : null;
switch (type) {
case 'string':
case 'number':
case 'boolean':
return type;
default:
return 'null';
}
};
private rebuildRootHierarchy() {
const hierarchicalData = buildHierarchicalData(this._dataProvider);
this.rootHierarchy = d3.hierarchy<any>(hierarchicalData).sort((a, b) => {
if (!isNaN(+a.data.name) && !isNaN(+b.data.name)) {
if (a.data.name < b.data.name) {
return -1;
}
if (a.data.name > b.data.name) {
return 1;
}
return 0;
} else {
return a.height - b.height;
}
});
}
}

Просмотреть файл

@ -1,36 +0,0 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license.
//
// Microsoft Bot Framework: http://botframework.com
//
// Bot Framework Emulator Github:
// https://github.com/Microsoft/BotFramwork-Emulator
//
// Copyright (c) Microsoft Corporation
// All rights reserved.
//
// MIT License:
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
export enum ViewState {
Graph = 'graph',
Json = 'json',
}

Просмотреть файл

@ -1,93 +0,0 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license.
//
// Microsoft Bot Framework: http://botframework.com
//
// Bot Framework Emulator Github:
// https://github.com/Microsoft/BotFramwork-Emulator
//
// Copyright (c) Microsoft Corporation
// All rights reserved.
//
// MIT License:
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import { ValueTypes } from '@bfemulator/app-shared/built/enums';
import { BotStateVisualizer } from './BotStateVisualizer';
import { IpcHandler } from './utils';
import { BotState } from './types';
import { ViewState } from './ViewState';
export class WindowHostReceiver {
private visualizer: BotStateVisualizer;
constructor(visualizer: BotStateVisualizer) {
this.visualizer = visualizer;
}
@IpcHandler('inspect')
protected inspectHandler(data: { value: BotState; valueType: string }): void {
const { visualizer } = this;
visualizer.isDiff = data.valueType === ValueTypes.Diff;
visualizer.dataProvider = data.value;
this.accessoryClick(visualizer.viewState || ViewState.Json);
}
@IpcHandler('theme')
protected async themeHandler(themeInfo): Promise<void> {
const oldThemeComponents = document.querySelectorAll('[data-theme-component="true"]');
const head = document.querySelector('head');
const fragment = document.createDocumentFragment();
const promises = [];
// Create the new links for each theme component
themeInfo.themeComponents.forEach(themeComponent => {
const link = document.createElement('link');
promises.push(
new Promise(resolve => {
link.addEventListener('load', resolve);
})
);
link.href = themeComponent;
link.rel = 'stylesheet';
link.setAttribute('data-theme-component', 'true');
fragment.appendChild(link);
});
head.insertBefore(fragment, head.firstElementChild);
// Wait for all the links to load their css
await Promise.all(promises);
// Remove the old links
oldThemeComponents.forEach(themeComponent => {
if (themeComponent.parentElement) {
themeComponent.parentElement.removeChild(themeComponent);
}
});
}
@IpcHandler('accessory-click')
protected accessoryClick(id: ViewState): void {
const { host } = window as any;
const resetId = id === ViewState.Graph ? ViewState.Json : ViewState.Graph;
host.setAccessoryState(resetId, 'default');
host.setAccessoryState(id, 'selected');
this.visualizer.viewState = id;
}
}

Просмотреть файл

@ -1,126 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<title>Bot State Viewer</title>
<style>
html {
width: 100%;
height: 100%;
}
div:empty, svg:empty {
display: none;
}
body {
padding: 0;
margin: 0;
height: 100%;
color: #d4d4d4;
}
::-webkit-scrollbar {
width: 5px;
height: 10px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: #5E5E5E;
}
#json-visualizer {
color: var(--log-panel-item-info);
padding: 8px 16px 0 16px;
font-family: var(--monospace-font-family);
font-size: 13px;
word-wrap: break-word;
white-space: pre-wrap;
overflow-y: auto;
user-select: text;
}
.json-key {
color: var(--log-panel-link);
}
.json-string {
color: var(--log-panel-item-error);
}
.json-number {
color: var(--log-panel-timestamp);
}
.json-boolean {
color: var(--log-panel-link);
}
.json-null {
color: var(--log-panel-link);
}
.devtools {
position: fixed;
bottom: 0;
right: 0;
}
.tool-button {
background-color: transparent;
color: var(--log-panel-link);
border: 1px solid var(--log-panel-link);
padding: 6px;
}
.tool-button:hover {
color: var(--log-panel-link);
}
#bot-state-visualizer, #json-visualizer {
padding: 10px;
font-family: var(--monospace-font-family);
font-size: 13px;
user-select: text;
}
.key {
fill: var(--log-panel-link);
}
.string, .removed, .json-removed {
color: var(--log-panel-item-error);
fill: var(--log-panel-item-error);
}
.number, .added, .json-added {
color: var(--log-panel-timestamp);
fill: var(--log-panel-timestamp);
}
.boolean {
fill: var(--log-panel-link);
}
.null {
fill: var(--log-panel-link);
}
.default, .json-default {
color: var(--log-panel-item-info);
fill: var(--log-panel-item-info);
}
</style>
</head>
<body>
<svg id="bot-state-visualizer"></svg>
<div id="json-visualizer"></div>
<script type="application/javascript" src="botstate.js"></script>
</body>
</html>

Просмотреть файл

@ -1,112 +0,0 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license.
//
// Microsoft Bot Framework: http://botframework.com
//
// Bot Framework Emulator Github:
// https://github.com/Microsoft/BotFramwork-Emulator
//
// Copyright (c) Microsoft Corporation
// All rights reserved.
//
// MIT License:
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import { BotStateVisualizer } from './BotStateVisualizer';
import { ViewState } from './ViewState';
import { WindowHostReceiver } from './WindowHostReceiver';
import { BotState } from './types';
(window as any).host = {
bot: {},
handlers: {
'accessory-click': [],
'bot-updated': [],
inspect: [],
theme: [],
},
on: function(event, handler) {
if (handler && Array.isArray(this.handlers[event]) && !this.handlers[event].includes(handler)) {
this.handlers[event].push(handler);
}
return () => {
this.handlers[event] = this.handlers[event].filter(item => item !== handler);
};
},
setAccessoryState: () => void 0,
};
const botState: BotState = {
conversationState: {
memory: {
id: 12,
value: 'bot',
},
},
userState: {
memory: {
value: 'greetings!',
dialogStack: ['test', 'test1', 'test2'],
},
},
};
describe('The BotStateVisualizer', () => {
let svg;
let div;
let visualizer;
beforeAll(() => {
svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.id = 'bot-state-visualizer';
div = document.createElement('div');
div.id = 'json-visualizer';
document.body.appendChild(svg);
document.body.appendChild(div);
visualizer = new BotStateVisualizer('#bot-state-visualizer', '#json-visualizer');
visualizer.viewState = ViewState.Graph;
new WindowHostReceiver(visualizer);
});
it('should render with data', () => {
(window as any).host.handlers.inspect.forEach(callback => callback({ valueType: '', value: botState }));
(window as any).host.handlers.theme.forEach(callback => callback({ themeComponents: ['dark.css'] }));
expect(svg.children[0].children.length).toBe(2);
});
it('should re-render when the window resizes', () => {
(window as any).host.handlers.inspect.forEach(callback => callback({ valueType: '', value: botState }));
const spy = jest.spyOn(visualizer, 'render');
window.dispatchEvent(new Event('resize'));
expect(spy).toHaveBeenCalled();
});
it('should switch themes', () => {
(window as any).host.handlers.theme.forEach(callback => callback({ themeComponents: ['light.css'] }));
let link: HTMLLinkElement = document.querySelector('[data-theme-component="true"]');
expect(link.href).toBe('http://localhost/light.css');
(window as any).host.handlers.theme.forEach(callback => callback({ themeComponents: ['dark.css'] }));
link = document.querySelector('[data-theme-component="true"]');
expect(link.href).toBe('http://localhost/dark.css');
});
});

Просмотреть файл

@ -1,75 +0,0 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license.
//
// Microsoft Bot Framework: http://botframework.com
//
// Bot Framework Emulator Github:
// https://github.com/Microsoft/BotFramwork-Emulator
//
// Copyright (c) Microsoft Corporation
// All rights reserved.
//
// MIT License:
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import { BotState, HierarchicalData } from './types';
export function IpcHandler(type: string): MethodDecorator {
return function(elementDescriptor: any) {
const { key, descriptor } = elementDescriptor;
const initializer = function() {
const bound = this[key].bind(this);
(window as any).host.on(type, bound);
return bound;
};
elementDescriptor.extras = [
{
kind: 'field',
key,
placement: 'own',
initializer,
descriptor: { ...descriptor, value: undefined },
},
];
return elementDescriptor;
};
}
export function hydrateWithChildren(data: any, parent: HierarchicalData) {
Object.keys(data).forEach(key => {
const child = { name: key } as HierarchicalData;
if (data[key] !== null && typeof data[key] === 'object') {
child.children = [];
hydrateWithChildren(data[key], child);
} else {
child.value = data[key];
}
parent.children.push(child);
});
}
export function buildHierarchicalData(botState: BotState): HierarchicalData {
const dataProvider = { name: 'botState', children: [] };
hydrateWithChildren(botState, dataProvider);
return dataProvider;
}

Просмотреть файл

@ -1,11 +0,0 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"outDir": "build/dist",
"lib": ["dom", "es2015.proxy", "es5", "esnext", "es7"],
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "esnext"
},
"include": ["./src"]
}

Просмотреть файл

@ -1,75 +0,0 @@
const { WatchIgnorePlugin } = require('webpack');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const path = require('path');
module.exports = {
entry: {
botstate: path.resolve('./src/index.ts'),
},
devtool: 'source-map',
target: 'electron-renderer',
module: {
rules: [
{
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'typings-for-css-modules-loader',
options: {
localIdentName: '[local]__[hash:base64:5]',
modules: true,
sass: false,
namedExport: true,
camelCase: true,
sourcemaps: true,
banner: '// This is a generated file. Changes are likely to result in being overwritten',
},
},
'resolve-url-loader',
'sass-loader',
],
},
{
test: /\.(png|jpg|gif|svg)$/,
use: ['file-loader'],
},
{
test: /\.(tsx?)|(jsx?)$/,
exclude: [/node_modules/],
use: {
loader: 'babel-loader',
options: {
ignore: ['**/*.spec.ts'],
},
},
},
],
},
devServer: {
hot: true,
inline: true,
port: 8080,
historyApiFallback: false,
},
resolve: {
extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'],
},
output: {
path: path.resolve('./public'),
filename: '[name].js',
publicPath: 'http://localhost:8080',
},
stats: {
warnings: false,
},
externals: {},
plugins: [
new WatchIgnorePlugin(['./build/**/*.*', './public/**/*.*', './src/**/*.d.ts']),
new CopyWebpackPlugin([{ from: './src/index.html', to: './index.html' }]),
],
};

Просмотреть файл

@ -1,42 +0,0 @@
{
"name": "Debug",
"node": {
"main": "main/built/index.js",
"debug": {
"enabled": false,
"websocket": {
"port": 8081
}
}
},
"client": {
"basePath": "client/build/",
"debug": {
"enabled": false,
"webpack": {
"port": 3031
}
},
"deeplink": "",
"filetypes": [],
"inspectors": [
{
"name": "Debug",
"src": "client/build/index.html",
"criteria": [
{
"path": "type",
"value": "event"
},
{
"path": "name",
"value": "debug"
}
],
"summaryText": [
"text"
]
}
]
}
}

Просмотреть файл

@ -1,23 +0,0 @@
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"chrome": "58",
"esmodules": true
}
}
],
"@babel/preset-typescript"
],
"ignore": [
"**/*.d.ts"
],
"sourceMaps": "inline",
"plugins": [
"@babel/proposal-class-properties",
"@babel/plugin-transform-react-jsx",
"@babel/plugin-transform-runtime"
]
}

Просмотреть файл

@ -1,3 +0,0 @@
module.exports = {
extends: '../../../../.eslintrc.react.js',
};

23
packages/extensions/debug/client/.gitignore поставляемый
Просмотреть файл

@ -1,23 +0,0 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
/node_modules
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
public/qna.js

Разница между файлами не показана из-за своего большого размера Загрузить разницу

5389
packages/extensions/debug/client/package-lock.json сгенерированный

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,41 +0,0 @@
{
"name": "@bfemulator/extension-debug-client",
"version": "0.1.0",
"private": true,
"homepage": "./",
"dependencies": {
"react": "~16.3.2",
"react-dom": "~16.3.2"
},
"scripts": {
"start": "cross-env BROWSER=none PORT=3031 react-scripts start",
"build": "webpack --mode production",
"lint": "eslint --color --quiet --ext .js,.jsx,.ts,.tsx ./src",
"lint:fix": "npm run lint -- --fix",
"test": ""
},
"devDependencies": {
"@babel/core": "^7.1.0",
"@babel/plugin-proposal-class-properties": "^7.1.0",
"@babel/plugin-proposal-object-rest-spread": "^7.0.0",
"@babel/plugin-transform-react-jsx": "^7.0.0",
"@babel/plugin-transform-runtime": "^7.4.4",
"@babel/preset-env": "^7.1.0",
"@babel/preset-typescript": "^7.1.0",
"@babel/runtime": "^7.1.5",
"@types/jest": "24.0.13",
"babel-eslint": "^10.0.1",
"babel-jest": "24.8.0",
"css-loader": "^1.0.1",
"eslint": "^5.12.0",
"eslint-config-prettier": "^3.5.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-notice": "^0.7.7",
"eslint-plugin-prettier": "^3.0.1",
"eslint-plugin-typescript": "^1.0.0-rc.3",
"jest": "24.8.0",
"url-loader": "^1.0.1",
"webpack": "^4.32.2",
"webpack-cli": "^3.3.2"
}
}

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 3.8 KiB

Просмотреть файл

@ -1,40 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

Просмотреть файл

@ -1,15 +0,0 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": "./index.html",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

Просмотреть файл

@ -1,28 +0,0 @@
.App {
text-align: center;
}
.App-logo {
animation: App-logo-spin infinite 20s linear;
height: 80px;
}
.App-header {
background-color: #222;
height: 150px;
padding: 20px;
color: white;
}
.App-title {
font-size: 1.5em;
}
.App-intro {
font-size: large;
}
@keyframes App-logo-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}

Просмотреть файл

@ -1,55 +0,0 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license.
//
// Microsoft Bot Framework: http://botframework.com
//
// Bot Framework Emulator Github:
// https://github.com/Microsoft/BotFramwork-Emulator
//
// Copyright (c) Microsoft Corporation
// All rights reserved.
//
// MIT License:
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class App extends Component {
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
</div>
);
}
}
export default App;

Просмотреть файл

@ -1,43 +0,0 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license.
//
// Microsoft Bot Framework: http://botframework.com
//
// Bot Framework Emulator Github:
// https://github.com/Microsoft/BotFramwork-Emulator
//
// Copyright (c) Microsoft Corporation
// All rights reserved.
//
// MIT License:
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App />, div);
ReactDOM.unmountComponentAtNode(div);
});

Просмотреть файл

@ -1,55 +0,0 @@
:global {
body {
padding: 0;
margin: 0;
height: 100%;
color: #d4d4d4;
font-family: monospace;
}
::-webkit-scrollbar {
width: 5px;
height: 10px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: #5E5E5E;
}
html {
.light, .dark {
}
--border-color: #00BCF2;
.high-contrast {
--border-color: #F38518;
}
}
}
li:focus, ul:focus {
outline: none;
position: relative;
&::before {
content: '';
position: absolute;
width: 100%;
height: 18px;
border: 1px solid var(--border-color);
top: 1px;
left: 0;
box-sizing: border-box;
}
}
li:focus::before {
left: 10px;
width: calc(100% - 10px);
}

Просмотреть файл

@ -1,40 +0,0 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license.
//
// Microsoft Bot Framework: http://botframework.com
//
// Bot Framework Emulator Github:
// https://github.com/Microsoft/BotFramwork-Emulator
//
// Copyright (c) Microsoft Corporation
// All rights reserved.
//
// MIT License:
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));

Просмотреть файл

@ -1,7 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3">
<g fill="#61DAFB">
<path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/>
<circle cx="420.9" cy="296.5" r="45.7"/>
<path d="M520.5 78.1z"/>
</g>
</svg>

До

Ширина:  |  Высота:  |  Размер: 2.6 KiB

Просмотреть файл

@ -1,148 +0,0 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license.
//
// Microsoft Bot Framework: http://botframework.com
//
// Bot Framework Emulator Github:
// https://github.com/Microsoft/BotFramwork-Emulator
//
// Copyright (c) Microsoft Corporation
// All rights reserved.
//
// MIT License:
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// In production, we register a service worker to serve assets from local cache.
// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on the "N+1" visit to a page, since previously
// cached resources are updated in the background.
// To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
// This link also includes instructions on opting out of this behavior.
const isLocalhost = Boolean(
window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
// 127.0.0.1/8 is considered localhost for IPv4.
window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/)
);
export default function register() {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
return;
}
window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) {
// This is running on localhost. Lets check if a service worker still exists or not.
checkValidServiceWorker(swUrl);
// Add some additional logging to localhost, pointing developers to the
// service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => {
// eslint-disable-next-line no-console
console.log(
'This web app is being served cache-first by a service ' +
'worker. To learn more, visit https://goo.gl/SC7cgQ'
);
});
} else {
// Is not local host. Just register service worker
registerValidSW(swUrl);
}
});
}
}
function registerValidSW(swUrl) {
navigator.serviceWorker
.register(swUrl)
.then(registration => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// At this point, the old content will have been purged and
// the fresh content will have been added to the cache.
// It's the perfect time to display a "New content is
// available; please refresh." message in your web app.
// eslint-disable-next-line no-console
console.log('New content is available; please refresh.');
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
// eslint-disable-next-line no-console
console.log('Content is cached for offline use.');
}
}
};
};
})
.catch(error => {
// eslint-disable-next-line no-console
console.error('Error during service worker registration:', error);
});
}
function checkValidServiceWorker(swUrl) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl)
.then(response => {
// Ensure service worker exists, and that we really are getting a JS file.
if (response.status === 404 || response.headers.get('content-type').indexOf('javascript') === -1) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => {
registration.unregister().then(() => {
window.location.reload();
});
});
} else {
// Service worker found. Proceed as normal.
registerValidSW(swUrl);
}
})
.catch(() => {
// eslint-disable-next-line no-console
console.log('No internet connection found. App is running in offline mode.');
});
}
export function unregister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => {
registration.unregister();
});
}
}

Просмотреть файл

@ -1,7 +0,0 @@
{
"extends": "../../../../tsconfig.json",
"compilerOptions": {
"outDir": "build/dist"
},
"include": ["./src"]
}

Просмотреть файл

@ -1,59 +0,0 @@
const path = require('path');
module.exports = {
entry: {
qna: path.resolve('./src/index.jsx')
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.(png|jpg|gif|svg)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192
}
}
]
},
{
test: /\.(tsx?)|(jsx)$/,
exclude: [/node_modules/],
use: {
loader: 'babel-loader',
"options": {
"ignore": ["**/*.spec.tsx?"]
}
}
},
],
},
devServer: {
hot: true,
inline: true,
port: 8080,
historyApiFallback: false
},
resolve: {
extensions: ['.js', '.jsx', '.json', '.ts', '.tsx']
},
output: {
path: path.resolve('./public'),
filename: '[name].js',
publicPath: 'http://localhost:8080',
},
stats: {
warnings: false
},
externals: {},
};

3126
packages/extensions/debug/package-lock.json сгенерированный

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,22 +0,0 @@
{
"name": "@bfemulator/extension-debug",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "jest",
"build": "npm run build:client",
"build:client": "cd ./client && npm run build"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@types/jest": "24.0.13",
"babel-jest": "24.8.0",
"jest": "24.8.0",
"npm-run-all": "^4.1.5"
},
"dependencies": {
"@babel/runtime": "^7.1.5"
}
}

Просмотреть файл

@ -36,6 +36,51 @@
"aria-selected": true
}
}
},
{
"id": "leftArrow",
"states": {
"default": {
"icon": "leftArrow"
},
"selected": {
"aria-selected": true,
"icon": "leftArrowSelected"
},
"disabled": {
"aria-disabled": true
}
}
},
{
"id": "rightArrow",
"states": {
"default": {
"icon": "rightArrow"
},
"selected": {
"aria-selected": true,
"icon": "rightArrowSelected"
},
"disabled": {
"aria-disabled": true
}
}
},
{
"id": "diff",
"states": {
"default": {
"label": "Show Diff"
},
"selected": {
"label": "Hide Diff",
"aria-selected": true
},
"disabled": {
"aria-disabled": true
}
}
}
]
}

Просмотреть файл

@ -10,7 +10,9 @@
"@bfemulator/ui-react": "^1.0.0",
"@bfemulator/sdk-shared": "^1.0.0",
"@bfemulator/app-shared": "^1.0.0",
"@bfemulator/sdk-client": "^1.0.0",
"botframework-schema": "^4.3.4",
"deep-diff": "^1.0.2",
"react": "~16.3.2",
"react-dom": "~16.3.2",
"react-json-tree": "^0.11.2"
@ -31,6 +33,7 @@
"@babel/plugin-transform-runtime": "^7.4.4",
"@babel/preset-env": "^7.1.0",
"@babel/preset-typescript": "^7.1.0",
"@types/deep-diff": "^1.0.0",
"@types/jest": "24.0.13",
"@types/lscache": "^1.0.29",
"@types/react": "~16.3.2",

Просмотреть файл

@ -32,81 +32,58 @@
//
import * as React from 'react';
import { mount } from 'enzyme';
import { ValueTypes } from '@bfemulator/app-shared';
import { ExtensionChannel } from '@bfemulator/sdk-shared';
import { InspectorHost } from '@bfemulator/sdk-client';
import { WindowHostReceiver } from './windowHostReceiver';
import { windowHostReceiver } from './windowHostReceiver';
import { JsonViewerExtension } from './jsonViewerExtension';
import { mockChatLogs, mockDiff0, mockDiff1 } from './mocks';
import { extractBotStateActivitiesFromLogEntries } from './utils';
(window as any).host = {
handlers: {
inspect: [],
theme: [],
},
on: function(event, handler) {
if (handler && Array.isArray(this.handlers[event]) && !this.handlers[event].includes(handler)) {
this.handlers[event].push(handler);
}
return () => {
this.handlers[event] = this.handlers[event].filter(item => item !== handler);
};
},
};
const mockData = {
valueType: ValueTypes.Diff,
value: {
conversationState: {
dialogState: {
dialogStack: {
'0': {
id: 'root',
state: {
options: {},
stepIndex: 0,
values: { instanceId: '8bc9292d-aa45-bf03-f7dc-50dea831c3fc' },
},
},
'1': {
id: 'slot-dialog',
state: {
'+slot': 'address',
'-slot': 'shoesize',
values: {
'+shoesize': 11,
age: 21,
fullname: { slot: 'last', values: { first: 'Joe', last: 'Schmo' } },
},
},
},
'2': {
'+id': 'address',
'-id': 'shoesize',
state: {
'+slot': 'street',
'-options': {
prompt: 'Please enter your shoe size.',
retryPrompt: 'You must enter a size between 0 and 16. Half sizes are acceptable.',
},
'-state': {},
values: {},
},
},
'3': { id: 'text', state: { options: { prompt: 'Please enter your street address.' }, state: {} } },
let hostCalls: any = {};
(window as any).host = new Proxy(
{
handlers: new Proxy(
{},
{
get(target, p) {
if (!(p in target)) {
target[p] = [];
}
return target[p];
},
},
eTag: '*',
}
),
on: function(event, handler) {
if (handler && Array.isArray(this.handlers[event]) && !this.handlers[event].includes(handler)) {
this.handlers[event].push(handler);
}
return () => {
this.handlers[event] = this.handlers[event].filter(item => item !== handler);
};
},
userState: {},
},
};
{
get(target, p) {
if (!(p in target)) {
target[p] = (...args) => {
(hostCalls[p] || (hostCalls[p] = [])).push(args);
};
}
return target[p];
},
}
);
let jsonViewerWrapper;
let jsonViewer;
describe('The JsonViewerExtension', () => {
let root: HTMLDivElement;
let host: InspectorHost;
beforeAll(async () => {
const Component = windowHostReceiver(JsonViewerExtension);
root = document.createElement('div');
root.id = 'root';
const themeTag = document.createElement('link');
@ -114,22 +91,58 @@ describe('The JsonViewerExtension', () => {
document.head.appendChild(themeTag);
document.body.appendChild(root);
jsonViewerWrapper = mount(<JsonViewerExtension />, { attachTo: root });
jsonViewerWrapper = mount(<Component />, { attachTo: root });
jsonViewer = jsonViewerWrapper.find(JsonViewerExtension).instance();
new WindowHostReceiver(jsonViewer);
(window as any).host.handlers.theme[0]({ themeName: 'high-contrast', themeComponents: ['dark.css'] });
host = (window as any).host as InspectorHost;
(host as any).handlers[ExtensionChannel.Theme][0]({ themeName: 'high-contrast', themeComponents: ['dark.css'] });
});
beforeEach(() => {
hostCalls = {};
});
it('should render with data and a theme', async () => {
expect(jsonViewerWrapper.find('*').length).toBeGreaterThan(0);
expect(jsonViewer.state.themeName).toBe('high-contrast');
expect(jsonViewer.props.themeName).toBe('high-contrast');
});
it('should set the color treatment for diff data appropriately', () => {
const spy = jest.spyOn(JsonViewerExtension as any, 'nodeColorVarName');
(window as any).host.handlers.inspect[0](mockData); // Simulate event through host
(host as any).handlers[ExtensionChannel.Inspect][0](mockDiff0); // Simulate event through host
expect(spy).toHaveReturnedWith('--log-panel-item-error');
expect(spy).toHaveReturnedWith('--log-panel-timestamp');
expect(spy).toHaveReturnedWith('--log-panel-item-info');
});
it('should produce a diff when the "diff" button is selected', async () => {
(host as any).handlers[ExtensionChannel.ChatLogUpdated][0]('1234', mockChatLogs);
(host as any).handlers[ExtensionChannel.AccessoryClick][0]('diff', 'default');
expect(hostCalls.setInspectorObjects[0]).toEqual(['1234', [mockDiff0]]);
});
it('should diff the next bot state when the right arrow is clicked', () => {
(host as any).handlers[ExtensionChannel.ChatLogUpdated][0]('1234', mockChatLogs);
(host as any).handlers[ExtensionChannel.AccessoryClick][0]('diff', 'default');
(host as any).handlers[ExtensionChannel.AccessoryClick][0]('rightArrow');
expect(hostCalls.setInspectorObjects[1]).toEqual(['1234', [mockDiff1]]);
});
it('should diff the previous bot state when the left arrow is clicked', () => {
(host as any).handlers[ExtensionChannel.ChatLogUpdated][0]('1234', mockChatLogs);
(host as any).handlers[ExtensionChannel.Inspect][0]({});
(host as any).handlers[ExtensionChannel.AccessoryClick][0]('diff', 'default');
(host as any).handlers[ExtensionChannel.AccessoryClick][0]('rightArrow');
(host as any).handlers[ExtensionChannel.AccessoryClick][0]('leftArrow');
expect(hostCalls.setInspectorObjects[2]).toEqual(['1234', [mockDiff0]]);
});
it('should should reset the highlighted objects', async () => {
(host as any).handlers[ExtensionChannel.ChatLogUpdated][0]('1234', mockChatLogs);
(host as any).handlers[ExtensionChannel.Inspect][0]({});
(host as any).handlers[ExtensionChannel.AccessoryClick][0]('diff', 'default');
(host as any).handlers[ExtensionChannel.HighlightedObjectsUpdated][0]([]);
const botStates = extractBotStateActivitiesFromLogEntries(mockChatLogs as any);
await new Promise(resolve => setTimeout(resolve, 1100));
expect(hostCalls.setHighlightedObjects[1]).toEqual(['1234', botStates.slice(0, 2)]);
});
});

Просмотреть файл

@ -35,11 +35,8 @@ import * as ReactDOM from 'react-dom';
import './index.scss';
import { WindowHostReceiver } from './windowHostReceiver';
import { windowHostReceiver } from './windowHostReceiver';
import { JsonViewerExtension } from './jsonViewerExtension';
function jsonViewerExtensionRef(ref) {
new WindowHostReceiver(ref);
}
ReactDOM.render(<JsonViewerExtension ref={jsonViewerExtensionRef} />, document.getElementById('root') as HTMLElement);
const Component = windowHostReceiver(JsonViewerExtension);
ReactDOM.render(<Component />, document.getElementById('root') as HTMLElement);

Просмотреть файл

@ -31,21 +31,16 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import * as React from 'react';
import { Component } from 'react';
import { CollapsibleJsonViewer } from '@bfemulator/ui-react';
import { ValueTypes } from '@bfemulator/app-shared/built/enums';
import { CollapsibleJsonViewerProps } from '@bfemulator/ui-react/src';
import { PureComponent } from 'react';
import { CollapsibleJsonViewer, CollapsibleJsonViewerProps } from '@bfemulator/ui-react';
import './index.scss';
interface JsonViewerExtensionState extends CollapsibleJsonViewerProps {
interface JsonViewerExtensionProps extends CollapsibleJsonViewerProps {
isDiff?: boolean;
}
export class JsonViewerExtension extends Component<{}, JsonViewerExtensionState> {
public state = { data: {}, themeName: 'light' } as JsonViewerExtensionState;
private diffNodesMap = new Map<HTMLElement, boolean>();
export class JsonViewerExtension extends PureComponent<JsonViewerExtensionProps, []> {
private static nodeColorVarName(paths: string[]): string {
let i = paths.length;
while (i--) {
@ -61,7 +56,7 @@ export class JsonViewerExtension extends Component<{}, JsonViewerExtensionState>
}
public render() {
const { themeName, data } = this.state;
const { themeName, data } = this.props;
return (
<div>
<CollapsibleJsonViewer
@ -70,28 +65,14 @@ export class JsonViewerExtension extends Component<{}, JsonViewerExtensionState>
valueRenderer={this.valueRenderer}
shouldExpandNode={this.shouldExpandNodeCallback}
themeName={themeName}
data={data}
data={data || {}}
/>
</div>
);
}
public setData(data: Record<string, any>): void {
this.diffNodesMap.clear();
const isDiff = data.valueType === ValueTypes.Diff;
if (data && (data.valueType === ValueTypes.BotState || isDiff)) {
this.setState({ data: data.value, isDiff });
} else {
this.setState({ data, isDiff });
}
}
public setTheme(themeName: string) {
this.setState({ themeName });
}
private labelRenderer = (path: string[]) => {
if (this.state.isDiff) {
if (this.props.isDiff) {
const color = JsonViewerExtension.nodeColorVarName(path);
return <span style={{ color: `var(${color}` }}>{path[0]}: </span>;
}
@ -99,7 +80,7 @@ export class JsonViewerExtension extends Component<{}, JsonViewerExtensionState>
};
private valueRenderer = (...path: string[]) => {
if (this.state.isDiff) {
if (this.props.isDiff) {
const color = JsonViewerExtension.nodeColorVarName(path);
return <span style={{ color: `var(${color}` }}>{path[0]}</span>;
}
@ -107,6 +88,6 @@ export class JsonViewerExtension extends Component<{}, JsonViewerExtensionState>
};
private shouldExpandNodeCallback = (keyName: string, data: any, level: number): boolean => {
return level === 0 || this.state.isDiff;
return level === 0 || this.props.isDiff;
};
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -30,13 +30,20 @@
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import { InspectorHost } from '@bfemulator/sdk-client';
import { Activity } from 'botframework-schema';
import { ExtensionChannel } from '@bfemulator/sdk-shared/build/types/ipc';
import { InspectableObjectLogItem, LogEntry, LogItem, LogItemType } from '@bfemulator/sdk-shared/build/types/log';
import { ValueTypes } from '@bfemulator/app-shared/built/enums/valueTypes';
import { diff } from 'deep-diff';
export function IpcHandler(type: string): MethodDecorator {
export function IpcHandler(type: ExtensionChannel): MethodDecorator {
return function(elementDescriptor: any) {
const { key, descriptor } = elementDescriptor;
const initializer = function() {
const host = (window as any).host as InspectorHost;
const bound = this[key].bind(this);
(window as any).host.on(type, bound);
host.on(type as any, bound);
return bound;
};
@ -52,3 +59,163 @@ export function IpcHandler(type: string): MethodDecorator {
return elementDescriptor;
};
}
export function IpcHost(mixins: (keyof InspectorHost)[]) {
return function(elementDescriptor: any) {
const { elements } = elementDescriptor;
const host = (window as any).host as InspectorHost;
mixins.forEach(mixin => {
const value = typeof host[mixin] === 'function' ? (host[mixin] as Function).bind(host) : host[mixin];
const element = elements.find(element => element.key === mixin);
const descriptor = {
key: mixin,
kind: 'method',
placement: 'own',
descriptor: {
get: () => value,
},
};
if (element) {
Object.assign(element, descriptor);
} else {
elements.push(descriptor);
}
});
return { ...elementDescriptor, elements };
};
}
export async function updateTheme(themeInfo: { themeName: string; themeComponents: string[] }): Promise<void> {
document.getElementById('root').className = themeInfo.themeName;
const oldThemeComponents = document.querySelectorAll('[data-theme-component="true"]');
const head = document.querySelector('head');
const fragment = document.createDocumentFragment();
const promises = [];
// Create the new links for each theme component
themeInfo.themeComponents.forEach(themeComponent => {
const link = document.createElement('link');
promises.push(
new Promise(resolve => {
link.addEventListener('load', resolve);
})
);
link.href = themeComponent;
link.rel = 'stylesheet';
link.setAttribute('data-theme-component', 'true');
fragment.appendChild(link);
});
head.insertBefore(fragment, head.firstElementChild);
// Wait for all the links to load their css
await Promise.all(promises);
// Remove the old links
oldThemeComponents.forEach(themeComponent => {
if (themeComponent.parentElement) {
themeComponent.parentElement.removeChild(themeComponent);
}
});
}
export function getBotState(
entries: LogEntry<InspectableObjectLogItem>[],
referenceBotState: Activity,
offset: number = -1
): Activity {
if (!referenceBotState) {
return null;
}
const allBotStates = extractBotStateActivitiesFromLogEntries(entries);
const index = allBotStates.findIndex(botState => botState.id === referenceBotState.id) + offset;
return allBotStates[index];
}
export function extractBotStateActivitiesFromLogEntries(entries: LogEntry<InspectableObjectLogItem>[]): Activity[] {
if (!entries) {
return [];
}
return entries
.reduce((agg, entry) => agg.concat(entry.items), [])
.filter((item: LogItem<InspectableObjectLogItem>) => {
const activity = item.payload.obj as Activity;
return item.type === LogItemType.InspectableObject && activity.valueType === ValueTypes.BotState;
})
.map((item: LogItem) => (item.payload as InspectableObjectLogItem).obj as Activity);
}
export function buildDiff(a: Activity, b: Activity): Activity {
const lhs = [];
const rhs = [];
const deltas = diff(b.value, a.value);
(deltas || []).forEach(diff => {
switch (diff.kind) {
case 'A':
{
const { item, path } = diff;
path.push(diff.index);
if (item.kind === 'D') {
lhs.push(path);
} else if (item.kind === 'E') {
rhs.push(path);
lhs.push(path);
} else {
rhs.push(path);
}
}
break;
case 'D':
lhs.push(diff.path);
break;
case 'E':
rhs.push(diff.path);
lhs.push(diff.path);
break;
case 'N':
rhs.push(diff.path);
break;
}
});
// Clone the bot state and update the keys to show changes
const botStateClone: Activity = JSON.parse(
JSON.stringify(a, (key: string, value: any) => {
if (value instanceof Array) {
return Object.keys(value).reduce((conversion: any, key) => {
conversion['' + key] = value[key];
return conversion;
}, {});
}
return value;
})
);
botStateClone.valueType = ValueTypes.Diff;
// values that were added
rhs.forEach(path => {
buildDiffNode('+', path, botStateClone.value, botStateClone.value);
});
// values that were removed
lhs.forEach(path => {
buildDiffNode('-', path, botStateClone.value, b.value);
});
return botStateClone;
}
export function buildDiffNode(prependWith: string, path: (string | number)[], target: any, source: any): void {
let key;
for (let i = 0; i < path.length; i++) {
key = path[i];
if (key in target && target[key] !== null && typeof target[key] === 'object') {
target = target[key];
source = source[key];
} else {
break;
}
}
const value = source[key];
delete target[key];
target[prependWith + key] = value;
}

Просмотреть файл

@ -1,87 +0,0 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license.
//
// Microsoft Bot Framework: http://botframework.com
//
// Bot Framework Emulator Github:
// https://github.com/Microsoft/BotFramwork-Emulator
//
// Copyright (c) Microsoft Corporation
// All rights reserved.
//
// MIT License:
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import { LogEntry } from '@bfemulator/sdk-shared';
import { IpcHandler } from './utils';
import { JsonViewerExtension } from './jsonViewerExtension';
export class WindowHostReceiver {
private jsonViewer: JsonViewerExtension;
constructor(jsonViewer: JsonViewerExtension) {
this.jsonViewer = jsonViewer;
}
@IpcHandler('inspect')
protected inspectHandler(data: { [prop: string]: any }): void {
this.jsonViewer.setData(data);
}
@IpcHandler('theme')
protected async themeHandler(themeInfo: { themeName: string; themeComponents: string[] }): Promise<void> {
const themeNameLower = themeInfo.themeName.toLowerCase();
this.jsonViewer.setTheme(themeNameLower);
document.getElementById('root').className = themeNameLower;
const oldThemeComponents = document.querySelectorAll('[data-theme-component="true"]');
const head = document.querySelector('head');
const fragment = document.createDocumentFragment();
const promises = [];
// Create the new links for each theme component
themeInfo.themeComponents.forEach(themeComponent => {
const link = document.createElement('link');
promises.push(
new Promise(resolve => {
link.addEventListener('load', resolve);
})
);
link.href = themeComponent;
link.rel = 'stylesheet';
link.setAttribute('data-theme-component', 'true');
fragment.appendChild(link);
});
head.insertBefore(fragment, head.firstElementChild);
// Wait for all the links to load their css
await Promise.all(promises);
// Remove the old links
oldThemeComponents.forEach(themeComponent => {
if (themeComponent.parentElement) {
themeComponent.parentElement.removeChild(themeComponent);
}
});
}
@IpcHandler('chat-log-updated')
protected async chatLogUpdatedHandler(conversationId: string, logItems: LogEntry[]): Promise<void> {}
}

Просмотреть файл

@ -0,0 +1,230 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license.
//
// Microsoft Bot Framework: http://botframework.com
//
// Bot Framework Emulator Github:
// https://github.com/Microsoft/BotFramwork-Emulator
//
// Copyright (c) Microsoft Corporation
// All rights reserved.
//
// MIT License:
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import * as React from 'react';
import { Component, ComponentClass } from 'react';
import { Activity } from 'botframework-schema';
import { ExtensionChannel } from '@bfemulator/sdk-shared/build/types/ipc';
import { ValueTypes } from '@bfemulator/app-shared/built/enums/valueTypes';
import { LogEntry, LogItem } from '@bfemulator/sdk-shared/build/types/log';
import {
buildDiff,
extractBotStateActivitiesFromLogEntries,
getBotState,
IpcHandler,
IpcHost,
updateTheme,
} from './utils';
export interface WindowHostReceiverState {
// The data that's actually displayed in the viewer
data?: Activity | LogItem;
isDiff?: boolean;
containsBotStateActivities?: boolean;
themeName?: string;
chatLogs?: { documentId: string; logItems: LogEntry[] };
// The item explicitly selected by the user
selectedItem: Activity | LogItem;
// The item we are using to diff with the selectedItem
selectedDiffItem: Activity;
}
interface JsonViewerExtensionAccessory {
json: string;
leftArrow: string;
rightArrow: string;
diff: string;
}
type AccessoryId = keyof JsonViewerExtensionAccessory;
export function windowHostReceiver(WrappedComponent: ComponentClass<any>): ComponentClass {
@IpcHost(['setAccessoryState', 'setHighlightedObjects', 'setInspectorObjects'])
class WindowHostReceiver extends Component<{}, WindowHostReceiverState> {
private setAccessoryState: (accessoryId: AccessoryId, state: string) => void;
private setHighlightedObjects: (documentId: string, items: Activity | Activity[]) => void;
private setInspectorObjects: (documentId: string, items: Activity | Activity[]) => void;
private pendingHighlightReset;
public state = { data: {} } as WindowHostReceiverState;
constructor(props) {
super(props);
this.updateButtonStates();
}
@IpcHandler(ExtensionChannel.Inspect)
protected inspectHandler(selectedItem: Activity | LogItem = {} as Activity | LogItem): void {
if (this.pendingHighlightReset) {
cancelAnimationFrame(this.pendingHighlightReset);
}
// always display the selected item
const newStateFragment = { data: selectedItem } as WindowHostReceiverState;
const { valueType } = selectedItem as Activity;
// pull the user out of diff mode if this data is not a bot state
newStateFragment.isDiff = valueType === ValueTypes.Diff;
// If this is not a diff, the selectedItem and data will be the same
// Once we enter diff mode, we want to retain the selected item
// as a reference to our cursor's index
if (valueType !== ValueTypes.Diff) {
newStateFragment.selectedItem = selectedItem || ({} as Activity);
}
this.setState(newStateFragment);
}
@IpcHandler(ExtensionChannel.Theme)
protected async themeHandler(themeInfo: { themeName: string; themeComponents: string[] }): Promise<void> {
const themeNameLower = themeInfo.themeName.toLowerCase();
this.setState({ themeName: themeNameLower });
return updateTheme(themeInfo);
}
@IpcHandler(ExtensionChannel.ChatLogUpdated)
protected async chatLogUpdatedHandler(documentId: string, logItems: LogEntry[]): Promise<void> {
const containsBotStateActivities = !!extractBotStateActivitiesFromLogEntries(logItems).length;
this.setState({ chatLogs: { documentId, logItems }, containsBotStateActivities });
}
@IpcHandler(ExtensionChannel.HighlightedObjectsUpdated)
protected highlightedObjectsUpdatedHandler(highlightedObjects: Activity[]): void {
// if we're diffing and the highlighted objects disappear,
// set them back to the items in the diff after a bit
if (this.pendingHighlightReset) {
clearTimeout(this.pendingHighlightReset);
}
this.pendingHighlightReset = setTimeout(() => {
const { isDiff, selectedItem, selectedDiffItem, chatLogs } = this.state;
if (
isDiff &&
selectedItem &&
(!highlightedObjects || !highlightedObjects.length || !Object.keys(highlightedObjects[0]).length)
) {
const shouldBeHighlighted = [selectedItem as Activity];
if (selectedDiffItem) {
shouldBeHighlighted.unshift(selectedDiffItem);
}
this.setHighlightedObjects(chatLogs.documentId, shouldBeHighlighted);
}
}, 1000);
}
@IpcHandler(ExtensionChannel.AccessoryClick)
protected accessoryClick(accessoryId: AccessoryId, currentState: string): void {
const newStateFragment = { selectedDiffItem: null } as WindowHostReceiverState;
const { selectedItem, chatLogs, isDiff } = this.state;
const { logItems, documentId } = chatLogs;
const highlightedObjects = [];
const inspectorObjects = [];
switch (accessoryId) {
case 'diff':
{
const newState = currentState === 'selected' ? 'default' : 'selected';
newStateFragment.isDiff = newState === 'selected';
const previousBotState =
getBotState(logItems, selectedItem as Activity) || extractBotStateActivitiesFromLogEntries(logItems)[0];
const nextBotState = getBotState(logItems, previousBotState, 1);
if (newStateFragment.isDiff && nextBotState && previousBotState) {
newStateFragment.selectedItem = nextBotState;
newStateFragment.selectedDiffItem = previousBotState;
inspectorObjects.push(buildDiff(nextBotState, previousBotState));
highlightedObjects.push(previousBotState, nextBotState);
} else {
inspectorObjects.push(selectedItem as Activity);
}
this.setAccessoryState('diff', newState);
}
break;
case 'leftArrow':
{
const newSelectedItem = getBotState(logItems, selectedItem as Activity);
const previousBotState = getBotState(logItems, newSelectedItem);
if (isDiff && previousBotState && newSelectedItem) {
newStateFragment.selectedItem = newSelectedItem;
newStateFragment.selectedDiffItem = previousBotState;
inspectorObjects.push(buildDiff(newSelectedItem, previousBotState));
highlightedObjects.push(previousBotState, newSelectedItem);
} else if (!isDiff) {
inspectorObjects.push(newSelectedItem || (selectedItem as Activity));
} else {
return;
}
}
break;
case 'rightArrow':
{
const newSelectedItem = getBotState(logItems, selectedItem as Activity, 1);
const previousBotState = selectedItem as Activity;
if (newSelectedItem && isDiff) {
newStateFragment.selectedItem = newSelectedItem;
newStateFragment.selectedDiffItem = previousBotState;
inspectorObjects.push(buildDiff(newSelectedItem, previousBotState));
highlightedObjects.push(newSelectedItem, previousBotState);
} else if (!isDiff) {
inspectorObjects.push(newSelectedItem || (selectedItem as Activity));
} else {
return;
}
}
break;
default:
return;
}
this.setState(newStateFragment);
this.setHighlightedObjects(documentId, highlightedObjects);
this.setInspectorObjects(documentId, inspectorObjects);
}
public render() {
const { selectedItem: _, ...props } = this.state;
this.updateButtonStates();
return <WrappedComponent {...props} />;
}
private updateButtonStates(): void {
const groupStates: Partial<AccessoryId>[] = ['leftArrow', 'rightArrow'];
const { containsBotStateActivities, isDiff } = this.state;
groupStates.forEach(accessoryId =>
this.setAccessoryState(accessoryId, containsBotStateActivities ? 'default' : 'disabled')
);
this.setAccessoryState('json', isDiff ? 'default' : 'selected');
this.setAccessoryState('diff', isDiff ? 'selected' : containsBotStateActivities ? 'default' : 'disabled');
}
}
return WindowHostReceiver;
}

Просмотреть файл

@ -33,6 +33,7 @@
import { Activity } from 'botframework-schema';
import { IBotConfiguration } from 'botframework-config/lib/schema';
import { ExtensionChannel } from '@bfemulator/sdk-shared/build/types/ipc';
export interface InspectorHost {
// The current bot (msbot schema)
@ -44,13 +45,16 @@ export interface InspectorHost {
};
// Each "on" function returns a method that when called, will unregister the handler.
on(event: 'inspect', handler: (activity: Activity) => void): () => void;
on(event: ExtensionChannel.Inspect, handler: (activity: Activity) => void): () => void;
on(event: 'bot-updated', handler: (bot: IBotConfiguration) => void): () => void;
on(event: ExtensionChannel.BotUpdated, handler: (bot: IBotConfiguration) => void): () => void;
on(event: 'accessory-click', handler: (id: string) => void): () => void;
on(event: ExtensionChannel.AccessoryClick, handler: (id: string, currentState: string) => void): () => void;
on(event: 'theme', handler: (themeInfo: { themeName: string; themeComponents: string[] }) => void): void;
on(
event: ExtensionChannel.Theme,
handler: (themeInfo: { themeName: string; themeComponents: string[] }) => void
): void;
// Enable/disable an accessory button
enableAccessory(id: string, enabled: boolean): void;
@ -63,4 +67,10 @@ export interface InspectorHost {
// Tracks a telemetry event to App Insights
trackEvent(name: string, properties?: { [key: string]: any }): void;
// Sets highlighted items within chat and the logs
setHighlightedObjects(documentId: string, items: Activity | Activity[]): void;
// Sets inspect items within the chat and logs
setInspectorObjects(documentId: string, items: Activity | Activity[]): void;
}

Просмотреть файл

@ -55,6 +55,7 @@ export interface InspectorCriteria {
export interface InspectorAccessory {
id?: string;
states?: { [id: string]: InspectorAccessoryState };
className?: string;
}
// =============================================================================

Просмотреть файл

@ -35,6 +35,7 @@ export * from './account';
export * from './activity';
export * from './attachment';
export * from './card';
export * from './ipc';
export * from './log';
export * from './payment';
export * from './response';

Просмотреть файл

@ -30,13 +30,26 @@
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
export interface HierarchicalData {
name: string;
children?: HierarchicalData[];
value?: string | boolean | number | null;
// from the extension to Emulator
export enum EmulatorChannel {
EnableAccessory = 'enable-accessory',
Log = 'logger.log',
LogError = 'logger.error',
LogLuisDeepLink = 'logger.luis-editor-deep-link',
SetAccessoryState = 'set-accessory-state',
SetHightlightedObjects = 'set-highlighted-objects',
SetInspectorObjects = 'set-inspector-objects',
SetInspectorTitle = 'set-inspector-title',
TrackEvent = 'track-event',
}
export interface BotState {
conversationState: { [prop: string]: any };
userState: { [prop: string]: any };
// From the Emulator to the extension
export enum ExtensionChannel {
AccessoryClick = 'accessory-click',
BotUpdated = 'bot-updated',
ChatLogUpdated = 'chat-log-updated',
HighlightedObjectsUpdated = 'highlighted-objects-updated',
Inspect = 'inspect',
Theme = 'theme',
ToggleDevTools = 'toggle-dev-tools',
}

Просмотреть файл

@ -30,8 +30,4 @@
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import { BotStateVisualizer } from './BotStateVisualizer';
import { WindowHostReceiver } from './WindowHostReceiver';
const botStateVisualizer = new BotStateVisualizer('#bot-state-visualizer', '#json-visualizer');
new WindowHostReceiver(botStateVisualizer);
export * from './extensionChannel';

Просмотреть файл

@ -33,7 +33,7 @@
import { LogItem } from './item';
export interface LogEntry {
export interface LogEntry<T = any> {
timestamp: number;
items: LogItem[];
items: LogItem<T>[];
}

Просмотреть файл

@ -221,6 +221,7 @@ describe('The JsonViewer', () => {
it('should expand a node when the right arrow is pressed', () => {
const targetGroup = simulatedTree.querySelector('#group2');
const groupThatShouldExpand = simulatedTree.querySelector('#group3');
groupThatShouldExpand.setAttribute('aria-expanded', 'false');
const target = targetGroup.firstElementChild;
const event = {
@ -229,8 +230,10 @@ describe('The JsonViewer', () => {
target,
};
const actuator: HTMLDivElement = targetGroup.querySelector('[role="button"]');
const spy = jest.spyOn(actuator, 'click');
(jsonViewer as any).onTreeKeydown(event as any);
expect(groupThatShouldExpand.getAttribute('aria-expanded')).toBe('true');
expect(spy).toHaveBeenCalled();
});
it('should collapse a node whe the left arrow is pressed', () => {
@ -245,8 +248,10 @@ describe('The JsonViewer', () => {
key: 'ArrowLeft',
target,
};
const actuator: HTMLDivElement = targetGroup.querySelector('[role="button"]');
const spy = jest.spyOn(actuator, 'click');
(jsonViewer as any).onTreeKeydown(event as any);
expect(groupThatShouldCollapse.getAttribute('aria-expanded')).toBe('false');
expect(spy).toHaveBeenCalled();
});
});
});

Просмотреть файл

@ -247,7 +247,6 @@ export class CollapsibleJsonViewer extends Component<CollapsibleJsonViewerProps,
if (ul.getAttribute('aria-expanded') === proposedAriaExpandedValue) {
return;
}
ul.setAttribute('aria-expanded', proposedAriaExpandedValue);
const actuator: HTMLDivElement = target.querySelector('[role="button"]');
actuator.click();
}