* 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:
Родитель
aa36bcacf6
Коммит
2247b4784e
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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',
|
||||
};
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -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',
|
||||
};
|
|
@ -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
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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"
|
||||
}
|
||||
}
|
Двоичные данные
packages/extensions/debug/client/public/favicon.ico
Двоичные данные
packages/extensions/debug/client/public/favicon.ico
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 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: {},
|
||||
};
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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();
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче