This commit is contained in:
Nick Beckley 2026-06-01 14:49:23 +01:00
parent 4c8f97d048
commit 480b557f81
58 changed files with 1516 additions and 67 deletions

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSUTW/cNhCG/4rAsyjzS/zQzbBb1HVTL+JFWvRGcUa2EC21oKhNA2P/e0DtBol7600Y6J0ZPvPOvJHoD0g6cns8/nLCmBdSkzxuIcGEpkxTxvdcdkJ3zDVWSN4y8Q+pyfiIX0lHjAqBB4dUokaqgLW0l0OgAOg448BEq0pO/7KQ7o34sQE8jQGbefmEaRnnSDry1xhh/lJq+7EJ07xCk+YJH+KSfQylmT8f7h7pH7e7/dPu8teCSxE3I5COKOTCKXSUSWeoEopRJ9BQVBDc4Kw2Hi6yMWZM0U/NAp9/1Ic5R8yhE42QDaPCkXNNwGdfWu79gvuvx9LFhui+xOstvH12b+SEiXSi/g7zku4mTONNno8TnnA6+rRgugnz4eBj6eWY5iOmPOKGZXvpNPk8zrG6FrubRoyZ1OTpudpNPg9zOrxj9XxB8PA/CJww9aQjkrVSG4NKSumd7gVDMDpoLfrgmRl6gTwga50yjhvGpRPMyVYba8AzF0hNXqa599Pvy8bPCdZbYEZogQqdUFq7wFBaHFqDDLzWvbbBgbRGKAtWeuc9dz2a4HQoBvngw+sYsXq4Jx1hHAVIi3QQXFNlwVPfSke1lnpAHpwRnNTkbo55jOu8LtVDzPiSNn6kI7/6acELudsUXseMIa+pQP1bl2K7NMMacvXDAZw1rJGMFdGaj2uuPiKMCUPGgnef1mu+/0iEFpvobk0JY652Pr9Wv/nltVDxNnDvylN1CAO2DAcGzGtmADVoBCXAgHIKtHIsaOV7rsC3vB9KhNQEi+Gqi8fBgrBqoEbalqpWC+p4MNT32DMwPPTSvONYPU1F54NlCkFShwNQ5binTklJQbbeCD0YI4afkFzNx1kBDHi6bOvWgQawCJpToXhLlXOcWtca2hvbu15bbEPp+RFTxOknUB/GkOZlHnJ19W71Dt3HNZaLU20+/jJG+u82o/s5fMZUlRn7MZYFu471XJMD+mVNeNgOVvdGdmW7qv12t4RutDW2Js/Zp7wer2FpGy21OZ/P528AAAD//wMAx6rPOvUEAAA=

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSUy27jNhSGX0XgWlR4EyVqlzod1MhkYkzcadEdRR45RGTSoKhMg8DvPqDsIM4MuuhOOOJ/Lt/5yVfk9R5Qh64Ph9+fwacJlSi5JcQIk5hITOiW8o7JjqiqbduGUfYPKpG7hRfUoUYYQ40CzEECFpbUuOeDwdaCooRawmqRc+rdhLpXpF1l4dkZqML0DeLkgkcd+st5G77n2tpVZgyzrWIYYe2npL3JzXxZr27x5+vN9n5zOjXBlMWVs6hDAihTAhQmXDVYMEGwYtBgENaoQbWy0fYkcz5B9HqsJvv0Xt+G5CGZjlWMVwQzhY4lsjrp3HKvJ9i+HHIXC6KbHC+X8PLZvaJniKhj5RvMU7orM7qr/dTPbrRXY9jtnN+tgh/cbo465cIlOsRwgJgcLHB+c17Hl89ht8v50Cc9ToBKtIW4d16Ppx83MOh5TA9hjguZyT791xnUIT2ngEq0kBzHpWxxHmY1OvDpF+1F4fuHYjPqNIS4/7CkhxP79f9Af6fNo/NQrG9QhwgFZnkLeGBUYtFajXXNFZaSywGoUQ2jqESr4JPzc5inYu0T7M7UPvR3Hc2jS2DSHPNMf8tstk0MdjapeF8wJRWpOCFZNKfDnIqvYF0EkyAPsY3zOd9PEibZIvrkRvgFz2qOEXwqNjo9Fn/o6RF1SOnWUK200loaM0BNYCCWaEkaC9JKsILZxgolrBSKGCl0T4XVNe2HHEElgmyyYvE114JzzgxWVgxY1I3B2vYNplwYRikTgvQf4Bb3Y9Zp0xIBlmMFg8VCUY2V4BxbXuuGyaFp2HDB6WwISjL1SxP+OYE9e+nLydpvs1t4Pt3jpU9pbQtWUswErbFQiuJW1Q3um7ZXvWyhNnmyVfBTeAP5DWIfJpfyI3LnvNvr8ecj75u5hehhvNjOnTMxTGFIxdmWxYd9fZ19fsWKxaLfncf/Lsa4CeYJYpGNpZ2/WObxeDz+AAAA//8DACYP7R0MBQAA

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACoxUTW/bOBD9KwLPpkJSFCUK2IPhtFgjTWMk7u5ibxQ5jInIokFRToMi/31BWa6dooe9CaP5ePPeG/5AvdoDatDycPh0hD4OaIGim0KMMIGJwIRuadEw0VCSs4oVhIt/0QK5O3hDDaq41lRLwAUIwNyQEreF1dgYkJRQQ1jJU0/1PKDmB1IuN3B0GnI//AVhcL5HDfrb9ca/ptnK5brzo8mD72DdD1H1OoH5ul7d4S/LzfZhc8oaYEjFuTOoQRwokxwkJoWsMGecYMmgwsCNllbWolLmVOb6CKFXXT6Yl8t842MPUTcsZ0VOMJPofYGMiipBbtUA27dDQjFRdJviiyk8fTY/0BECatjiTOap3Y3u3M1+aEfXmZuowjNEG9QeXn14gaPq0AIdgj9AiA4mbh6esgskSnKSM8EIQQt0r/TO9ZCtb7OHLi2sdE04mAJLsAZzSRWWvCiwKUpVMWGrilm0QHcQeuiuut47HfzgbcxmyrMPc1a+j64f/Thk6z7Cc1DxVPdZdQOgBdqMbeeG3SMo87b1j2P6149d2mU1hgB9zDYq7rI/1bBDDZKq1lRJJZUSWlsoCVhiiBKkMiCMAMOZqQyX3AguiRZctZQbVdLWpghaoIcxHsZ45v/7FYhtcPs9mAuC7cTxplPR+rBfG+ijsy5Jc86YDNV101bZ3HPVOejjB5JRgwgFZooasGVUYF4bhVVZSCxEISxQLStGT4RZ9zz+5IlYa3hhy7oWrWwpAWarmpPKtsRoW2hpyhYqVpGqhlpqQtqS07qVmhigJU8m3QRvRh1/tUIxCfQ49uk6s3Va+9X1+LvgF0Y2wUevfZcEcx08vQ0R9r9TbeX3Bz+4mPa3s7TfBrj3ffo73//M2Vy89PESg3QH2XR6FZjC8gqwaVmBOSdwOj3DWjC1rkXNkoy3Xr9AyJLBlOsnTc6eenjKlkHvXAQdx5Am/zMtdZLz8/lkLnzkXz9tVz7A8nBYzNE/jomkU7OzAT68K9+GlL7zQ7xaeV7tCTp7BnZlJwPH01PlUlAYU4MRFDNOS8ylpLiWZYXbqm5lK2oo9cWu2SMYF0DHqd82jGnazOzvfLkM0Vml45CO54vXk51mf84pv4K8ku1n9Wn4lXSzleZOlCTLPp0ezslB/+vdfH9/f/8PAAD//wMAGp/mGygGAAA=

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRTTW/cNhD9K8KcRZmkKErUzVin6MJNvEgWadEbxRnFhGVqQVFOAmP/e0DtBol764UgBnwz72P4CsE+E/Rwezq9e6GQFigh+a0kudSMa8bFUdS91L1oKt3JRhv+L5Tg7+k79NAq54QzxGrSxBTyhg316BgiGcEFctmo3NN+WaB/BesrpBfvqJqXzxQXPwfo4W8fcP6aZ1tfuWlesYrzRPuwJBtcJvNhv7tnf90ejg+Hy6uFlgyuPEIPioQ0igzjtWmZkoozI6llpNCZ0XS6tXiB+ZAoBjtVCz79mo9zCpRcLytZV5xJA+cS0CabKQ92oeP3U2axWXSX6+VW3q79K7xQhF6WP828tLtxk795XobVT3hzOZNdnrLKU5xPFJOnzZNN5jTZ5OdQXCftJk8hQQkPn4rDZNM4x+c3Rn266N//D/nHPP3dN3JrItzNa0jQg6gNlPDeukcfqNjfQQ9ckMS6IzZKoZnq0DLb1IZpXeuRhDOtFFDCbg7Jh3Vel2IfEn2JmwDo4Q87LXShfhvdo0/k0hqzqn903oVDnHF1qfjlv+AVr2rOM2hNpzUVHwl9JJco6zvG9drvPxCp5QbarTFSSMXBpsfiT7s8Qg/Gdk5YY4212rmRGk4jR241b5E0akIlsUVlFGpluNPKDkKhbcQw5gqUQDnuYtuwUdVcSeKMj9IwNVrJhq6VbJCtEELrpnVvfSwepoyzruOKsGaGRmTKCMuMqmuGdWNbqce2leNvllzTFzwbjPRy+SsbA43YEWrBpBINU8YI1pmmZUPbDWbQHTUbg3uKgabfjHrvXZyXeUzFdXmKN9Z9XEP+78W2SF99YN+2jO5m90SxyBlbH/J6X2M9n8/nHwAAAP//AwDZhTs/NgQAAA==

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSUTW/cNhCG/4owZ1HmlyhRN2OdoAs39SJZpEVvFDmKCcvUgqScGMb+94LaDVKfil4IYsR3+M4zQ71BMM8IA9yeTh9eMOQENWS/hTjlilBFKDsyMXA1sLZRPZW6a/+GGvw9vsIAnbSWWY1EoEIiHW3JKCZLnEPNKHOUt7LkNN8SDG9gfOPwxVtslvQVY/JLgAH+9MEt38vdxjd2XlbXxGXGfUjZBFvM/LHf3ZPfbw/Hh8PlVMJUxI13MIBExrVETajQHZFcUqI5dgSls3rSveqMu8h8yBiDmZvknn7d75YcMNuBN1w0lHAN5xqcyaZYHk3C4+upuNgQ3ZV4vYW37fAGLxhh4PVPmJd0N3b2N89pXP3sbi5rNukpbetkbF7iK9RwissJY/a48blNCZ/H+fVo0tPHy5myTR9+oF0zut2yhgwDMKGghg3QPJvsl1BdPe5mjyFvH3P0IXn7X7kE1PDwpTrMJk9LfH7Xjy8XzPv/QfmTsY8+YLW/gwEoQ+5Ej2TiTBHZO0NMKzRRSqgJmdUdZ1DDbgnZh3VZU7UPGb/FrSQY4KOZE1783Ub76DPavMZS51+qzNUhLm61ufrVS0Yb2ghKi2jNpzVXn9H5iDZjKeIY12u+r+8lXPFNtFtjxJCrg8mP1W8mPcIA2vSWGW20McraCVuKE3XUKNo5VE6hk9x1TmrplNTUKmlGJp1p2TiVCNSAZXSqbVrbzhReLUE6CiKtEkQLIwgy17GxxZGa6R3H6mEuOmN7KtEJonFyRGpmiJZCECda03E1dR0vup9IrvPAaAHs8OXy7jYHyrkenWKES9YSqTUjvW47Mnb9qEfVY2uL53uMAed/gfrkbVzSMuXqOiHVO3Sf11D+HdU2Ld99ID+2Ht0t9gljVXpsfChP5drW8/l8/gcAAP//AwAKlhXBggQAAA==

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRTwY7cKBD9FYuz8QDG2Pg26tlVWpPstJJRstobpoo0igdaGE8SjfrfV7g7Sia33NCDV/XqveKFBPOEZCS3p9NfzxjyQmqS/QYJJhRlijL+yNtRqJF3jRpk1w/6P1ITf4/fyUh6aS23GmmLCqkE1tGpdZYCoOaMAxOdLDXN54WML8T4BvDZW2zi8hHT4mMgI/nkA8SvpbfxjZ3jCk2KM+7Dkk2wRcw/+909fXt7eHw4XF4tuBRy44GMRCIXWqKmrNU9lUIyqgX2FCVY7fSgegMXmg8ZUzBzs8CXn/0h5oDZjqIRbcOo0ORcEzDZFMmTWfDx+6mo2Cy6K3i9wdtxfCHPmMgo6h9mXsrd2Nnf2Pj0ZALcOB/8ciQ1OaV4wpQ9bnZsE86zyT6G6tpkN3sMmdTk4UN1mE12MT298ujDZfT9H0z+ztijD1jt78hIGEcB7YDUCa6oHMBQ07WaKtUqh9zqXnBSk10M2Yc1rku1Dxk/p00lGcnfZl7wou822aPPaPOaivR/Vckav/m8i1AARmpySBFWm6ufdnPWsKZl5fJhzac1V+8RfEKbscz0mNZr+d8oQomNtFtTwpCrg8nH6o1ZjmQk2gyWG220Mcpahx1Dx4AZxXpABQpBCuhBaglKamaVNBOXYDo+uYIU3SXdaluorp/aaeiQOhwclWZAOijXUauB9cw4kCBe2Vo9zIVn7MAkQks1OqBSc0O1bFsKbWd6oVzfC/eLJdfEOSt+Az5fvsamQAEMCIpTIXlHpdacDrrr6dQPk57UgJ0tmu8xBZx/Meqdtyku0eXqujDVK+ver6F872pbnq8+0G9bZHfRfsFUlciND2Wbrymfz+fz/wAAAP//AwBHtmfAJQQAAA==

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSUTW/cNhCG/4rAs6jwS/zQzbBb1HBTL2KjLXobcUaxEK20oKhNA8P/vaB2i8S99SIIA87wnWdeziub4UisYzen009nmvPKapbHPaSEslxYLuSz1J2ynWkbb5TxPvzFajY+0DfWMWdilDEQ12SJGxQt7/UQOSIFKSQK1ZpSEz6vrHtlMDZI5zFSs6y/U1rHZWYd+2Occfla7oaxidOyYZOWie7nNcMci5jf7m8f+K83h+fHw+XUSmtJbkZkHQvBOYyt5KF8jO4VD70HrlRPZHsTpBWXtHHOlGaYmhW/fL8flzxTjp1qlG4EV4G91QwhQ5Hcw0rP305FxY7orsTrPbz/dq/sTIl1qv4X5qXchziNH9atn+hM0wnSSulDXI5HmJHV7JSWE6U80o7lTKlf1jEXojE68F5K9OBM69DrVmmHWhB64VoMGMAaO6jYtgP2PjgzGFTSoCVjesdqtoObJsjjMldX7bfTSHNmNXt8qg4T5GFJx3fony5E7/8H0CKbdUyLVlvnyGitIdheCUJno7WqjyDc0CuSkUQbjAvSCamDEkG31nmHIEJkNfsI8WWcqbq/Yx0TkhRqT3xQ0nLjETi0OnBrtR1IxuCUZDW7XeY8ztuyrdX9nOlz2vtlHfsZppUund6k+DJminlLBcKftpjxkBbcYq6+G0CKRjRalJ4et3zacvWJcEwUMxUcz2m71vtPirJqT7rdUqI5VwfIL9UvsL4UhuCjhAABwMY4UCtoECjACodk0RIahQ5NMGhNENEa6KVBaGU/lAirGRW/VbvFXYhRhmHgwaHiZgjEYegVp9a46ACs8fSOY/U4lTyIXhhCzQMNyE2QwIPRmqNuwSk7OKeGH5BczSJFAYx0vjzWXYFF9IRWcmVky00IkvvQOt4734feempj0fxAaabpB1Afx5iWdRlydfVa9Q7dp20uC6faffd1nPnf+4zulviFUlVmDONc3td1rG81OxKsW6Ljvq+6V3Yoj6t63teW8o1U1tbsKUPK2+ka1m3jvHNvb29v/wAAAP//AwB1G8B29AQAAA==

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSUS2/cNhDHv4rAsyjz/dDNsFvUdVMv4kVa9MbHyBaipRYUtWlg7HcPqN0gcW+9EMSA8+Bv/jNvKLkDoB7dHo+/nCCVBbWojJuJEaYwUZjQPeU9U72QnSGCU2n+QS0aH+Er6pEWIdBgAXNQgEUkEns+BBwjWEpoJEyKGtO9LKh/Q27sIpzGAN28fIK8jHNCPfprTHH+UnO7sQvTvMYuzxM8pKW4FGoxfz7cPeI/bnf7p93l1QJLde7GiHpkrdYxSIptPQT3DFtvHGbMAygvLFXk4jamAjm5qVvi5x/541wSlNCzjvGOYGbRuUXRFVdL9m6B/ddjrWJDdF/t7Wberv0bOkFGPWu/w7yEuwnTeFPm4wQnmI4uL5Bvwnw4uBRRi455PkIuI2xYtp9OkyvjnJprsrtphFRQi56em93kyjDnwztWzxcED/+DwAmyRz3iRHKlNQjOubPKMwJRq6AU88ERPXgGNACRVmhLNaHcMmK5VNro6IgNqEUv0+zd9Puy8bOMeBOJZoqBAMuEUjYQ4AYGqYFEp5RXJtjIjWbCRMOddY5aDzpYFapAPrjwOiZoHu5RjwgFFrkBPDCqsDDRYSe5xUpxNQANVjOKWnQ3pzKmdV6X5iEVeMkbP9SjX920wIXcbQ6vY4FQ1lyh/q1qsl2e4xpK80MBlHSk46QyelrLcS3NR4hjhlCg4t3n9RrvPy5Msc3pbs0ZUml2rrw2v7nltVJxJlBn61dVCANIAgOJxCmiI6ioIAoWdRRWRCUsCUo4T0V0kvqhWlCLoAqu2TRuiDSKS4epBo+FdB4bIQR2SkrFSIyR2nccm6ep+rlgiIDIsYUhYmGpw1ZwjiOXTjM1aM2Gn5BcxUdJBRzhdJnWrQIVo4GoKGaCSiyspdhYqbHXxluvDMhQK3iEnGD6CdSHMeR5mYfSXLXbvEP3cU114zSbjr+MCf+79eh+Dp8hN7XHbkx1wK5tPbfoAG5ZMxy2hdW/oV2drma/7S1mOsqUatFzcbmsx6uZy04brc/n8/kbAAAA//8DAMMGw8r1BAAA

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSUXW/jKBSG/4rFtXEBY2x81013tFGn02ianV3tHYbjFNWBCHBnqyr/fYSTqumM9mJvLOuY8/W8r3lFTu0B9ej6cPj9GVyKqETJLiFGmMBEYEK3tO6Z6HlTdS2lTUf/QSWyt/CCetRyramWgGsQgLkhDR7qUWNjQFJCDWENzzXVLqL+FSlbGXi2Giofv0GI1jvUo7+sM/577q1spSc/myr4CdYuJuV0HubLenWLP19vtveb06kIMSdX1qAeSdm2RjcUy/zg9cCwHDqFGRsAxMAlFeSUZl2C4NRURfP03t/45CDpnlWsrghmEh1LZFRSeeRBRdi+HPIUC6KbHC+X8PLav6JnCKhn5RvMU7krPdmrfRxmO5mrye921u1W3o12NweVcuMSHYI/QEgWFji/WafCy2e/2+V66JOaIqASbSHsrVPT6cMNjGqe0oOfw0Immqf/OoN6pObkUYkWktO0tC3Oy6wmCy79knvR+P6h2EwqjT7sP4j0cGK//h/o75R+tA6K9Q3qEaHATN0BHhkVmHdGYdXUEgtRixGoli2jqEQr75J1s59jsXYJdmdqH+a7DvrRJtBpDnmnv0U22yZ4M+tUvAtMSUWqmuRJ7ud0mFPxFYwNoBPkJbZhPtf7KYUJtiR9shP8gmc1hwAuFRuVHos/VHzMOFSnqZJKKiW0HqEhMBJDlCCtAWEEGM5Ma7jkRnBJtOBqoNyohg5jjqASQTZZsfgaajaKWkmsx85gLroGSzXUWFLWSC1ZI4bxA9zifsp5SneEg6mxhNFgLqnCktc1NnWjWibGtmXjBaezISjJ1C9N+GcEc/bSl5O133Y38Hz6j5c5hTEdGEEx47TBXEqKO9m0eGi7QQ6ig0bnzVbeRf8G8huEwUeb8iVyZ53dq+nnI+/K3EJwMF2oc2d18NGPqTjbsvig19fZ5VusWCz63Tr872KMG6+fIBTZWMq6CzGPx+PxBwAAAP//AwA/OrCDDAUAAA==

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACoxUwW7bOBD9FYFnUyEpihIF7MFwWqyRpjESd3exN4ocxkRk0aAop0GRf19Qlmun6GEvgjDkzLx57w1/oF7tATVoeTh8OkIfB7RA0U0hRpjARGBCt7RomGi4yFnJK0H4v2iB3B28oQZVXGuqJeACBGBuSInbwmpsDEhKqCGs5Kmmeh5Q8wMplxs4Og25H/6CMDjfowb97XrjX1Nv5XLd+dHkwXew7oeoep3AfF2v7vCX5Wb7sDndGmBIybkzqEFSVpXRJcUyfXjRMizbWmHGWgDRckkFOaW5PkLoVZcP5uXS3/jYQ9QNy1mRE8wkel8go6JKkFs1wPbtkFBMFN2m+GIKT7/ND3SEgBq2OJN5KnejO3ezH9rRdeYmqvAM0Qa1h1cfXuCoOrRAh+APEKKDiZuHp+wCiZKc5EwwkoDfK71zPWTr2+yhSwMrXRMOpsASrMFcUoUlLwpsilJVTNiqYhYt0B2EHrqrqvdOBz94G7OZ8uxDn5Xvo+tHPw7Zuo/wHFQ85X1W3QBogTZj27lh9wjKvG3945jO+rFLs6zGEKCP2UbFXfanGnZJF1VrqqSSSgmtLZQELDFECVIZEEaA4cxUhktuBJdEC65ayo0qaWtTBC3QwxgPYzzz//0KxDa4/R7MBcF24njTqWh92K8N9NFZl6Q535gM1XXTVNlcc9U56OMHklGDCAVmihqwZVRgXhuFVVlILEQhLFAtK0ZPhFn3PP7kiVhreGHLuhatbCkBZquak8q2xGhbaGnKFipWkaqGWmpC2pLTupWaGKAlN2m64M2o469WKCaBHsc+bWe2TmO/uh5/F/zCyCb46LXvkmCug6e3IcL+d6qt/P7gBxfT/HaW9tsA975Pp/P+z5zNyUsfLzFIe5BNq2dKSgjhGhNVl5iXosWKVwWmINqqJEChSDLeev0CIUsGU66fNDl76uEpWwa9cxF0HEPq/M801EnOz+eVufCRf/20XfkAy8NhMUf/OCaSTsXOBvjwrnwb0vWdH+LVyPNoT9DZM7ArOxk4np4ql4LCmBqMoJhxWmIuJcW1LCvcVnUrW1FDqS92zR7BuAA6TvW2YUzdZmZ/58tliM4qHYe0PF+8nuw0+3O+8ivIK9l+Zp+aX0k3W2muREmy7NPp4Zwc9L/ezff39/f/AAAA//8DAHaYtSMoBgAA

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSTQW/cNhCF/4rAsyiTFEWJuhnrFF24iRfJIi16G3FGMWGZWlCUk8DY/x5Qu4GT3nohhIGG8943j68swDOxnt2eTu9eKKSFlSz5raSEMlwYLuRR1r0yfSOrphG6M82/rGT+nr6znrXaOeks8ZoMcY2i4UM9Oo5IVgqJQjU63wlfFta/MvAV0ot3VM3LZ4qLnwPr2d8+4Pw1zwZfuWlesYrzRPuwJAgui/mw393zv24Px4fD5a+FltxceWQ9s7Zt0TWS23zoelDcDh1wpQYiM2grjbi0+ZAoBpiqBZ/e5uOcAiXXq0rVleDKsnPJEBJkyQMsdPx+yio2RHe5Xm7l7bN/ZS8UWa/KnzAv1924yd88L8PqJ7y5nAmWp+zyFOcTxeRpY7LZnCZIfg7FddJu8hQSK9nDp+IwQRrn+PwbqE8X//v/Yf+Yp7/7Rm5NhLt5DYn1TNaWlew9uEcfqNjfsZ4JSQrrjviopOG6Q+DQ1JYbU5uRpLOtkqxkuzkkH9Z5XYp9SPQlbgZYz/6AaaGL9NvoHn0il9aYXf1jchYOccbVpeKNvxSVqGqRRT6s6bSm4iOhj+QSZX/HuF7v+0+LMmpr2q0xUkjFAdJj8ScsjxkKdE6CBQtgnBupETQKFGBEi2TQEGqFLWqr0WgrnNEwSI3QyGHMFVYyyusutoQ16JpukC0ni8C1GiQHJztOVA/KEIxa5FC8cSweptwHrhOasOaWRuTaSuBW1zXHuoFWmbFt1fgLkuv2pciAkV4ub2VTYBA7QiO50rLh2lrJO9u0fGi7wQ6mo8ZlzfcUA02/gHrvXZyXeUzFNTzFb+g+riG/92IL0lcf+LdtR3eze6JY5B2DDzne17Wez+fzDwAAAP//AwBiE6abNgQAAA==

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRU0W7cKhD9FYtn4wDG2Pgt2rS6q9w2q3bVVn0bw7hBcfAKcNoo2n+vsLdK83R1X0Zo4Axnzhx4IR4ekfTk+nR694Q+RVKS5NaUYEJRpijjR173QvUNrxrZMaXb76Qk7hafSU9aaQw3GmmNCqm0rKFDPRpqLWrOuGWikbkm/IikfyHgKotPzmA1xy8Yops96clX5+38M98NrjLTvNgqzBPufUzgTSbzcb+7pf9eH453h+1UxJjBlbOkJ1q3rTUNpzoHWQ+C6qEDKsSAqAapuWIbzPmEwcNURfvwer+dk8dkelGJumJUaHIuiYUEmfIAEY/Pp8xilegm58s1vS77F/KEgfSi/CPmVu7KTO7qMQ6Lm+zVFhPEh7jGEUyawzMpySnMJwzJ4arPdYz4OEzPR4gP77czeRnf/UKzJLS7efGJ9ITXipRkFWiaILnZFxeOu8mhT+tmCs5HZ/6rVk1Kcve5OEyQxjk8vpnH503m/f9Q+QOYe+ex2N+QnjCOwtYd0lFwRWVngUJTa6pUrUbkRreCk5LsZp+cX+YlFnuf8EdYWyI9eQ9TxI3fdTD3LqFJS8h9flPZV4cw28Wk4nWWnFWsqllmcrek05KKT2hdQJMwN3EMy6Xel7cQocQK2i0hoE/FAdJ98Q/E+9w5dIaDBg2gjBmxYTgyy0Cx1qKyCq0UtrVSS6ukZkZJGLi00PBhzBlSEszWKTa3Am90yxQFZWoqrVYUYJQUAczQqrZudUa86ljcTRkHpmMSbU01jpZKzYFqWdfU1g20Qo1tK8a/JLn4gbMssMWn7d2tDJS1HVrFqZC8oVJrTjvdtHRou0EPqsPGZAa3GDxOfwn1wZkwx3lMxcUhxRvpPi0+/x3F6pafztNf64xuZvOAocgzBufzU7mM9Xw+n38DAAD//wMAXVqRGIIEAAA=

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSTT2/cIBDFv4rFeXEAY2x8izatukrTrJqorXrDzDiL4sAK4/xRtN+9wrtVkt56sayxH/Pm94ZX4s0Dko6c7/efHtGniaxIcktJMKEoU5TxW151QnU1L+tatLzRv8mKuEt8IR1ppLXcaqQVKqQSWE37arAUADVnHJioZT7T3E2keyXGlYCPzmIZph8YJxc86chP5yE85d7GlXYMM5QxjLjxUzLeZjPfNutL+vV8e3u9Pf414ZTFpQPSEa2bBmzNqc4PWfWC6r41VIgeUfVSc8WOMucTRm/GcoL7t/4QksdkO1GKqmRUaHJYETDJZMu9mfD2ZZ9dLIgucn21lJfX7pU8YiSdWP2FeTzuzI7uzIaHB+PhbHDeTTuyIvsY9hiTwwXHMuE4muSCL05N1qNDn8iKXN8U29GkIcSHD4xujqNv/mPyK2N3zmOxuSAdYRwFVC3SQXBFZQuGmrrSVKlKDcitbgQnK7IOPjk/h3kqNj7hXVxcko58NuOER3/n0e5cQpvmmK3/UjlrfHZpHSAXcu9tDDDbVLzh5qxkZcXyx+s57edUfEdwEW3CPNNtnE/H/yMRSiyi9Rwj+lRsTdoVX8y0yyBMa7nRRhujrB2wZjgwYEaxBlCBQpACGpBagpKaWSVNzyWYmvdDrmTfOd1iWSg0rGGqQdrKvNUWDO11P1DAQbRaDapts+INa3E9Zp2xLZMIFdU4AJWaG6plVVGoatMINTSNGN4hOSXOWeYN+Hi8GosDBdAiKE6F5DWVWnPa6rqhfdP2ulct1jY7uMTocXwH6srZGKYwpOK0MMUHdN9nn693sSzPk/P0eYnsIth7jEWO3Dift/mU8uFwOPwBAAD//wMAq0uqsyUEAAA=

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSUTW/cNhCG/4rAsyjzS/zQzbBb1HDTLGKjLXobcUaxEK20oKhNA2P/e0F5i8S99UYMOMOXz7wzr2yGI7GO3Z5OP51pziurWR73kBLKcmG5kM9Sd0Z22jVBt0p5+xer2fhI31jHnIlRxkBckyVuULS810PkiBSkkChUa0pN+Lyy7pXB2CCdx0jNsv5OaR2XmXXsj3HG5Wt5G8YmTsuGTVomepjXDHMsYn57uHvkv94enj8e3m6ttJbkZkTWMe+dbTFaboIibgwoDrEduJM6RDMIGkIRAWMzzpnSDFOz4pfv7+OSZ8qxU43SjeAqsEvNEDIUyT2s9PztVFTsiO5LvN7D+7F7ZWdKrFP1vzDfyt3EabxZt36iM00nSCulm7gcjzAjq9kpLSdKeaQdy5lSv6xjLkRjdOC9lOjBmdah163SDrUg9MK1GDCANXZQsW0H7H1wZjCopEFLxvSO1WwHN02Qx2WurtrvppHmzGr28ak6TJCHJR3foX96I/rwP4AW2axjWrTaOkdGaw3B9koQOhutVX0E4YZekYwk2mBckE5IHZQIurXOOwQRIqvZB4gv40zVwz3rmJCkUHvig5KWG4/AodWBW6vtQDIGpySr2d0y53Helm2tHuZMn9P+X9axn2Fa6e2ntym+jJli3lKB8Kctsg9pwS3m6rsBpGhEo4UoSVs+bbn6RDgmipkKjue0Xev9J0VZtSfdbSnRnKsD5JfqF1hfWMcC+CghQACwMQ7UChoECrDCIVm0hEahQxMMWhNEtAZ6aRBa2Q8lwmpGxW/VbnHprAERFMdge24iIPeD1Vy23htpBGjh33GsPk4lD6IXhlDzQANyEyTwYLTmqFtwyg7OqeEHJFezSFEAI53fhnVXYBE9oZVcGdlyE4LkPrSO9873obee2lg0P1KaafoB1IcxpmVdhlxdvVa9Q/dpm8vCqXbffR1n/vfeo/slfqFUlR7DOJf5urb1UrMjwbolOu77qntlhzJc1fO+tpRrrBehZk8ZUt5O17AOjQxBXy6Xyz8AAAD//wMA9VbUEPQEAAA=

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACoxUzW7bOBB+FYFnUSElihIF7MFwWtRI0xiJu7vojSKHMRFZNCjKaRD43ReU5dopetibMCJnvr/hO+rlDlCDFvv9pwP0YUApCnYq5STnmHBM6IYWDaNNUWdFWRLCqx8oRfYO3lCDKqYUVQJwARww06TEbWEU1hoEJVSTvGSxp3weUPOOpM00HKyCzA1/gx+s61GD/rG9dq9xtrSZ6tyoM+86WPVDkL2KYL6tlnf462K9eVifTg0wxMuZ1ahBdV3xUiuOmcgBMyZzLFVpcEULoZghYEQEIW1m+wC+l1026JfLfO1CD0E1eZYXGcG5QMcUaRlkhNzKATZv+4hikug21tOpPH027+gAHjV5ehbz1O5GdfZmN7Sj7fRNkP4ZgvFyB6/Ov8BBdihFe+/24IOFSZuHp+QCiZKMZDnPCUEpupdqa3tIVrfJQxcJS1UTBrrAAozGTFCJBSsKrItSVjk3VZUblKI78D10V13vrfJucCYks+TJhzlL1wfbj24cklUf4NnLcLr3WXYDoBStx7azw/YRpH7buMcx/uvHLnJZjt5DH5K1DNvkixy2qEFC1opKIYWUXCkDJQFDNJGcVBq45qBZrivNBNOcCaI4ky1lWpa0NbGCUvQwhv0Yzvr/vAKx8Xa3A31BsJk0XncyGOd3Kw19sMZGa84npkB13cQqmXsuOwt9+CAyahChkOuiBmxyyjGrtcSyLATmvOAGqBJVTk+CGfs8/tKJGKNZYcq65q1oKYHcVDUjlWmJVqZQQpctVHlFqhpqoQhpS0brViiigZZMR3be6VGF36NQTAY9jn3czmQVab/aHv/k7KLI2rvglOuiYbaDp7chwO5Pri3dbu8GGyJ/M1v7fYB718e/8/7Pms2XFy5cahD3IJlWj1U0B8E5JlLVmGlJcVtrgxUtSpkXpKW6RCm6deoFfBIDJm0/eXLO1MNTsvBqawOoMPo4+d+J1MnOz+eVueiRffu0WToPi/0+nat/HaJIp2bnAHx4V74P8fjWDeGK8kztCTpzBnYVJw2H01NlY5FrXYPmFOeMlpgJQXEtygq3Vd2KltdQqktck0fQ1oMKU7+NH+O0Wdk/5XLhgzVShSEuz1enpjjN+ZyP/A7yyrZft0/Dr6ybozR3oiRG9un0cE4J+l/v5vF4PP4HAAD//wMABRuhhygGAAA=

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSTwY7cKBCGX8XibDyAARvfRj27SmuSnVYySlZ7w1Q5jeKBFsaTRKN+9xXujpLJLTf0Q1X99VXxQoJ9QjKQ29Ppr2cMeSE1yX6TBBOaMk0Zf+TtIPkgZSM4U8zw/0hN/D1+JwPppHPcGaQtaqQSmKJjOzkKgIYzDkwoWXLazwsZXoj1DeCzd9jE5SOmxcdABvLJB4hfS23rGzfHFZoUZ9yHJdvgipl/9rt7+vb28PhwuLxacCnBjQcykL7vtAKnqTQCqZRWUOvURDveGicnhpMpJqxvfMiYgp2bBb78rA8xB8xuEI1oG0aFIeeagM22WB7tgo/fT8XFhuiu6PUmb8fhhTxjIoOof8C8pLtxs79x8enJBriZfPDLkdTklOIJU/a44dg6nGebfQzVtchu9hgyqcnDh+ow2zzF9PSK0YdL6/s/6PyddUcfsNrfkYEwjgLaHukkuKayB0utag3VutUTcmc6wUlNdjFkH9a4LtU+ZPycNpdkIH/becGLv9vkjj6jy2sq1v/VpRh+83kXoQiM1OSQIqwuVz9xc9awpmXl8mHNpzVX7xF8Qpex9PSY1mv630KEFlvQbk0JQ64ONh+rN3Y5koEY2ztujTXWaucmVAwnBsxq1gFq0AhSQAfSSNDSMKelHbkEq/g4FaX4LtOttoVCkK1QI9IRlaKyd46OvFO04+U971Q7ja+wVg9zibOuZxKhpQYnoNJwS41sWwqtsp3QU9eJ6Rck14lzVngDPl++xuZAA/QImlMhuaLSGE57ozo6dv1oRt2jcsXzPaaA8y+g3nmX4hKnXF0XpnqF7v0ayveutuX56gP9to3sLrovmKoycutD2ebrlM/n8/l/AAAA//8DAO33rqElBAAA

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSUUW/cKBDHv4rFs3EAY2z8Fm1a3SrXy6pd9U73NoZxg+LACnDaKNrvfsK7pzZPp3tBaGBm/vObgTfi4RnJSG5Ppw8v6HMiNcluMwkmFGWKMn7k7Sj5KGUjWNdxrf8mNXH3+EpG0ktjuNFIW1RIpWUdndrZUGtRc8YtE50sMeFbIuMbAddYfHEGm5C+YkwueDKSP5234XvJDa4xS1htE8OCe58yeFPE/LHf3dPfbw/Hh8PlVsJUnBtnyUiGoVedNYpKLZBKCYKC6Wba81YbOTOcdREBrnE+Y/SwNMk+/cxvQ/aYzSga0TaMCk3ONbGQoUieIOHx9VRUbIjuir3ezNt2fCMvGMko6n9hXsLdmMXdPKdpdYu9uawZ0lPa1hlMDvGV1OQUwwljdrjxuU0Jn6fl9Qjp6ePlTtmmDz/QrBntLqw+k5HwVpGabICWBbILvrpq3C0Ofd4Oc3Q+OfNfsVpSk4cv1WGBPIf4/K4fXy6Y9/+D8icwj85jtb8jI2EchW0HpLPgisrBAoWu1VSpVs3Ije4FJzXZBZ+dX8Oaqr3P+C1uJZGRfIQl4UXfbTSPLqPJayx1/qVKskMMdjW5+tlLzhrWtIwVpzWf1lx9RusimoyliGNcr/G+vncRSmxOuzVG9Lk6QH6sfoP0SEaiYTAcNGgAZcyMHcOZWQaK9RaVVWilsL2VWlolNTNKwsSlhY5Pc7GQmmAZnWqbVrRopIGOzgonKqeuoxMOQGHgyGduWC/ec6weluIHZmASbUs1zpZKzYFq2bbUth30Qs19L+ZfkFzngbMC2OLL5d1tCpS1A1rFqZC8o1JrTgfd9XTqh0lPasDOFM33GD0uv4D65EwMKcy5uk5I9Q7d59WXv6PapuW78/TH1qO7YJ4wVqXH4Hx5Kte2ns/n8z8AAAD//wMAE1/rSoIEAAA=

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRTS2/cNhD+KwLPosyXKFE3Y52iCzfxIlmkRW8jchgTlqkFRTkJjP3vAbUbJO6tF4IY8Jv5HsNXEuEZyUBuT6d3LxjzQmqSw1YSTGjKNGX8yOWg+KBUI1jPW8X/JTUJ9/idDKRT1nJrkErUSJVjLR2lt9Q5NJxxx0SrSk/4spDhlUBoHL4Ei828fMa0hDmSgfwdopu/ltkQGjvNq2vSPOE+LhmiLWQ+7Hf39K/bw/HhcHm14FLATXBkIH3f6dZZTZURSJUCQcG2nnZcGqs8Q28KCQhNiBlThKlZ3NOv+W7OEbMdRCNkw6gw5FwTBxkK5REWPH4/FRabRXelXm/l7Tq8khdMZBD1TzMv7W7sFG6el3ENk7u5nBmWp6LylOYTphxw82STOU2Qwxyr66TdFDBmUpOHT9Vhguzn9PzGqE8X/fv/If9Ypr/7hnbN6HbzGjMZCJeG1OQ92McQsdrfkYEwjsLJHqkXXFPVO6DQSkO1ltojt6YTnNRkN8cc4jqvS7WPGb+kTQAZyB8wLXihfpvsY8ho85qKqn904XFIs1ttrn75z1nDGslYAa35tObqI7qQ0GYs+o5pvfb7D0RosYF2a0oYc3WA/Fj9CcsjGYiB3nIwYAC0tR5bhp45Bpp1DrXT6JRwnVNGOa0Ms1rByJWDlo++VEhNsMRdbRsmZQe9B0k77wRVnUDaOwTq1WgFiHZUKN/4WD1MBQe2ZwqdpAa9o8pwoEZJSZ1soRPad53wv1lyTZ+zYrDDl8tf2Rho53p0mlOheEuVMZz2pu3o2PWjGXWPrS2c7zFFnH4z6n2waV5mn6vr8lRvrPu4xvLfq22RvoZIv20Z3c32CVNVMoYQy3pfYz2fz+cfAAAA//8DANVoRU82BAAA

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSUXW/jKBSG/4rFtXEBY2x81013tFGn02ianV3tHYbjFNWBCHBnqyr/fYSTqumM9mLvrGPe8/GcF16RU3tAPbo+HH5/BpciKlGyS4gRJjARmNAtrXvO+rqpBCed5OIfVCJ7Cy+oRy3XmmoJuAYBmBvS4KEeNTYGJCXUENbwnFPtIupfkbKVgWerofLxG4RovUM9+ss647/n2spWevKzqYKfYO1iUk7nZr6sV7f48/Vme785nYoQs7iyBvVoGLnhghisBRsxJx3BHSiBFVDgpqMSWHOSWZcgODVV0Ty91zc+OUi6ZxWrK4KZRMcSGZVUbnlQEbYvh9zFgugmx8slvHz2r+gZAupZ+QbzlO5KT/ZqH4fZTuZq8ruddbuVd6PdzUGlXLhEh+APEJKFBc5v1qnw8tnvdjkf+qSmCKhEWwh769R0+nEDo5qn9ODnsJCJ5um/zqAeqTl5VKKF5DQtZYvzMKvJgku/aC8K3z8Um0ml0Yf9hyU9nNiv/wf6O6UfrYNifYN6RCgwU3eAR0YF5p1RWDW1xELUYgSqZcsoKtHKu2Td7OdYrF2C3Znah/6ug360CXSaQ57pb5HNtgnezDoV7wumpCJVTUgWzekwp+IrGBtAJ8hDbMN8zveThAm2iD7ZCX7Bs5pDAJeKjUqPxR8qPqIeSdVpqqSSSgmtR2gIjMQQJUhrQBgBhjPTGi65EVwSLbgaKDeqocOYI6hEkE1WLL4momasIxLX1HSY12ODpaoHzIWghOpRctZ+gFvcT1mndEc4mBpLGA3mkioseV1jUzeqZWJsWzZecDobgpJM/dKEf0YwZy99OVn7bXYDz6d7vPQpjOnACIoZpw3mUlLcyabFQ9sNchAdNDpPtvIu+jeQ3yAMPtqUH5E76+xeTT8fed/MLQQH08V27qwOPvoxFWdbFh/29XV2+RUrFot+tw7/uxjjxusnCEU2lrLuYpnH4/H4AwAA//8DACOBIt4MBQAA

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSUW2/cNhCF/4rAZ1Hm/aI3w27RhZt6ES/aoG8UZxQL0UoLito0MPa/F9RukbhveRMGOsPDb87wjUzhiKQl96fTL2ec8kJqkoetJJgwlBnK+IHLVolW6sYw77TWf5OaDE/4jbTEqhh59EglGqQKmKad7CMFQM8ZBya0Kj3D54W0byQMDeB5iNjMy5+YlmGeSEv+GiaYv5azw9DEcV6hSfOIu2nJYYrFzB+7hyf6+/3+8Ly//rXgUsTNAKQlXa9AGQY0GtFTxRyjDoOhATkqcNyj0FfZMGVMUxibBb58Px/mPGGOrWiEbBgVnlxqAiGHYrkLCx6+nYqLDdFjqddbefts38gZE2lF/R/Ma7u7OA53y9qNeMbxFNKC6S7Ox2OYgNTklOYTpjzghuWMqZuXIReiMdrgHOfgglXagpNaSAuSIThmNXjwwSjTi6h1D53zVvUKBFdgUKnOkpps4MYx5GGeqpv3h3HAKZOaPL9U+zHkfk7Hd+hfrkR3PwG02CYtkUxLYy0qKWXwphMMwZpojOhiYLbvBPKITHtlPbeMSy+Yl9pYZyEwH0lNPoT4OkxY7R5JSxhHAdIh7QU3VDkINGjpqTHS9Mijt4KTmjzMUx6mdV6Xajdl/Jy2+5KW/BrGBa83vU/xdcgY85oKhE+mhHGfZlhjrr4HgLOGNZKxIlrzac3VR4QhYcxYcBzSeuv3P4kwYhM9rCnhlKt9yK/Vb2F5JS3xwUUefPAhmBh71Ax7BiwYZgENGAQlwILyCozyLBoVOq4gaN71pUJqgiVv1RZxzXoudadpj6ipYr2gnjFHey6CdMZaUPIdx+p5LLoQHVMIknrsgSrPA/VKSgpSBytMb63of0ByCwtnBTDg+bqsmwMD4BAMp0JxTZX3nDqvLe2s63xnHOpYPD9hmnD8AdSHIaZ5mftc3bJWvUP3cZ3Kg1Ntufs6TPSfbUaPc/yCqSozDsNU9us21ktNjhiWNeFxe6/aN7Ivy1UdtmdL6IZ5J2rykkPK6+lW1rKxzrjL5XL5FwAA//8DAPYEweL0BAAA

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSUTW/cNhCG/4rAsyjzS/zQzbBb1HVTL+JFWvQ2Ike2EC21oKhNA2P/e0DtBol7600Y6J0ZPvPOvJEIByQduT0efzlhzAupSR63kGBCU6Yp43suOyU62Tat4cpq9w+pyfiIX0lHjPKee4dUokaqAmtpLwdPQ0DHGQ9MtKrkhJeFdG8ExibgafTYzMsnTMs4R9KRv8YY5i+lNoyNn+Y1NGme8CEuGaIvzfz5cPdI/7jd7Z92l78WXIq4GQPpSD+ooDQL1GsxUMUsoxZBU0COKljuULQX2RgzpghTs4TPP+qHOUfMvhONkA2jwpFzTQJkKC33sOD+67F0sSG6L/F6C2+f3Rs5YSKdqL/DvKS78dN4k+fjhCecjpAWTDd+PhwgBlKTY5qPmPKIG5btpdMEeZxjdS12N40YM6nJ03O1myAPczq8Y/V8QfDwPwicMPWkI5K1UhuDSkoJTveCYTDaay16D8wMvUDukbVOGccN49IJ5mSrjTUBmPOkJi/T3MP0+7Lxc4L1NjAjtECFTiitnWcoLQ6tQRZA615b74K0RigbrAQHwF2Pxjvti0E+gH8dI1YP96QjjKMI0iIdBNdU2QAUWumo1lIPyL0zgpOa3M0xj3Gd16V6iBlf0saPdORXmBa8kLtN/nXM6POaCtS/dSm2S3NYfa5+OICzhjWSsSJa83HN1UcMY0KfseDdp/Wa7z8SocUmultTwpirHeTX6jdYXgsVsJ6DK0/V3g/YMhxYYKCZCaiDxqBEMEE5FbRyzGsFPVcBWt4PJUJqgsVw1eZxqSQGNgBtfQtUoeYUmDXUh9aI4EQ/wHuO1dNUdOAtUxgkdTgEqhwH6pSUNMgWjNCDMWL4CcnVfJwVwAFPl23dOtAhWAyaU6F4S5VznFrXGtob27teW2x96fkRU8TpJ1AfRp/mZR5ydfVu9Q7dxzWWi1NtPv4yRvrvNqP72X/GVJUZwxjLgl3Heq7JAWFZEx62g9W9kV3Zrmq/3S3RNsxZUZPnDCmvx2u4lY2x2p7P5/M3AAAA//8DADaVhNr1BAAA

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACoxUS2/bOBD+KwLPpkJSFCUK2IPhtFgjTWMk7u5ibxQ5jInIokFRToMi/31BWa6dooe9CaN5fY/hD9SrPaAGLQ+HT0fo44AWKLopxAgTmAhM6JYWDWdNIXJS0IrW7F+0QO4O3lCDKq411RJwAQIwN6TEbWE1NgYkJdQQVvLUUz0PqPmBlMsNHJ2G3A9/QRic71GD/na98a9ptnK57vxo8uA7WPdDVL1Oy3xdr+7wl+Vm+7A5ZQ0wpOLcGdSg1nLDBTFYC2YxJzXBNSiBFVDgpqYSWHkqc32E0KsuH8zLZb7xsYeoG5azIieYSfS+QEZFlVZu1QDbt0PaYqLoNsUXU3j6bH6gIwTUsMWZzFO7G925m/3Qjq4zN1GFZ4g2qD28+vACR9WhBToEf4AQHUzcPDxll5UoyUnOBCMELdC90jvXQ7a+zR66BFjpmnAwBZZgDeaSKix5UWBTlKpiwlYVs2iB7iD00F11vXc6+MHbmM2UZx/mrHwfXT/6ccjWfYTnoOKp7rPqBkALtBnbzg27R1Dmbesfx/SvH7uEZTWGAH3MNirusj/VsEMNkqrWVEkllRJaWygJWGKIEqQyIIwAw5mpDJfcCC6JFly1lBtV0tamCFqghzEexnjm//vVEtvg9nswlw22E8ebTkXrw35toI/OuiTNOWMyVNdNqLK556pz0McPJKMGEQrMFDVgy6jAvDYKq7KQWIhCWKBaVoyeCLPuefzJE7HW8MKWdS1a2VICzFY1J5VtidG20NKULVSsIlUNtdSEtCWndSs1MUBLbhK64M2o469WKCaBHsc+XWe2TrBfXY+/C35hZBN89Np3STDXwdPbEGH/O9VWfn/wg4sJv52l/TbAve/T3/n+Z87m4qWPlxikO8im0yuZopYwiRWnBea20LjmdYsVU9TQtpVGJxlvvX6BkCWDKddPmpw99fCULYPeuQg6jiFN/mcCdZLz8/lkLnzkXz9tVz7A8nBYzNE/jomkU7OzAT68K9+GlL7zQ7yCPEN7gs6eF7uyk4Hj6alyKSiMqcEIihmnJeZSUlzLssJtVbeyFTWUE86TXbNHMC6AjlO/bRjTtJnZ3/lyGaKzSschHc8Xryc7zf6cU35d8kq2n9Wn4VfSzVaaO1GSLPt0ejgnB/2vd/P9/f39PwAAAP//AwC2uuIgKAYAAA==

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACoxU227cNhD9FYGvFRXeRJF6c20HXaSOjeyiLfo2Ikc2a5laUJSTwPC/F9TauaJAXwRhSM45c+bMPJEID0h6cnY8Xj5izAupSQ5bSDChKdOU8QOXvRK94o0xgivd/U1qEt7hZ9KTTjnHnUUqUSNVnrV0kKOj3qPljHsmWlVywu1C+icCofH4GBw28/IHpiXMkfTkzxD9/LFgQ2jcNK++SfOEu7hkiK6Qeb87f0d/P7s5XN+cbi24lMdN8KQnw6i80sxTp8VIFTOMGgRNATkqb7hF0Z6ehZgxRZiaxd9/xfdzjphdLxohG0aFJc818ZChUB5gwcPnY2GxSXRR4vUW3n77J/KIifSifhXzlO6Nm8Kbh2VYw+TfbF9Sk2Oaj5hywE2N6331lQRnDWuEFoyRmlyBuwsRq91FdT2VEsEZptBLanH0VFkO1CopqZctdEKPXSdGUpN3mCJO32S9Ci7Nyzzm6kXk6juc/dn5ZYRhwoLxFqYFSU12yz5D9DDNES8/oVvzKdchreX4fI45xHVel2oXM94meDl/fX++poQxVzeQ76rfYLkjPbFgHAcLFkA7N2LLcGSegWadR+01eiV855VVXivLnFYwcOWh5cNYIqQmvxYN96tzuCxf2WwmmaaNQ/XSp/MpYMzfyUh6wjgKLw3SUXBNlfFAoZWWai31iNzZTvBXmAOkW8ykJ6MZhoE5NwhluQNhQfqRSa9HK0TXgRpl2ymhdcs6cL7rBFdeduhAjpIZQWpyk2a/uvxjq+XWgJs0/4MuF6lITzRrW+NgGIRW3IA1qLhWRlvsrBqBKekG4K2BYeAMWuasEQ64tqAG345fVLqMtyHiRViOE3z+Btc0upG/OADDR1A/XH+b4AE/zun+/cnGzfvLA6nJhzWWjVDtikU+hkg/6TLRV+uUw+EuIXj0V7PHn32EZVyqbUJH27pRGks96DKhbKAwGEalYcC1VErpov3F7O4xVcVhEGKZqi/JrvfVWXJ3IaPLayr8/tp4XO+rmwnyOKeH7zbJN4X9KEAjJGelMx4fT9to46i9N+g1p0LxliprOTW27ejQmcEO2mDrimLXaz6uufqAPiR0eav4xYsb5vkduvufxXh1wYtFOSv17k9bbJP2fy2x55o8ICxrwodtW/dPZBcjpg35Yj2N4i5ehWkKC7o5+oX0WijTdK1WLwz/855kvOmkUs/Pz8//AgAA//8DAEDMGTMbBgAA

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRTTW/cNhD9K8KcRZmkKErUzVin6MJNvEgWadEbxRnFhGVqQVFOAmP/e0DtBk5664UgBnwz72P4CsE+E/Rwezq9e6GQFigh+a0kudSMa8bFUdS9kr0SVdd2XKruXyjB39N36KFVzglniNWkiSnkDRvq0TFEMoIL5LJRuaf9skD/CtZXSC/eUTUvnykufg7Qw98+4Pw1z7a+ctO8YhXnifZhSTa4TObDfnfP/ro9HB8Ol1cLLRlceYQehlGh0hyZ03JkinecdWQ1syRIYScMyeYC8yFRDHaqFnx6m49zCpRcLytZV5xJA+cS0CabKQ92oeP3U2axWXSX6+VW3q79K7xQhF6WP828tLtxk795XobVT3hzOZNdnrLKU5xPFJOnzZNN5jTZ5OdQXCftJk8hQQkPn4rDZNM4x+ffjPp00b//H/KPefq7b+TWRLib15CgB1EbKOG9dY8+ULG/gx64IIl1R2yUQjPVoWW2qQ3TutYjCWdaKaCE3RySD+u8LsU+JPoSNwHQwx92WuhC/Ta6R5/IpTVmVf/ovAuHOOPqUvHmv+AVr2rOM2hNpzUVHwl9JJco6zvG9drvPxCp5QbarTFSSMXBpsfiT7s8Qg/Gdk5YY4212rmRGk4jR241b5E0akIlsUVlFGpluNPKDkKhbcQw5gqUQDnuYtswIdRQu4GYbCRnCp1idqCWNdia0TnpBpspvvlYPEwZZ13HFWHNDI3IlBGWGVXXDOvGtlKPbSvHXyy5pi94Nhjp5fJXNgYasSPUgkklGqaMEawzTcuGthvMoDtqXOZ8TzHQ9ItR772L8zKPqbguT/GbdR/XkP97sS3SVx/Yty2ju9k9USxyxtaHvN7XWM/n8/kHAAAA//8DAOoNqD02BAAA

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRUwW7cNhD9FWHOokxSFCXqZqwTdOGmXiSLtOiN4oxiwjK1ICknhrH/Xki7ReJT0QtBjPiGb9571BsE+0zQw+3p9OGFQk5QQvZbSXKpGdeMi6OoeyV7JaquVa1q6r+hBH9Pr9BDq5wTzhCrSRNTyBs21KNjiGQEF8hlo9ae9luC/g2sr5BevKNqTl8pJj8H6OFPH3D+vt5tfeWmecEqzhPtQ8o2uJXMH/vdPfv99nB8OFxOJUoruPIIPQyjQqU5MqflyBTvOOvIamZJkMJOGJLNBeZDphjsVCV8+nk/zjlQdr2sZF1xJg2cS0Cb7Up5sImOr6eVxSbR3Vovt/K27d/ghSL0svxXzEu7Gzf5m+c0LH7Cm8uabXpK2zpal+f4CiWc4nyimD1t+tymRM/D9Hq06enj5cy6TR9+kFsy4W5eQoYeRK2hhE2gabLZz6G4ctxNnkLePuboQ/Luv3rVUMLDl+Iw2TzO8fmdH18uMu//h8qfrHv0gYr9HfTABUmsO2KjFJqpDi2zTW2Y1rUeSTjTSgEl7OaQfVjmJRX7kOlb3EaCHj7aKdGF3210jz6Ty0tc5/xLr7k6xBkXl4ufXgpe8armfAUt+bTk4jOhj+QyrUMc43Lt9/U9RGq5gXZLjBRycbD5sfjNpkfowdjOCWussVY7N1LDaeTIreYtkkZNqCS2qIxCrQx3WtlBKLSNGMa1AiXQGp1iS6uVjZAtR9Y5NTJFamRdKw3ruNNScMnR4Dsdi4dpw7mOK8KaGRqRKSMsM6quGdaNbaUe21aOv0hyzYPgq8BIL5d3tzHQiB2hFkwq0TBljGCdaVo2tN1gBt1R41bO9xQDTb8I9cm7OKd5zMU1IcU76T4vYf13FFtavvvAfmwe3c3uiWKxemx9WJ/K1dbz+Xz+BwAA//8DAELILvyCBAAA

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSUW2/cNhCF/4rAZ1Hm/aI3w27RhZt6ES/aoG8UZxQL0UoLito0MPa/F9RukLpveRMGOjOH3xzyjUzhiKQl96fTL2ec8kJqkoetJJgwlBnK+IHLVslW8EZK75R2f5OaDE/4jbTEqhh59EglGqQKmKad7CMFQM8ZBya0Kj3D54W0byQMDeB5iNjMy5+YlmGeSEv+GiaYv5bZYWjiOK/QpHnE3bTkMMVi5o/dwxP9/X5/eN5f/1pwKeJmANIS1wsTQtdR77tAFaCjgXtOueaopFYBFb/KhiljmsLYLPDlx3yY84Q5tqIRsmFUeHKpCYQciuUuLHj4diouNkSPpV5v5e2zfSNnTKQV9XeY13Z3cRzulrUb8YzjKaQF012cj8cwAanJKc0nTHnADcsZUzcvQy5EY7TBOc7BBau0BSe1kBYkQ3DMavDgg1GmF1HrHjrnreoVCK7AoFKdJTXZwI1jyMM8VTfvD+OAUyY1eX6p9mPI/ZyO79C/XInufgJosU1aIpmWxlpUUsrgTScYgjXRGNHFwGzfCeQRmfbKem4Zl14wL7WxzkJgPpKafAjxdZiw2j2SljCOAqRD2gtuqHIQaNDSU2Ok6ZFHb0WZ/jBPeZjWeV2q3ZTxc9rOS1ryaxgXvJ70PsXXIWPMayoQPpkSxn2aYY25+hEAzhrWSMaKaM2nNVcfEYaEMWPBcUjrrd//JMKITfSwpoRTrvYhv1a/heWVtMQHF3nwwYdgYuxRM+wZsGCYBTRgEJQAC8orMMqzaFTouIKgedeXCqkJlrxVW8SNFyxy1VOuvKLKR0+d95zqTisP6KXD/h3H6nksuhAdUwiSeuyBKs8D9UpKClIHK0xvrSi670huYeGsAAY8Xy/r1QGAQzCcCsU1VWW489rSzrrOd8ahjsXzE6YJx/+A+jDENC9zn6tb1qp36D6uU3lwqi13X4eJ/rPt6HGOXzBVZcdhmMr9uq31UpMjhmVNeNzeq/aN7Mvlqg7bsyV0Y73zNXnJIeX1dCtb2Xjv3OVyufwLAAD//wMAMacs9fQEAAA=

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACoxUS2/bOBD+KwLPpkJKFCUK2IPhtFgjTWMk7u5ibxQ5jInIpEFRToMi/31BWa6dooe9CaN5fY/hD+TkHlCLlofDpyO4OKAFinYKFaTgmHBM6JaWLSvbguZNwTgvxL9ogewdvKEW1UwpqgTgEjhgpkmFu9IorDUISqgmRcVST/k8oPYHkjbXcLQKcj/8BWGw3qEW/W2d9q9ptrS56v2o8+B7WLshSqfSMl/Xqzv8ZbnZPmxOWQMMqTi3GrWoMQWXsuuwEJ3ETEODJRUU04oCKysmgdFTmXURgpN9PuiXy3zto4Oo2iIvypzgQqD3BdIyyrRyJwfYvh3SFhNFtym+mMLTZ/sDHSGgtlicyTy1u1G9vdkP3Wh7fRNleIZogtzDqw8vcJQ9WqBD8AcI0cLEzcNTdlmJkpzkBS8IQQt0L9XOOsjWt9lDnwBL1RAGusQCjMZMUIkFK0usy0rWBTd1XRi0QHcQHPRXXe+tCn7wJmYz5dmHOSvvonWjH4ds7SI8BxlPdZ9lPwBaoM3Y9XbYPYLUb1v/OKZ/buwTltUYAriYbWTcZX/KYYdaJGSjqBRSSMmVMlARMEQTyUmtgWsOmhW61kwwzZkgijPZUaZlRTuTImiBHsZ4GOOZ/+9XS2yD3e9BXzbYThxvehmND/u1BhetsUmac8ZkqL6fUGVzz1VvwcUPJKMWEQqFLhvApqAcs0ZLLKtSYM5LboAqURfJUSvvjH0ef/JEjNGsNFXT8E50lEBh6oaR2nREK1MqoasO6qImdQONUIR0FaNNJxTRQCumE7rg9ajir1YoJ4EeR5euM1sn2K/W4e+cXRjZBB+98n0SzPbw9DZE2P9OtZXfH/xgY8JvZmm/DXDvXfo73//M2Vy89PESg3QH2XR6siLSlCXHomQCM91JLIhssDGkNgZ0WUOJFujWqxcIWTKYtG7S5Oyph6dsGdTORlBxDGnyPxOok5yfzydz4SP/+mm78gGWh8Nijv5xTCSdmp0N8OFd+Tak9J0f4hXkGdoT9Oa82JWdNBxPT5VNQa51A5pTXDBaYSYExY2oatzVTSc63kClLnbNHkHbACpO/bZhTNNmZn/ny2WI1kgVh3Q8X7ya7DT7c075dckr2X5Wn4ZfSTdbae5ESbLs0+nhnBz0v97N9/f39/8AAAD//wMAM2qugCgGAAA=

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSU227cNhCGX0XgtSjzJB50Z9gt6rqpF/EiLXo3JEe2EK20oKhNA2PfPaB2g8S9650w0D+Hb/7hG5nggKQjt8fjLyec8kJqkoctJJjQlGnK+J7LTslO8EY4boXl/5CaDI/4lXTEqBB4cEglaqQqspZ62QcaIzrOeGSiVSUnvCykeyMwNBFPQ8BmXj5hWoZ5Ih35a5ji/KXUhqEJ47zGJs0jPkxLhimUZv58uHukf9zu9k+7y18LLkXcDJF0xPZCA3hPnfNAVURLgTtOectRyVYBKn6RDVPGNMHYLPHzj/pxzhPm0IlGyIZR4ci5JhEylJY9LLj/eixdbIjuS7zewttn90ZOmEgn6u8wL+luwjjc5Pk44gnHI6QF002YDweYIqnJMc1HTHnADcs26ThCHuapuha7GwecMqnJ03O1GyH3czq8Y/V8QfDwPwicMHnSEclaqY1BJaUEp71gGI0OWgsfgJneC+QBWeuUcdwwLp1gTrbaWBOBuUBq8jLOHsbfl42fE8zbyIzQAhU6obR2gaG02LcGWQStvbbBRWmNUDZaCQ6AO48mOB2KQT5AeB0mrB7uSUcYRxGlRdoLrqmyESi00lGtpe6RB2dEmeZunvIwrfO6VA9Txpe08SMd+RXGBS/kblN4HTKGvKYC9W9diu3SHNeQqx8O4KxhjWSsiNZ8XHP1EeOQMGQsePdpveb7j0RosYnu1pRwytUO8mv1GyyvhQrYwMGVUXUIPbYMexYZaGYi6qgxKhFNVE5FrRwLWoHnKkLLfV8ipCZYDFdtHjcsGghtT73RLVWtZ9RxISj4wGPfC2Xb9h3H6mksOgiWKYySOuwjVY4DdUpKGmULRujeGNH/hORqPs4K4Iiny7VuHegYLUbNqVC8pco5Tq1rDfXGeue1xTaUnh8xTTj+BOrDENK8zH2urt6t3qH7uE7lxak2H38ZJvrvtqP7OXzGVJUdwzCVA7uu9VyTA8KyJjxsD1b3Rnbluqr99m6JtjHOupo8Z0h5PV7DRjbOWXs+n8/fAAAA//8DALmMltD1BAAA

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSUTW/jNhCG/4rAs6mQFEWZuqVOFzWy2Rgbd1v0NiJHDhGZNCgq2yDwf19QdhBnFz30Joz4zsczL/lKPOyRtOT6cPj9GX0ayYIkN4cEE4oyRRnf8qqVVSt4WSnBlBD/kAVxt/hCWtJIY7jRSCtUSKVlNe2q3lBrUXPGLRO1zDlhN5L2lYArLT47g2UYv2EcXfCkJX85b8P3XBtcaYYw2TKGAdd+TOBNbubLenVLP19vtveb06kRxywunSUtWfZCAXQd1boDKi0uKXDNKa85yqqWgJKfZM4njB6GcrRP7/VtSB6TaUUpqpJRoclxQSwkyC13MOL25ZC7mBHd5PhiDs+f7St5xkhasXiDeUp3ZQZ3tR+7yQ32agi7nfO7VfC9200RUi68IIcYDhiTwxnOb85DfPkcdrucj3yCYUSyIFuMe+dhOP24wR6mIT2EKc5kRvv0X2dIS2BKgSzITHIY5rLFeZjV4NCnX7QXhe8fis0AqQ9x/2FJDyf26/+B/g7Mo/NYrG9ISxhHYasl0l5wReXSAoW60lSpSvXIjW5E1qyCT85PYRqLtU+4O1P70N91NI8uoUlTzDP9rbLZNjHYyaTifcGclaysGMuiKR2mVHxF6yKahHmIbZzO+X6SCCVm0Sc34C94VlOM6FOxgfRY/AHjI2mJhqXhoEEDKGN6rBn2zDJQrLGorEIrhW2s1NIqqZlREjouLdS863OELAhmkxWzr1HpjlVNTbUGQWUvO6pr2dAKGmGENpxVH+EW90PWgVkyibaiGntLpeZAtawqaqsaGqH6phH9BaezITjLyS5N+OeI9uylLydrv81u8fl0j+c+lbVLtIpTIXlNpdacLnXd0K5ZdrpTS6xNnmwV/BjeQH7D2IXRpfyI3Dnv9jD8fOR9M7cYPQ4X27lzJoYx9Kk427L4sK+vk8+vWDFb9Lvz9N/ZGDfBPGEssrHA+YtlHo/H4w8AAAD//wMA2IvlogwFAAA=

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRTwW7UMBD9lcjndWo7jhPnVm1BrAp0RStA3CaeCWuROivHKaBq/x05uwjKjZv17Dfz5r3xMwvwSKxj18fjqycKaWYblvwKKaEMF4YL+SCrTledsmUjhGis+sI2zN/ST9axRjsnnSVekSGuUdS8rwbHEclKIVGoWuea8HVm3TMDXyI9eUflNH+kOPspsI598gGn77k3+NKN04JlnEbahTlBcFnM+932lr+93j/c7c+vZpozufTIOtYOygD0Pbe2B66RWg7SSi5rSbqqNZCWZ5oPiWKAsZzx25/+OKVAyXWqVFUpuLLstGEICbLkHmZ6+HnMKlaLbjK+WeH12D2zJ4qsU5vfZp7LXbnRX7np8RECXg0++PnANuwYpyPF5Gm1Y51wHCH5KRSXJtvRU0hsw+7ui/0IaZji4wuP7s+j7/5j8nfgDj5QsbthHROSFFYt8UFJw3WLwKGuLDemMgNJZxuVOdspJB+WaZmLXUj0Na4qWcdewzjTWd91dAefyKUlZumfTc6afvi0nTADgm3YPk64uFT8sVuKUpSVyJd3SzouqfhA6CO5RHmmh7hcyv9DUUatpO0SI4VU7CEdijcwH1jHLLROggULYJwbqBY0CBRgRINk0BBqhQ1qq9FoK5zR0EuNUMt+yEjWndMt1oXSUuKAdcO1A8216DXvexq4rYwZemtb0eMLW4u7MfPAtUITVtzSgFxbCdzqquJY1dAoMzSNGv6y5JK4FNlvpKfz11gVGMSW0EiutKy5tlby1tYN75u2t71pqXZZ8y3FQONfRr3zLk7zNKTisjDFC+s+LCF/72Jdnu8+8B9rZDeT+0axyJGDD3mbLymfTqfTLwAAAP//AwAqQNGLJQQAAA==

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRUTW/cNhT8KwLPS5lfokTdjHWCLtzUi2SRFr09kU8xYZlakJQTw9j/XlDaIPWp6IUgnjiP82aGeiMBnpH05PZ8/vCCISeyI9mvJcGEpkxTxk9c9kr2wtTaKK2N+JvsiL/HV9KTVlnLrUEqUSNVjjV0kKOlzqHhjDsmGlV6wrdE+jcCvnb44i3Wc/qKMfk5kJ786YObv5e7wdd2mhdXx3nCQ0gZgi1k/jjs7+nvt8fTw3E7lTAVcO0d6Uk3Cg0wDNSYAahy2FHghlPecFSyUYCKbzAfMsYAU53c06/73ZwDZtuLWsiaUWHIZUccZCiUB0h4ej0XFqtEd6W+W8vrtn8jLxhJL3Y/xdza3djJ3zynYfGTu9nWDOkpresINs/xlezIOc5njNnjqs9tSvg8TK8nSE8ftzNlmz78QLtkdPt5CZn0hEtNdmQVaJog+zlUV477yWPI68ccfUje/lcvSXbk4Ut1nCCPc3x+58eXTebD/1D5E9hHH7A63JGeMI7CyQ7pKLimqnNAoZGGai31iNyaVhTMfg7Zh2VeUnUIGb/FdSTSk48wJdz43Ub76DPavMQy51+65OoYZ7fYXP3ykrOa1ZKxAlryecnVZ3Q+os1YhjjF5drv63uI0GIF7ZcYMeTqCPmx+g3SI+mJgc5yMGAAtLUjNgxH5hho1jrUTqNTwrVOGeW0MsxqBQNXDho+jKVCdgRLdKotrUK2jbSKIraWKmY5NdIyqgE1ArDOmPGdjtXDVHBgO6bQSWpwdFQZDtQoKamTDbRCj20rCu6nJNc8cFYEdviyvbuVgXauQ6c5FYo3VBnDaWealg5tN5hBd9jYwvkeY8DpX0J98jbOaR5zdU1I9U66z0so/45qTct3H+iP1aO72T5hrIrH4EN5KldbL5fL5R8AAAD//wMAoRyWV4IEAAA=

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRTS2/cNhD+KwLPS5kvUaJuxjpFF27qRbJoi95GnFFMWKYWFOUkMPzfC2o3SN1bLwQx4DfzPYavLMIzsZ7dns8fXijmhe1YDltJCWW5sFzIk9S90b1ytXVtq1X3N9uxcE/fWc9a4730jrgmS9ygaPigR88RyUkhUajGlJ7wZWH9K4NQI70ET/W8/EFpCXNkPfszRJy/ltkQaj/NK9ZpnugQlwzRFzK/H/b3/Lfb4+nheHm10FLAdUDWs25UFmAYuHMDcIPUcZBOctlIMroxQEZeYCFmShGmesGnn/NxzpGy71WtdC24cuxtxxAyFMoDLHT6fi4sNovuSn23lbdr/8peKLFe7X6YeWl346dw87wMa5jw5nJmWJ6KynOaz5RyoM2TTeY0QQ5zrK6T9lOgmNmOPXyujhPkcU7P74z6fNF/+B/yT2X6h2/k10y4n9eYWc+kdmzHPoJ/DJGqwx3rmZCkUHfERyUtNx0Ch0Y7bq22I0nvWlXa7eeYQ1zndakOMdOXtAlgPfsFpoUu1G+TfwyZfF5TUfWXLbtwTDOuPlc//ZeiFrUWooDWfF5z9YkwJPKZir5TWq/9/gNRVm2g/ZoSxVwdIT9Wv8LyyHrmoPMSHDgA6/1IjaBRoAArWiSLltAobNE4g9Y44a2BQRqERg5jqbAdoxJ3tW0YaAckoOPK6ZabDog77DQfpDANYmPGcXznY/UwbTjfCUOouaMRuXESuDNac9QNtMqObasK7ocl1/SlKAYjvVz+ysbAInaEVnJlZMONc5J3rmn50HaDG2xHjS+c7ylFmv5l1Mfg07zMY66uy1O9s+7TGst/r7ZF+hoi/7ZldDf7J0pVyRhCLOt9jfXt7e3tHwAAAP//AwDn5IjHNgQAAA==

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACoxU227jNhD9FYGvFRXeRIl6S50saqRJjLXRFn0bkaOYjUwZFJXsIsi/F5KTzWVRoC+CMCTnnDlzZp5IgAOShpwfj5cPGNJIcpL8EhJMaMo0ZXzHZaNkI0xRMc60qP4mOfFX+J00pFLWcmuQStRIlWMlbWVnqXNoOOOOiVLNOeFuJM0TAV84fPAWi2H8A+Poh0Aa8qcPbnicscEXth8mV8Shx3UYEwQ7k7lZr67o7+eb3e3mdGvEcX5ceEcaUndCA7QtNaYFqhzWFLjhlJcclSwVoOKnZz4kjAH6YnT3b/huSAGTbUQhZMGoMOQ5Jw4SzJRbGHH3/TizWCS6mOP5El5+myfygJE0In8V85TuzPb+7DC2k+/d2fIlOTnG4YgxeVzUuN1mbyQ4K1ghtGCM5OQa7N4HzNYX2W0/lwi2ZgqdpAY7R5XhQI2SkjpZQiV0V1WiIzm5whiwf5f12ts4jEOXsheRsw842/PVZYC2xxnjC/Qjkpysx22C4KAfAl5+QzulU65dnObj1RCSD9Mwjdk6JLyL8HL++n41xYghZRtI++w3GPekIQZqy8GAAdDWdlgy7JhjoFnlUDuNTglXOWWU08owqxW0XDkoedvNEZKTX2cNt5O1OI5vbBaT9P3CIXvp06r3GNIHGUlDGEfhZI20E1xTVTugUEpDtZa6Q25NJfgrzA7iHSbSkK5u25ZZ2wpluAVhQLqOSac7I0RVgepkWSmhdckqsK6qBFdOVmhBdpLVguRkEwc32fS51XJpwCYO/6BNs1SkIZqVZW2hbYVWvAZTo+Ja1dpgZVQHTEnbAi9raFvOoGTW1MIC1wZU68ruh0qX4c4HvPDjsYfv73DrQhfyFwtQ8w7Up+tfIhzwcYj3NycbFzeXO5KTr1OYN0K2ni3y6AP9pueJvp765Hf7iODQXQ8Of/YRzuOSLRNaCle7sgTKVN1RBVpSkAopZ2WHyuqKYUVycjHYe4zZ7DDwYZ6qH8lut9l5tHuf0KYpzvz+WnjcbrNND6kb4uHDJnlX2GcBCiE5mzvj8OG0jRaO2rkaneZUKF5SZQyntSkr2lZ1a1pdY2lnxW6ndJxS9hWdj2jTUvGLFxfM1R7t/c9ivLrgxaKczV7bnrbYIu3/WmLPOTkgjFPEw7KtmyeyDgHjgnwxnUZxHa593/sR7RDcSJpaqrqodGVeGP7nPcVEURrGn5+fn/8FAAD//wMAys86yBsGAAA=

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSUW2/cNhCF/4rAZ1HmTbzozbBbdOGmXsSLNugbxRnGQrTSgqI2DYz97wW1G6TuW96Egc7w8JszfCOTPyLpyP3p9MsZp7yQmuRhKwkmNGWaMn7gslOqY65h1iij2d+kJsMTfiMdMSoEHhxSiRqpAtbSXsZAAdBxxoGJVpWe/vNCujfihwbwPARs5uVPTMswT6Qjfw0TzF/L2X5owjiv0KR5xN20ZD+FYuaP3cMT/f1+f3jeX/9acCniZgDSERukQotIdXSRKt621FkZKbjeCS1chBivsmHKmCY/Ngt8+XE+zHnCHDrRCNkwKhy51AR89sVy7xc8fDsVFxuix1Kvt/L22b2RMybSifo7zGu7uzAOd8vaj3jG8eTTgukuzMejn4DU5JTmE6Y84IbljKmflyEXoiEYby3nYL1RrQErWyENSIZgmWnBgfNa6ShC20borTMqKhBcgUalekNqsoEbR5+Heapu3h/GAadMavL8Uu1Hn+Ocju/Qv1yJ7n4CaLFNOiJZK7UxqKSU3uleMASjg9aiD56Z2AvkAVnrlHHcMC6dYE622lgDnrlAavLBh9dhwmr3SDrCOAqQFmkUXFNlwVPfSke1ljoiD84ITmryME95mNZ5XardlPFz2u5LOvKrHxe83vQ+hdchY8hrKhA+6RLGfZphDbn6EQDOGtZIxopozac1Vx8RhoQhY8FxSOut3/8kQotN9LCmhFOu9j6/Vr/55ZV0xHkbuHfeea9DiNgyjAyY18wAatAISoAB5RRo5VjQyvdcgW95H0uF1ARL3qot4kE538uoKVihqWLcUseNp4Frbr0B6eR7jtXzWHQ+WKYQJHUYgSrHPXVKSgqy9UboaIwok/yO5BYWzgpgwPN1WTcHGsAiaE6F4i1VznFqXWtob2zvem2xDcXzE6YJx/+A+jCENC9zzNUta9U7dB/XqTw41Za7r8NE/9lm9DiHL5iqMmM/TGW/bmO91OSIflkTHrf3qnsj+7Jc1WF7tiRrHBemJi/Zp7yebmXjGqWFuVwul38BAAD//wMACyupKPQEAAA=

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSUXW/jKBSG/4rFtXEBY2x81013tFGn02ianV3tHYZDiupAhHFnqyr/fYSTqumM9mLvrGPe8/GcF16RV3tAPbo+HH5/Bp8mVKLklhAjTGAiMKFbWvec90RWlJG2IfwfVCJ3Cy+oRy3XmmoJuAYBmBvS4KG2GhsDkhJqCGt4zql2E+pfkXKVgWenoQrTN4iTCx716C/nTfieaytX6THMpophhLWfkvI6N/NlvbrFn6832/vN6dQEUxZXzqAedbrm0AFgYaXFnDYNll1tsZGDZIJJa6w9yZxPEL0aq8k8vdc3IXlIumcVqyuCmUTHEhmVVG55UBNsXw65iwXRTY6XS3j57F/RM0TUs/IN5indlR7d1X4aZjeaqzHsds7vVsFbt5ujSrlwiQ4xHCAmBwuc35xX8eVz2O1yPvRJjROgEm0h7p1X4+nHDVg1j+khzHEhM5mn/zqDeqTmFFCJFpLjuJQtzsOsRgc+/aK9KHz/UGxGlWyI+w9LejixX/8P9HdKPzoPxfoG9YhQYKbuAFtGBeadUVg1tcRC1MIC1bJlFJVoFXxyfg7zVKx9gt2Z2of+rqN+dAl0mmOe6W+RzbaJwcw6Fe8LpqQiVU1IFs3pMKfiKxgXQSfIQ2zjfM73k4QJtog+uRF+wbOaYwSfio1Kj8UfanpEPZKq01RJJZUSWltoCFhiiBKkNSCMAMOZaQ2X3AguiRZcDZQb1dDB5ggqEWSTFYuv684CpR3BkrUK8xYkHngjMWmM0o1iurbtB7jF/Zh1SneEg6mxBGswl1Rhyesam7pRLRO2bZm94HQ2BCWZ+qUJ/5zAnL305WTtt9kNPJ/u8dKnMKYDIyhmnDaYS0lxJ5sWD203yEF00Og82Sr4KbyB/AZxCJNL+RG5c97t1fjzkffN3EL0MF5s587pGKZgU3G2ZfFhX19nn1+xYrHod+fxv4sxboJ+glhkYynnL5Z5PB6PPwAAAP//AwBHon9jDAUAAA==

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSUTW/cNhCG/4rAsyjzS/zQzbBb1HVTL+JFWvRGcUa2EC21oKhNA2P/e0DtBol7600Y6J0ZPvPOvJHoD0g6cns8/nLCmBdSkzxuIcGEpkxTxvdcdkp1zDVMOWmF+YfUZHzEr6QjRoXAg0MqUSNVwFrayyFQAHSccWCiVSWnf1lI90b82ACexoDNvHzCtIxzJB35a4wwfym1/diEaV6hSfOED3HJPobSzJ8Pd4/0j9vd/ml3+WvBpYibEUhHbJAKLSLVgxuo4m1LnZUDBdc7oYUbYBgusjFmTNFPzQKff9SHOUfMoRONkA2jwpFzTcBnX1ru/YL7r8fSxYbovsTrLbx9dm/khIl0ov4O85LuJkzjTZ6PE55wOvq0YLoJ8+HgI5CaHNN8xJRH3LBsL50mn8c5Vtdid9OIMZOaPD1Xu8nnYU6Hd6yeLwge/geBE6aedESyVmpjUEkpvdO9YAhGB61FHzwzQy+QB2StU8Zxw7h0gjnZamMNeOYCqcnLNPd++n3Z+DnBegvMCC1QoRNKaxcYSotDa5CB17rXNjiQ1ghlwUrvvOeuRxOcDsUgH3x4HSNWD/ekI4yjAGmRDoJrqix46lvpqNZSD8iDM4KTmtzNMY9xndeleogZX9LGj3TkVz8teCF3m8LrmDHkNRWof+tSbJdmWEOufjiAs4Y1krEiWvNxzdVHhDFhyFjw7tN6zfcfidBiE92tKWHM1c7n1+o3v7wWKt4G7l15qg5hwJbhwIB5zQygBo2gBBhQToFWjgWtfM8V+Jb3Q4mQmmAxXLV5nPUoglWCGi0GqqyQ1AcItDUBNXBtPLB3HKunqeh8sEwhSOpwAKoc99QpKSnI1huhB2NEccZ3JFfzcVYAA54u27p1oAEsguZUKN5S5Ryn1rWG9sb2rtcW21B6fsQUcfoJ1IcxpHmZh1xdvVu9Q/dxjeXiVJuPv4yR/rvN6H4OnzFVZcZ+jGXBrmM91+SAflkTHraD1b2RXdmuar/dLckax4WpyXP2Ka/Ha9i4Rmlhzufz+RsAAAD//wMAolJWbPUEAAA=

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACoxUS2/bOBD+KwLPpkJKFCUK2IPhtFgjTWMk7u5ibxQ5jInIpEFRToMi/31BWa6dooe9CaN5fY/hD+TkHlCLlofDpyO4OKAFinYKFaTgmHBM6JaWLWMtEXnVkJKX9F+0QPYO3lCLaqYUVQJwCRww06TCXWkU1hoEJVSTomKpp3weUPsDSZtrOFoFuR/+gjBY71CL/rZO+9c0W9pc9X7UefA9rN0QpVNpma/r1R3+stxsHzanrAGGVJxbjVrUqJJBA4C5EQYzWlVYNKXBWnSi4IUw2phTmXURgpN9PuiXy3zto4Oo2iIvypzgQqD3BdIyyrRyJwfYvh3SFhNFtym+mMLTZ/sDHSGgtlicyTy1u1G9vdkP3Wh7fRNleIZogtzDqw8vcJQ9WqBD8AcI0cLEzcNTdlmJkpzkBS8IQQt0L9XOOsjWt9lDnwBL1RAGusQCjMZMUIkFK0usy0rWBTd1XSTAdxAc9Fdd760KfvAmZjPl2Yc5K++idaMfh2ztIjwHGU91n2U/AFqgzdj1dtg9gtRvW/84pn9u7BOW1RgCuJhtZNxlf8phh1okZKOoFFJIyZUyUBEwRBPJSa2Baw6aFbrWTDDNmSCKM9lRpmVFO5MiaIEexngY45n/71dLbIPd70FfNthOHG96GY0P+7UGF62xSZpzxmSovp9QZXPPVW/BxQ8koxYRCoUuG8CmoByzRkssq1JgzktugCpRF/REmLHP40+eiDGalaZqGt6JjhIoTN0wUpuOaGVKJXTVQV3UpG6gEYqQrmK06YQiGmjFdEIXvB5V/NUK5STQ4+jSdWbrBPvVOvydswsjm+CjV75Pgtkent6GCPvfqbby+4MfbEz4zSzttwHuvUt/5/ufOZuLlz5eYpDuIJtOT3ZUdU1dYN5phZksBW50TXHZ1YqKQiqA1P3WqxcIWTKYtG7S5Oyph6dsGdTORlBxDGnyPxOok5yfzydz4SP/+mm78gGWh8Nijv5xTCSdmp0N8OFd+Tak9J0f4hXkGdoT9Oa82JWdNBxPT5VNQa51A5pTXDBaYSYExY2oatzVTSc63kClLnbNHkHbACpO/bZhTNNmZn/ny2WI1kgVh3Q8X7ya7DT7c075dckr2X5Wn4ZfSTdbae5ESbLs0+nhnBz0v97N9/f39/8AAAD//wMAkjOOpigGAAA=

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACoxUy27kNhD8FYHXiDLfEnVzbC8ycPzAziAJcmuRLZuxTA0oyt6F4X8PpLHXj0WAXAShSXZVV1f3E4lwj6Qlx/v92QPGPJGS5LCGBBOGMkMZ33HZKtVyXdVMas7s36Qk4Ry/k5bUyjnuLFKJBqnyTNNO9o56j5Yz7pnQaskJNxNpnwiEyuNDcFiN0x+YpjBG0pI/Q/Tj44INoXLDOPsqjQNu4pQhuoXM5ebknP5+fL27uj7cmnBaHlfBk5Y0TipsEKnpbU8V15raRvbU284KI2zv+/7wLMSMKcJQTf7uDd+POWJ2raiErBgVljyXxEOGhXIHE+6+7xcWq0SnS7xcw+tv+0QeMJFWlK9iHtIduSEc3U/dHAZ/tH5JSfZp3GPKAVc1rrbFGwnOKlYJIxgjJbkAdxsiFpvT4mpYSgTXMIVeUou9p8pyoFZJSb3UUAvT17VYSjzHFHF4l/UiuDROY5+LF5GLDzjb45OzCN2AC8YXGCYkJdlM2wzRwzBGPPuGbs6HXLs0L8cnY8whzuM8FZuY8SbBy/nr+5M5JYy5uIZ8W/wG0y1piYXGcbBgAYxzPWqGPfMMDKs9Gm/QK+Frr6zyRlnmjIKOKw+ad/0SISX5ddFwOzuH0/TGZjXJMKwcipc+nQwBY/4gI2kJ4yi8bJD2ghuqGg8UtLTUGGl65M7Wgr/C7CDdYCYt6Zuu65hznVCWOxAWpO+Z9Ka3QtQ1qF7qWgljNKvB+boWXHlZowPZS9YIUpLrNPrZ5c+tlmsDrtP4D7q8SEVaYpjWjYOuE0bxBmyDihvVGIu1VT0wJV0HXDfQdZyBZs42wgE3FlTndf9DpbN4EyKehmk/wPd3uE1lKvmLA2h4D+rT9S8J7vFxTHeXBxtXl2c7UpKvc1w2QrFZLPIYIv1mlom+mIccdrcJwaO/GD3+7CNcxqVYJ5R3EpxWkkqsHVXC1LRzhlENrPNSauSdJCU5Hd0dpmJxGIS4TNWPZFfb4ji525DR5Tkt/P5aeVxti+sBcj+m+w+b5F1hnwWohORs6YzHh8M2Wjka7xv0hlOhuKbKWk4bq2va1U1nO9OgdotiV3Pez7n4ij4kdHmt+MWLK+bJLbq7n8V4dcGLRTlbvLY9bLFV2v+1xJ5Lco8wzQnv123dPpFNjJhW5NP5MIqbeBGGIUzoxugn0hrd6IpJo18Y/uc9I+uKa2Gen5+f/wUAAP//AwBHzZ1MGwYAAA==

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSUUW/cKBDHv4rFs3EAY2z8Fm1a3SrXy6pd9aq+YWZoUBy8Apw2ivLdT3i3yuXpdC8IjZnhP7/54xcSzCOSkVyfTh+eMOREapL9FhJMKMoUZfzI21HKkXeN0px1SnwnNfG3+ExG0ktrudVIW1RIJbCOTq2zFAA1ZxyY6GSpaX4kMr4Q4xvAJ2+xWdJXjMkvgYzkbx9g+VnuNr6x87JCE5cZ9yFlE2wR89d+d0v/vD4c7w7nUwlTSW48kJEMtpU4IFLltKOSdx3VQ+so6EkLJbQD585pPmSMwcxNgoe3+2HJAbMdRSPahlGhyWtNwGRTJE8m4fH5VFRsiG5KvN7C23Z8IU8YySjq3zDP5a7s7K8e07T6Ga7OazbpIW2rMzYv8ZnU5BSXE8bsceNznRI+TvPz0aSHj+czZZs+/EK7ZoTdsoZMRsJbRWqyAZpnk/0SqovG3ewx5O1jjj4kb/+rVktqcvelOswmuyU+vpvHlzPm/f+g/MnYex+w2t+QkTCOAtoBqRNcUTmAoaZrNVWqVQ651b3gpCa7JWQf1mVN1T5k/BG3lshIPpo54VnfdbT3PqPNayx9flPFV4e4wGpz9TZLzhrWtIyVpDWf1lx9RvARbcbSxDGul3pf36cIJbak3RojhlwdTL6v/jDpnoxEm8Fyo402RlnrsGPoGDCjWA+oQCFIAT1ILUFJzaySZuISTMcnVyKkJlisU53dKmFwTABFzg2Vreup6TpDlbAC5AB66AuTN47V3VzyjB2YRGipRgdUam6olm1Loe1ML5Tre1H4/0Zy8QNnpRjg0/ndbQoUwICgOBWSd1Rqzemgu55O/TDpSQ3Y2aL5FmPA+V+gPnkbl7S4XF0cUr1D93kN5d9RbW756QP9tc3oZrEPGKsyY+NDeSqXsb6+vr7+AwAA//8DAEkVCfaCBAAA

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRTwY7cKBD9FYuz8QDG2Pg26tlVWpPstJJRstobpoo0igdaGE8SjfrfV7g7Sia33NCDV/XqveKFBPOEZCS3p9NfzxjyQmqS/QYJJhRlijL+yNtRypF3jdID01r9R2ri7/E7GUkvreVWI21RIZXAOjq1zlIA1JxxYKKTpab5vJDxhRjfAD57i01cPmJafAxkJJ98gPi19Da+sXNcoUlxxn1Ysgm2iPlnv7unb28Pjw+Hy6sFl0JuPJCRDLaVOCBS5bSjkncd1UPrKOhJCyW0A+cuNB8ypmDmZoEvP/tDzAGzHUUj2oZRocm5JmCyKZIns+Dj91NRsVl0V/B6g7fj+EKeMZFR1D/MvJS7sbO/sfHpyQS4cT745UhqckrxhCl73OzYJpxnk30M1bXJbvYYMqnJw4fqMJvsYnp65dGHy+j7P5j8nbFHH7Da35GRMI4C2gGpE1xROYChpms1VapVDrnVveCkJrsYsg9rXJdqHzJ+TptKMpK/zbzgRd9tskef0eY1Fen/qpI1fvN5F6EAjNTkkCKsNlc/7easYU3LyuXDmk9rrt4j+IQ2Y5npMa3X8r9RhBIbabemhCFXB5OP1RuzHMlItBksN9poY5S1DjuGjgEzivWAChSCFNCD1BKU1MwqaSYuwXR8cgUpuku61bZQ0LvJ9SCosb2iEruBGhgstZOVPWNge9e/srV6mAvP2IFJhJZqdECl5oZq2bYU2s70Qrm+FyWOH5ZcE+es+A34fPkamwIFMCAoToXkHZVaczrorqdTP0x6UgN2tmi+xxRw/sWod96muESXq+vCVK+se7+G8r2rbXm++kC/bZHdRfsFU1UiNz6Ubb6mfD6fz/8DAAD//wMAry4kfSUEAAA=

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRTS2/cNhD+KwLPosyXKFE3Y52iCzfxIlmkRW8UZxgTlqkFRTkJjP3vAbUbJO6tF4IY8Jv5HsNXEu0zkoHcnk7vXjDmhdQkh60kmNCUacr4kctBqYG3jTad1Fz8S2oS7vE7GUinnOPOIJWokSpgLR2ldxQADWccmGhV6Wm/LGR4JTY0gC/BYTMvnzEtYY5kIH+HCPPXMtuGxk3zCk2aJ9zHJdvoCpkP+909/ev2cHw4XF4tuBRwE4AMpHdSYY9ItTeeKt621PTSUzCjEVoYD95fYCFmTNFOzQJPv+bDnCNmN4hGyIZRYci5JmCzLZRHu+Dx+6mw2Cy6K/V6K2/X4ZW8YCKDqH+aeWl346Zw87yMa5jg5nJmuzwVlac0nzDlgJsnm8xpsjnMsbpO2k0BYyY1efhUHSab/Zye3xj16aJ//z/kH8v0d9/QrRlhN68xk4FwaUhN3lv3GCJW+zsyEMZRgOyResE1VT1YaltpqNZSe+TOdIKTmuzmmENc53Wp9jHjl7QJIAP5w04LXqjfJvcYMrq8pqLqH1124ZBmWF2ufvnPWcMayVgBrfm05uojQkjoMhZ9x7Re+/0HIrTYQLs1JYy5Otj8WP1pl0cyEGN7x62xxlrtnMeWoWfArGYdoAaNoAR0oIwCrQxzWtmRK7AtH32pkJpgibvaNsxbBcIqR1s7Mqp639PeW0cVWt9p7FCq/o2P1cNUcNb1TCFIatADVYZbapSUFGRrO6F914kSzU9LrulzVgwGfLn8lY2BBugRNKdC8ZYqYzjtTdvRsetHM+oeW1c432OKOP1m1Pvg0rzMPlfX5aneWPdxjeW/V9sifQ2RftsyupvdE6aqZGxDLOt9jfV8Pp9/AAAA//8DAOIvUl42BAAA

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,88 @@
using Microsoft.AspNetCore.Mvc;
using PlotLine.Services;
using PlotLine.ViewModels;
namespace PlotLine.Controllers;
public sealed class ScenariosController(IScenarioService scenarios) : Controller
{
public async Task<IActionResult> Index(int projectId)
{
var model = await scenarios.ListAsync(projectId);
return model is null ? NotFound() : View(model);
}
public async Task<IActionResult> Create(int projectId, int? bookId)
{
var model = await scenarios.GetCreateAsync(projectId, bookId);
return model is null ? NotFound() : View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(ScenarioCreateViewModel model)
{
if (string.IsNullOrWhiteSpace(model.ScenarioName))
{
ModelState.AddModelError(nameof(model.ScenarioName), "Scenario name is required.");
var repopulated = await scenarios.GetCreateAsync(model.ProjectID, model.BookID);
model.BookOptions = repopulated?.BookOptions ?? [];
return View(model);
}
var scenarioId = await scenarios.CreateAsync(model);
return RedirectToAction(nameof(Timeline), new { id = scenarioId });
}
public async Task<IActionResult> Timeline(int id)
{
var model = await scenarios.GetTimelineAsync(id);
return model is null ? NotFound() : View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> MoveScene(int scenarioId, int sceneId, int? anchorSceneId, int? targetChapterId, string position)
{
await scenarios.MoveSceneAsync(scenarioId, sceneId, anchorSceneId, targetChapterId, position);
return RedirectToAction(nameof(Timeline), new { id = scenarioId });
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Validate(int id)
{
var warningCount = await scenarios.ValidateAsync(id);
TempData["ScenarioMessage"] = $"Scenario validation found {warningCount} warning{(warningCount == 1 ? "" : "s")}.";
return RedirectToAction(nameof(Timeline), new { id });
}
public async Task<IActionResult> Compare(int id)
{
var model = await scenarios.CompareAsync(id);
return model is null ? NotFound() : View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Apply(int id, string confirmation)
{
if (!string.Equals(confirmation, "This will replace the current scene order with the scenario order. Continue?", StringComparison.Ordinal))
{
TempData["ScenarioMessage"] = "Scenario was not applied. Use the exact confirmation text.";
return RedirectToAction(nameof(Compare), new { id });
}
await scenarios.ApplyAsync(id);
TempData["ScenarioMessage"] = "Scenario applied to the main story order.";
return RedirectToAction(nameof(Timeline), new { id });
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Archive(int id, int projectId)
{
await scenarios.ArchiveAsync(id);
return RedirectToAction(nameof(Index), new { projectId });
}
}

View File

@ -233,6 +233,19 @@ public interface IDynamicsRepository
Task RunContinuityChecksAsync(int projectId, int? bookId, int? sceneId);
}
public interface IScenarioRepository
{
Task<IReadOnlyList<Scenario>> ListAsync(int projectId);
Task<Scenario?> GetAsync(int scenarioId);
Task<int> CreateAsync(int projectId, int? bookId, string scenarioName, string? description);
Task<(IReadOnlyList<Book> Books, IReadOnlyList<Chapter> Chapters, IReadOnlyList<ScenarioScene> Scenes, IReadOnlyList<ScenarioWarning> Warnings)> GetTimelineAsync(int scenarioId);
Task MoveSceneAsync(int scenarioId, int sceneId, int? anchorSceneId, int? targetChapterId, string position);
Task<int> ValidateAsync(int scenarioId);
Task<(IReadOnlyList<ScenarioComparisonRow> MovedScenes, IReadOnlyList<ScenarioWarning> Warnings)> CompareAsync(int scenarioId);
Task ApplyAsync(int scenarioId);
Task ArchiveAsync(int scenarioId);
}
public sealed class TimelineData
{
public Project? Project { get; init; }
@ -895,6 +908,76 @@ public sealed class DynamicsRepository(ISqlConnectionFactory connectionFactory)
}
}
public sealed class ScenarioRepository(ISqlConnectionFactory connectionFactory) : IScenarioRepository
{
public async Task<IReadOnlyList<Scenario>> ListAsync(int projectId)
{
using var connection = connectionFactory.CreateConnection();
var rows = await connection.QueryAsync<Scenario>("dbo.Scenario_List", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure);
return rows.ToList();
}
public async Task<Scenario?> GetAsync(int scenarioId)
{
using var connection = connectionFactory.CreateConnection();
return await connection.QuerySingleOrDefaultAsync<Scenario>("dbo.Scenario_Get", new { ScenarioID = scenarioId }, commandType: CommandType.StoredProcedure);
}
public async Task<int> CreateAsync(int projectId, int? bookId, string scenarioName, string? description)
{
using var connection = connectionFactory.CreateConnection();
return await connection.QuerySingleAsync<int>(
"dbo.Scenario_Create",
new { ProjectID = projectId, BookID = bookId, ScenarioName = scenarioName, Description = description },
commandType: CommandType.StoredProcedure);
}
public async Task<(IReadOnlyList<Book> Books, IReadOnlyList<Chapter> Chapters, IReadOnlyList<ScenarioScene> Scenes, IReadOnlyList<ScenarioWarning> Warnings)> GetTimelineAsync(int scenarioId)
{
using var connection = connectionFactory.CreateConnection();
using var result = await connection.QueryMultipleAsync("dbo.Scenario_Timeline", new { ScenarioID = scenarioId }, commandType: CommandType.StoredProcedure);
return (
(await result.ReadAsync<Book>()).ToList(),
(await result.ReadAsync<Chapter>()).ToList(),
(await result.ReadAsync<ScenarioScene>()).ToList(),
(await result.ReadAsync<ScenarioWarning>()).ToList());
}
public async Task MoveSceneAsync(int scenarioId, int sceneId, int? anchorSceneId, int? targetChapterId, string position)
{
using var connection = connectionFactory.CreateConnection();
await connection.ExecuteAsync(
"dbo.Scenario_MoveScene",
new { ScenarioID = scenarioId, SceneID = sceneId, AnchorSceneID = anchorSceneId, TargetChapterID = targetChapterId, Position = position },
commandType: CommandType.StoredProcedure);
}
public async Task<int> ValidateAsync(int scenarioId)
{
using var connection = connectionFactory.CreateConnection();
return await connection.QuerySingleAsync<int>("dbo.Scenario_Validate", new { ScenarioID = scenarioId }, commandType: CommandType.StoredProcedure);
}
public async Task<(IReadOnlyList<ScenarioComparisonRow> MovedScenes, IReadOnlyList<ScenarioWarning> Warnings)> CompareAsync(int scenarioId)
{
using var connection = connectionFactory.CreateConnection();
using var result = await connection.QueryMultipleAsync("dbo.Scenario_Compare", new { ScenarioID = scenarioId }, commandType: CommandType.StoredProcedure);
return ((await result.ReadAsync<ScenarioComparisonRow>()).ToList(), (await result.ReadAsync<ScenarioWarning>()).ToList());
}
public async Task ApplyAsync(int scenarioId)
{
using var connection = connectionFactory.CreateConnection();
await connection.ExecuteAsync("dbo.Scenario_ApplyToMain", new { ScenarioID = scenarioId }, commandType: CommandType.StoredProcedure);
}
public async Task ArchiveAsync(int scenarioId)
{
using var connection = connectionFactory.CreateConnection();
await connection.ExecuteAsync("dbo.Scenario_Archive", new { ScenarioID = scenarioId }, commandType: CommandType.StoredProcedure);
}
}
public sealed class SceneDependencyRepository(ISqlConnectionFactory connectionFactory) : ISceneDependencyRepository
{
public async Task<SceneDependencyLookupData> GetLookupsAsync()

View File

@ -1426,6 +1426,73 @@ public sealed class DynamicsIssue
public string? EntityType { get; set; }
}
public sealed class Scenario
{
public int ScenarioID { get; set; }
public int ProjectID { get; set; }
public int? BookID { get; set; }
public string ScenarioName { get; set; } = string.Empty;
public string? Description { get; set; }
public DateTime BaseCreatedDate { get; set; }
public bool IsAppliedToMain { get; set; }
public bool IsArchived { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime UpdatedDate { get; set; }
public string ProjectName { get; set; } = string.Empty;
public string? BookTitle { get; set; }
public int SceneCount { get; set; }
public int WarningCount { get; set; }
}
public sealed class ScenarioScene
{
public int ScenarioSceneOrderID { get; set; }
public int ScenarioID { get; set; }
public int SceneID { get; set; }
public int OriginalChapterID { get; set; }
public int ProposedChapterID { get; set; }
public int ProposedSortOrder { get; set; }
public string? Notes { get; set; }
public decimal ChapterNumber { get; set; }
public string ChapterTitle { get; set; } = string.Empty;
public decimal SceneNumber { get; set; }
public string SceneTitle { get; set; } = string.Empty;
public string? Summary { get; set; }
public string RevisionStatusName { get; set; } = string.Empty;
public string TimeLabel { get; set; } = string.Empty;
public string? ThreadLabels { get; set; }
public string? AssetLabels { get; set; }
public string? CharacterLabels { get; set; }
public string? LocationLabel { get; set; }
public int WarningCount { get; set; }
}
public sealed class ScenarioWarning
{
public int ScenarioWarningID { get; set; }
public int ScenarioValidationRunID { get; set; }
public int ScenarioID { get; set; }
public int? SceneID { get; set; }
public string EntityType { get; set; } = string.Empty;
public int? EntityID { get; set; }
public string WarningTypeName { get; set; } = string.Empty;
public string SeverityName { get; set; } = "Info";
public string Message { get; set; } = string.Empty;
public string? Details { get; set; }
public DateTime CreatedDate { get; set; }
}
public sealed class ScenarioComparisonRow
{
public int SceneID { get; set; }
public string SceneTitle { get; set; } = string.Empty;
public string MainPosition { get; set; } = string.Empty;
public string ScenarioPosition { get; set; } = string.Empty;
public int MainIndex { get; set; }
public int ScenarioIndex { get; set; }
public bool HasMoved { get; set; }
}
public sealed class AnalyticsBusyScene
{
public int SceneID { get; set; }

View File

@ -36,6 +36,7 @@ public class Program
builder.Services.AddScoped<IWriterWorkspaceRepository, WriterWorkspaceRepository>();
builder.Services.AddScoped<IExportRepository, ExportRepository>();
builder.Services.AddScoped<IDynamicsRepository, DynamicsRepository>();
builder.Services.AddScoped<IScenarioRepository, ScenarioRepository>();
builder.Services.AddScoped<IProjectService, ProjectService>();
builder.Services.AddScoped<IBookService, BookService>();
builder.Services.AddScoped<IChapterService, ChapterService>();
@ -51,6 +52,7 @@ public class Program
builder.Services.AddScoped<IWriterWorkspaceService, WriterWorkspaceService>();
builder.Services.AddScoped<IExportService, ExportService>();
builder.Services.AddScoped<IDynamicsService, DynamicsService>();
builder.Services.AddScoped<IScenarioService, ScenarioService>();
var app = builder.Build();

View File

@ -187,6 +187,19 @@ public interface IDynamicsService
Task RunContinuityChecksAsync(int projectId, int? bookId, int? sceneId);
}
public interface IScenarioService
{
Task<ScenarioListViewModel?> ListAsync(int projectId);
Task<ScenarioCreateViewModel?> GetCreateAsync(int projectId, int? bookId);
Task<int> CreateAsync(ScenarioCreateViewModel model);
Task<ScenarioTimelineViewModel?> GetTimelineAsync(int scenarioId);
Task MoveSceneAsync(int scenarioId, int sceneId, int? anchorSceneId, int? targetChapterId, string position);
Task<int> ValidateAsync(int scenarioId);
Task<ScenarioCompareViewModel?> CompareAsync(int scenarioId);
Task ApplyAsync(int scenarioId);
Task ArchiveAsync(int scenarioId);
}
public sealed class ProjectService(IProjectRepository projects, IBookRepository books) : IProjectService
{
public Task<IReadOnlyList<Project>> ListAsync() => projects.ListAsync();
@ -2287,6 +2300,108 @@ public sealed class DynamicsService(IProjectRepository projects, IDynamicsReposi
dynamics.RunContinuityChecksAsync(projectId, bookId, sceneId);
}
public sealed class ScenarioService(IProjectRepository projects, IBookRepository books, IScenarioRepository scenarios, IContinuityValidationService validation) : IScenarioService
{
public async Task<ScenarioListViewModel?> ListAsync(int projectId)
{
var project = await projects.GetAsync(projectId);
return project is null ? null : new ScenarioListViewModel
{
Project = project,
Scenarios = await scenarios.ListAsync(projectId)
};
}
public async Task<ScenarioCreateViewModel?> GetCreateAsync(int projectId, int? bookId)
{
var project = await projects.GetAsync(projectId);
if (project is null)
{
return null;
}
return new ScenarioCreateViewModel
{
ProjectID = projectId,
BookID = bookId,
BookOptions = (await books.ListByProjectAsync(projectId))
.Select(x => new SelectListItem($"Book {x.BookNumber}: {x.BookTitle}", x.BookID.ToString(), x.BookID == bookId))
.ToList()
};
}
public Task<int> CreateAsync(ScenarioCreateViewModel model) =>
scenarios.CreateAsync(model.ProjectID, model.BookID, model.ScenarioName, model.Description);
public async Task<ScenarioTimelineViewModel?> GetTimelineAsync(int scenarioId)
{
var scenario = await scenarios.GetAsync(scenarioId);
if (scenario is null)
{
return null;
}
var project = await projects.GetAsync(scenario.ProjectID);
if (project is null)
{
return null;
}
var data = await scenarios.GetTimelineAsync(scenarioId);
return new ScenarioTimelineViewModel
{
Project = project,
Scenario = scenario,
Books = data.Books,
Chapters = data.Chapters,
Scenes = data.Scenes,
Warnings = data.Warnings,
ChapterOptions = data.Chapters.Select(x => new SelectListItem($"Ch {x.ChapterNumber:g}: {x.ChapterTitle}", x.ChapterID.ToString())).ToList()
};
}
public Task MoveSceneAsync(int scenarioId, int sceneId, int? anchorSceneId, int? targetChapterId, string position) =>
scenarios.MoveSceneAsync(scenarioId, sceneId, anchorSceneId, targetChapterId, position);
public Task<int> ValidateAsync(int scenarioId) => scenarios.ValidateAsync(scenarioId);
public async Task<ScenarioCompareViewModel?> CompareAsync(int scenarioId)
{
var scenario = await scenarios.GetAsync(scenarioId);
if (scenario is null)
{
return null;
}
var project = await projects.GetAsync(scenario.ProjectID);
if (project is null)
{
return null;
}
var data = await scenarios.CompareAsync(scenarioId);
return new ScenarioCompareViewModel
{
Project = project,
Scenario = scenario,
MovedScenes = data.MovedScenes,
ScenarioWarnings = data.Warnings
};
}
public async Task ApplyAsync(int scenarioId)
{
var scenario = await scenarios.GetAsync(scenarioId);
await scenarios.ApplyAsync(scenarioId);
if (scenario is not null)
{
await validation.ValidateProjectAsync(scenario.ProjectID);
}
}
public Task ArchiveAsync(int scenarioId) => scenarios.ArchiveAsync(scenarioId);
}
public sealed class AssetService(IProjectRepository projects, IAssetRepository assets, ILocationRepository locations) : IAssetService
{
public async Task<StoryAssetListViewModel?> GetAssetsAsync(int projectId)

View File

@ -0,0 +1,493 @@
SET ANSI_NULLS ON;
GO
SET QUOTED_IDENTIFIER ON;
GO
IF OBJECT_ID(N'dbo.Scenarios', N'U') IS NULL
BEGIN
CREATE TABLE dbo.Scenarios
(
ScenarioID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_Scenarios PRIMARY KEY,
ProjectID int NOT NULL,
BookID int NULL,
ScenarioName nvarchar(200) NOT NULL,
Description nvarchar(max) NULL,
BaseCreatedDate datetime2 NOT NULL CONSTRAINT DF_Scenarios_BaseCreatedDate DEFAULT SYSUTCDATETIME(),
IsAppliedToMain bit NOT NULL CONSTRAINT DF_Scenarios_IsAppliedToMain DEFAULT 0,
IsArchived bit NOT NULL CONSTRAINT DF_Scenarios_IsArchived DEFAULT 0,
CreatedDate datetime2 NOT NULL CONSTRAINT DF_Scenarios_CreatedDate DEFAULT SYSUTCDATETIME(),
UpdatedDate datetime2 NOT NULL CONSTRAINT DF_Scenarios_UpdatedDate DEFAULT SYSUTCDATETIME(),
CONSTRAINT FK_Scenarios_Projects FOREIGN KEY (ProjectID) REFERENCES dbo.Projects(ProjectID),
CONSTRAINT FK_Scenarios_Books FOREIGN KEY (BookID) REFERENCES dbo.Books(BookID)
);
END;
GO
IF OBJECT_ID(N'dbo.ScenarioSceneOrder', N'U') IS NULL
BEGIN
CREATE TABLE dbo.ScenarioSceneOrder
(
ScenarioSceneOrderID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_ScenarioSceneOrder PRIMARY KEY,
ScenarioID int NOT NULL,
SceneID int NOT NULL,
ProposedChapterID int NULL,
ProposedSortOrder int NOT NULL,
Notes nvarchar(max) NULL,
CreatedDate datetime2 NOT NULL CONSTRAINT DF_ScenarioSceneOrder_CreatedDate DEFAULT SYSUTCDATETIME(),
UpdatedDate datetime2 NOT NULL CONSTRAINT DF_ScenarioSceneOrder_UpdatedDate DEFAULT SYSUTCDATETIME(),
CONSTRAINT FK_ScenarioSceneOrder_Scenarios FOREIGN KEY (ScenarioID) REFERENCES dbo.Scenarios(ScenarioID),
CONSTRAINT FK_ScenarioSceneOrder_Scenes FOREIGN KEY (SceneID) REFERENCES dbo.Scenes(SceneID),
CONSTRAINT FK_ScenarioSceneOrder_Chapters FOREIGN KEY (ProposedChapterID) REFERENCES dbo.Chapters(ChapterID),
CONSTRAINT UQ_ScenarioSceneOrder_ScenarioScene UNIQUE (ScenarioID, SceneID)
);
END;
GO
IF OBJECT_ID(N'dbo.ScenarioValidationRuns', N'U') IS NULL
BEGIN
CREATE TABLE dbo.ScenarioValidationRuns
(
ScenarioValidationRunID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_ScenarioValidationRuns PRIMARY KEY,
ScenarioID int NOT NULL,
StartedDate datetime2 NOT NULL CONSTRAINT DF_ScenarioValidationRuns_StartedDate DEFAULT SYSUTCDATETIME(),
CompletedDate datetime2 NULL,
WarningCount int NOT NULL CONSTRAINT DF_ScenarioValidationRuns_WarningCount DEFAULT 0,
CONSTRAINT FK_ScenarioValidationRuns_Scenarios FOREIGN KEY (ScenarioID) REFERENCES dbo.Scenarios(ScenarioID)
);
END;
GO
IF OBJECT_ID(N'dbo.ScenarioWarnings', N'U') IS NULL
BEGIN
CREATE TABLE dbo.ScenarioWarnings
(
ScenarioWarningID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_ScenarioWarnings PRIMARY KEY,
ScenarioValidationRunID int NOT NULL,
ScenarioID int NOT NULL,
SceneID int NULL,
EntityType nvarchar(80) NOT NULL,
EntityID int NULL,
WarningTypeName nvarchar(150) NOT NULL,
SeverityName nvarchar(50) NOT NULL,
Message nvarchar(500) NOT NULL,
Details nvarchar(max) NULL,
CreatedDate datetime2 NOT NULL CONSTRAINT DF_ScenarioWarnings_CreatedDate DEFAULT SYSUTCDATETIME(),
CONSTRAINT FK_ScenarioWarnings_Runs FOREIGN KEY (ScenarioValidationRunID) REFERENCES dbo.ScenarioValidationRuns(ScenarioValidationRunID),
CONSTRAINT FK_ScenarioWarnings_Scenarios FOREIGN KEY (ScenarioID) REFERENCES dbo.Scenarios(ScenarioID),
CONSTRAINT FK_ScenarioWarnings_Scenes FOREIGN KEY (SceneID) REFERENCES dbo.Scenes(SceneID)
);
END;
GO
CREATE OR ALTER PROCEDURE dbo.Scenario_List
@ProjectID int
AS
BEGIN
SET NOCOUNT ON;
SELECT sc.ScenarioID, sc.ProjectID, sc.BookID, sc.ScenarioName, sc.Description, sc.BaseCreatedDate,
sc.IsAppliedToMain, sc.IsArchived, sc.CreatedDate, sc.UpdatedDate,
p.ProjectName, b.BookTitle,
COUNT(DISTINCT sso.SceneID) AS SceneCount,
COUNT(DISTINCT sw.ScenarioWarningID) AS WarningCount
FROM dbo.Scenarios sc
INNER JOIN dbo.Projects p ON p.ProjectID = sc.ProjectID
LEFT JOIN dbo.Books b ON b.BookID = sc.BookID
LEFT JOIN dbo.ScenarioSceneOrder sso ON sso.ScenarioID = sc.ScenarioID
LEFT JOIN dbo.ScenarioWarnings sw ON sw.ScenarioID = sc.ScenarioID
WHERE sc.ProjectID = @ProjectID AND sc.IsArchived = 0
GROUP BY sc.ScenarioID, sc.ProjectID, sc.BookID, sc.ScenarioName, sc.Description, sc.BaseCreatedDate,
sc.IsAppliedToMain, sc.IsArchived, sc.CreatedDate, sc.UpdatedDate, p.ProjectName, b.BookTitle
ORDER BY sc.UpdatedDate DESC, sc.ScenarioName;
END;
GO
CREATE OR ALTER PROCEDURE dbo.Scenario_Get
@ScenarioID int
AS
BEGIN
SET NOCOUNT ON;
SELECT sc.ScenarioID, sc.ProjectID, sc.BookID, sc.ScenarioName, sc.Description, sc.BaseCreatedDate,
sc.IsAppliedToMain, sc.IsArchived, sc.CreatedDate, sc.UpdatedDate,
p.ProjectName, b.BookTitle,
(SELECT COUNT(*) FROM dbo.ScenarioSceneOrder WHERE ScenarioID = sc.ScenarioID) AS SceneCount,
(SELECT COUNT(*) FROM dbo.ScenarioWarnings WHERE ScenarioID = sc.ScenarioID) AS WarningCount
FROM dbo.Scenarios sc
INNER JOIN dbo.Projects p ON p.ProjectID = sc.ProjectID
LEFT JOIN dbo.Books b ON b.BookID = sc.BookID
WHERE sc.ScenarioID = @ScenarioID AND sc.IsArchived = 0;
END;
GO
CREATE OR ALTER PROCEDURE dbo.Scenario_Create
@ProjectID int,
@BookID int = NULL,
@ScenarioName nvarchar(200),
@Description nvarchar(max) = NULL
AS
BEGIN
SET NOCOUNT ON;
INSERT dbo.Scenarios (ProjectID, BookID, ScenarioName, Description)
VALUES (@ProjectID, @BookID, @ScenarioName, @Description);
DECLARE @ScenarioID int = CAST(SCOPE_IDENTITY() AS int);
INSERT dbo.ScenarioSceneOrder (ScenarioID, SceneID, ProposedChapterID, ProposedSortOrder)
SELECT @ScenarioID, nso.SceneID, nso.ChapterID,
ROW_NUMBER() OVER (ORDER BY nso.GlobalSceneIndex) * 10
FROM dbo.NarrativeSceneOrder nso
WHERE nso.ProjectID = @ProjectID AND (@BookID IS NULL OR nso.BookID = @BookID)
ORDER BY nso.GlobalSceneIndex;
SELECT @ScenarioID;
END;
GO
CREATE OR ALTER PROCEDURE dbo.Scenario_Timeline
@ScenarioID int
AS
BEGIN
SET NOCOUNT ON;
DECLARE @ProjectID int = (SELECT ProjectID FROM dbo.Scenarios WHERE ScenarioID = @ScenarioID);
DECLARE @BookID int = (SELECT BookID FROM dbo.Scenarios WHERE ScenarioID = @ScenarioID);
SELECT BookID, ProjectID, BookTitle, BookNumber, Description, SortOrder, CreatedDate, UpdatedDate, IsArchived
FROM dbo.Books
WHERE ProjectID = @ProjectID AND IsArchived = 0 AND (@BookID IS NULL OR BookID = @BookID)
ORDER BY SortOrder, BookNumber;
SELECT c.ChapterID, c.BookID, c.ChapterNumber, c.ChapterTitle, c.POVCharacterID, c.Summary, c.SortOrder,
c.RevisionStatusID, rs.StatusName AS RevisionStatusName, c.CreatedDate, c.UpdatedDate, c.IsArchived
FROM dbo.Chapters c
INNER JOIN dbo.Books b ON b.BookID = c.BookID
INNER JOIN dbo.RevisionStatuses rs ON rs.RevisionStatusID = c.RevisionStatusID
WHERE b.ProjectID = @ProjectID AND c.IsArchived = 0 AND b.IsArchived = 0 AND (@BookID IS NULL OR b.BookID = @BookID)
ORDER BY b.SortOrder, c.SortOrder, c.ChapterNumber;
SELECT sso.ScenarioSceneOrderID, sso.ScenarioID, sso.SceneID, s.ChapterID AS OriginalChapterID,
ISNULL(sso.ProposedChapterID, s.ChapterID) AS ProposedChapterID, sso.ProposedSortOrder, sso.Notes,
ch.ChapterNumber, ch.ChapterTitle, s.SceneNumber, s.SceneTitle, s.Summary, rs.StatusName AS RevisionStatusName,
COALESCE(NULLIF(s.RelativeTimeText, N''), CONVERT(nvarchar(20), s.StartDateTime, 106), tm.TimeModeName) AS TimeLabel,
threadData.ThreadLabels, assetData.AssetLabels, characterData.CharacterLabels, loc.LocationPath AS LocationLabel,
(SELECT COUNT(*) FROM dbo.ScenarioWarnings sw WHERE sw.ScenarioID = @ScenarioID AND sw.SceneID = s.SceneID) AS WarningCount
FROM dbo.ScenarioSceneOrder sso
INNER JOIN dbo.Scenes s ON s.SceneID = sso.SceneID
INNER JOIN dbo.Chapters ch ON ch.ChapterID = ISNULL(sso.ProposedChapterID, s.ChapterID)
INNER JOIN dbo.RevisionStatuses rs ON rs.RevisionStatusID = s.RevisionStatusID
INNER JOIN dbo.TimeModes tm ON tm.TimeModeID = s.TimeModeID
LEFT JOIN dbo.LocationPaths loc ON loc.LocationID = s.PrimaryLocationID
OUTER APPLY
(
SELECT STRING_AGG(CONVERT(nvarchar(max), pt.ThreadTitle), N', ') AS ThreadLabels
FROM dbo.ThreadEvents te INNER JOIN dbo.PlotThreads pt ON pt.PlotThreadID = te.PlotThreadID
WHERE te.SceneID = s.SceneID
) threadData
OUTER APPLY
(
SELECT STRING_AGG(CONVERT(nvarchar(max), sa.AssetName), N', ') AS AssetLabels
FROM dbo.AssetEvents ae INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = ae.StoryAssetID
WHERE ae.SceneID = s.SceneID
) assetData
OUTER APPLY
(
SELECT STRING_AGG(CONVERT(nvarchar(max), c.CharacterName), N', ') AS CharacterLabels
FROM dbo.SceneCharacters sc INNER JOIN dbo.Characters c ON c.CharacterID = sc.CharacterID
WHERE sc.SceneID = s.SceneID
) characterData
WHERE sso.ScenarioID = @ScenarioID
ORDER BY ch.SortOrder, ch.ChapterNumber, sso.ProposedSortOrder, s.SceneID;
SELECT ScenarioWarningID, ScenarioValidationRunID, ScenarioID, SceneID, EntityType, EntityID, WarningTypeName, SeverityName, Message, Details, CreatedDate
FROM dbo.ScenarioWarnings
WHERE ScenarioID = @ScenarioID
ORDER BY CASE SeverityName WHEN N'Critical' THEN 4 WHEN N'Warning' THEN 3 WHEN N'Info' THEN 2 ELSE 1 END DESC, CreatedDate DESC;
END;
GO
CREATE OR ALTER PROCEDURE dbo.Scenario_MoveScene
@ScenarioID int,
@SceneID int,
@AnchorSceneID int = NULL,
@TargetChapterID int = NULL,
@Position nvarchar(20)
AS
BEGIN
SET NOCOUNT ON;
DECLARE @ChapterID int;
DECLARE @NewSortOrder int;
DECLARE @AnchorSortOrder int;
IF @AnchorSceneID IS NOT NULL
BEGIN
SELECT @ChapterID = ISNULL(ProposedChapterID, s.ChapterID), @AnchorSortOrder = ProposedSortOrder
FROM dbo.ScenarioSceneOrder sso
INNER JOIN dbo.Scenes s ON s.SceneID = sso.SceneID
WHERE sso.ScenarioID = @ScenarioID AND sso.SceneID = @AnchorSceneID;
IF @Position = N'Before'
SELECT @NewSortOrder = ISNULL(MAX(ProposedSortOrder), @AnchorSortOrder - 20) + ((@AnchorSortOrder - ISNULL(MAX(ProposedSortOrder), @AnchorSortOrder - 20)) / 2)
FROM dbo.ScenarioSceneOrder
WHERE ScenarioID = @ScenarioID AND ISNULL(ProposedChapterID, 0) = ISNULL(@ChapterID, 0) AND ProposedSortOrder < @AnchorSortOrder;
ELSE
SELECT @NewSortOrder = (@AnchorSortOrder + ISNULL(MIN(ProposedSortOrder), @AnchorSortOrder + 20)) / 2
FROM dbo.ScenarioSceneOrder
WHERE ScenarioID = @ScenarioID AND ISNULL(ProposedChapterID, 0) = ISNULL(@ChapterID, 0) AND ProposedSortOrder > @AnchorSortOrder;
END
ELSE
BEGIN
SET @ChapterID = @TargetChapterID;
SELECT @NewSortOrder = ISNULL(MAX(ProposedSortOrder), 0) + 10
FROM dbo.ScenarioSceneOrder
WHERE ScenarioID = @ScenarioID AND ProposedChapterID = @ChapterID;
END;
UPDATE dbo.ScenarioSceneOrder
SET ProposedChapterID = @ChapterID,
ProposedSortOrder = ISNULL(@NewSortOrder, ProposedSortOrder),
UpdatedDate = SYSUTCDATETIME()
WHERE ScenarioID = @ScenarioID AND SceneID = @SceneID;
;WITH ordered AS
(
SELECT ScenarioSceneOrderID,
ROW_NUMBER() OVER (PARTITION BY ProposedChapterID ORDER BY ProposedSortOrder, ScenarioSceneOrderID) * 10 AS NextSortOrder
FROM dbo.ScenarioSceneOrder
WHERE ScenarioID = @ScenarioID
)
UPDATE sso
SET ProposedSortOrder = ordered.NextSortOrder,
UpdatedDate = SYSUTCDATETIME()
FROM dbo.ScenarioSceneOrder sso
INNER JOIN ordered ON ordered.ScenarioSceneOrderID = sso.ScenarioSceneOrderID;
UPDATE dbo.Scenarios SET UpdatedDate = SYSUTCDATETIME() WHERE ScenarioID = @ScenarioID;
END;
GO
CREATE OR ALTER PROCEDURE dbo.Scenario_Validate
@ScenarioID int
AS
BEGIN
SET NOCOUNT ON;
DELETE FROM dbo.ScenarioWarnings WHERE ScenarioID = @ScenarioID;
INSERT dbo.ScenarioValidationRuns (ScenarioID)
VALUES (@ScenarioID);
DECLARE @RunID int = CAST(SCOPE_IDENTITY() AS int);
;WITH ScenarioOrder AS
(
SELECT sso.SceneID, sso.ScenarioID, ISNULL(sso.ProposedChapterID, s.ChapterID) AS ChapterID,
ch.BookID, ROW_NUMBER() OVER (ORDER BY b.SortOrder, ch.SortOrder, sso.ProposedSortOrder, s.SceneID) AS ScenarioIndex,
s.SceneTitle, s.StartDateTime
FROM dbo.ScenarioSceneOrder sso
INNER JOIN dbo.Scenes s ON s.SceneID = sso.SceneID
INNER JOIN dbo.Chapters ch ON ch.ChapterID = ISNULL(sso.ProposedChapterID, s.ChapterID)
INNER JOIN dbo.Books b ON b.BookID = ch.BookID
WHERE sso.ScenarioID = @ScenarioID
)
INSERT dbo.ScenarioWarnings (ScenarioValidationRunID, ScenarioID, SceneID, EntityType, EntityID, WarningTypeName, SeverityName, Message, Details)
SELECT @RunID, @ScenarioID, targetScene.SceneID, N'SceneDependency', sd.SceneDependencyID, N'Scene Dependency Broken', N'Warning',
N'Scenario warning: a required scene dependency is out of order.',
CONCAT(sourceScene.SceneTitle, N' should occur before ', targetScene.SceneTitle, N'.')
FROM dbo.SceneDependencies sd
INNER JOIN dbo.SceneDependencyTypes sdt ON sdt.SceneDependencyTypeID = sd.SceneDependencyTypeID
INNER JOIN ScenarioOrder sourceScene ON sourceScene.SceneID = sd.SourceSceneID
INNER JOIN ScenarioOrder targetScene ON targetScene.SceneID = sd.TargetSceneID
WHERE sd.IsArchived = 0 AND sdt.TypeName = N'Must Occur Before' AND sourceScene.ScenarioIndex >= targetScene.ScenarioIndex;
;WITH ScenarioOrder AS
(
SELECT sso.SceneID, sso.ScenarioID, ROW_NUMBER() OVER (ORDER BY b.SortOrder, ch.SortOrder, sso.ProposedSortOrder, s.SceneID) AS ScenarioIndex,
s.SceneTitle, s.StartDateTime
FROM dbo.ScenarioSceneOrder sso
INNER JOIN dbo.Scenes s ON s.SceneID = sso.SceneID
INNER JOIN dbo.Chapters ch ON ch.ChapterID = ISNULL(sso.ProposedChapterID, s.ChapterID)
INNER JOIN dbo.Books b ON b.BookID = ch.BookID
WHERE sso.ScenarioID = @ScenarioID
)
INSERT dbo.ScenarioWarnings (ScenarioValidationRunID, ScenarioID, SceneID, EntityType, EntityID, WarningTypeName, SeverityName, Message, Details)
SELECT @RunID, @ScenarioID, later.SceneID, N'Scene', later.SceneID, N'Date / Order Conflict', N'Info',
N'Scenario warning: an exact scene date appears earlier than a previous scenario scene.',
CONCAT(later.SceneTitle, N' is dated before ', earlier.SceneTitle, N'.')
FROM ScenarioOrder earlier
INNER JOIN ScenarioOrder later ON later.ScenarioIndex = earlier.ScenarioIndex + 1
WHERE earlier.StartDateTime IS NOT NULL AND later.StartDateTime IS NOT NULL AND later.StartDateTime < earlier.StartDateTime;
;WITH ScenarioOrder AS
(
SELECT sso.SceneID, ROW_NUMBER() OVER (ORDER BY b.SortOrder, ch.SortOrder, sso.ProposedSortOrder, s.SceneID) AS ScenarioIndex
FROM dbo.ScenarioSceneOrder sso
INNER JOIN dbo.Scenes s ON s.SceneID = sso.SceneID
INNER JOIN dbo.Chapters ch ON ch.ChapterID = ISNULL(sso.ProposedChapterID, s.ChapterID)
INNER JOIN dbo.Books b ON b.BookID = ch.BookID
WHERE sso.ScenarioID = @ScenarioID
),
ThreadEventsOrdered AS
(
SELECT te.PlotThreadID, te.ThreadEventID, te.SceneID, pt.ThreadTitle, tet.TypeName,
so.ScenarioIndex,
MIN(CASE WHEN tet.TypeName LIKE N'%Setup%' THEN so.ScenarioIndex END) OVER (PARTITION BY te.PlotThreadID) AS FirstSetupIndex
FROM dbo.ThreadEvents te
INNER JOIN dbo.PlotThreads pt ON pt.PlotThreadID = te.PlotThreadID
INNER JOIN dbo.ThreadEventTypes tet ON tet.ThreadEventTypeID = te.EventTypeID
INNER JOIN ScenarioOrder so ON so.SceneID = te.SceneID
)
INSERT dbo.ScenarioWarnings (ScenarioValidationRunID, ScenarioID, SceneID, EntityType, EntityID, WarningTypeName, SeverityName, Message, Details)
SELECT @RunID, @ScenarioID, SceneID, N'PlotThread', PlotThreadID, N'Plot Thread Order', N'Warning',
N'Scenario warning: a reveal or payoff may occur before its setup.',
CONCAT(ThreadTitle, N' has ', TypeName, N' before the first setup beat.')
FROM ThreadEventsOrdered
WHERE (TypeName LIKE N'%Reveal%' OR TypeName LIKE N'%Payoff%')
AND FirstSetupIndex IS NOT NULL AND ScenarioIndex < FirstSetupIndex;
;WITH ScenarioOrder AS
(
SELECT sso.SceneID, ROW_NUMBER() OVER (ORDER BY b.SortOrder, ch.SortOrder, sso.ProposedSortOrder, s.SceneID) AS ScenarioIndex
FROM dbo.ScenarioSceneOrder sso
INNER JOIN dbo.Scenes s ON s.SceneID = sso.SceneID
INNER JOIN dbo.Chapters ch ON ch.ChapterID = ISNULL(sso.ProposedChapterID, s.ChapterID)
INNER JOIN dbo.Books b ON b.BookID = ch.BookID
WHERE sso.ScenarioID = @ScenarioID
),
AssetFirstSeen AS
(
SELECT ae.StoryAssetID, MIN(so.ScenarioIndex) AS FirstSeenIndex
FROM dbo.AssetEvents ae
INNER JOIN ScenarioOrder so ON so.SceneID = ae.SceneID
GROUP BY ae.StoryAssetID
)
INSERT dbo.ScenarioWarnings (ScenarioValidationRunID, ScenarioID, SceneID, EntityType, EntityID, WarningTypeName, SeverityName, Message, Details)
SELECT @RunID, @ScenarioID, NULL, N'AssetDependency', ad.AssetDependencyID, N'Asset Dependency Order', N'Info',
N'Scenario warning: an asset appears before a validating dependency source appears.',
CONCAT(target.AssetName, N' appears before ', source.AssetName, N'.')
FROM dbo.AssetDependencies ad
INNER JOIN dbo.AssetDependencyTypes adt ON adt.AssetDependencyTypeID = ad.AssetDependencyTypeID
INNER JOIN dbo.StoryAssets source ON source.StoryAssetID = ad.SourceAssetID
INNER JOIN dbo.StoryAssets target ON target.StoryAssetID = ad.TargetAssetID
INNER JOIN AssetFirstSeen sourceSeen ON sourceSeen.StoryAssetID = ad.SourceAssetID
INNER JOIN AssetFirstSeen targetSeen ON targetSeen.StoryAssetID = ad.TargetAssetID
WHERE ad.IsArchived = 0 AND adt.IsValidatingDependency = 1 AND targetSeen.FirstSeenIndex < sourceSeen.FirstSeenIndex;
;WITH ScenarioOrder AS
(
SELECT sso.SceneID, ROW_NUMBER() OVER (ORDER BY b.SortOrder, ch.SortOrder, sso.ProposedSortOrder, s.SceneID) AS ScenarioIndex
FROM dbo.ScenarioSceneOrder sso
INNER JOIN dbo.Scenes s ON s.SceneID = sso.SceneID
INNER JOIN dbo.Chapters ch ON ch.ChapterID = ISNULL(sso.ProposedChapterID, s.ChapterID)
INNER JOIN dbo.Books b ON b.BookID = ch.BookID
WHERE sso.ScenarioID = @ScenarioID
),
MetricSteps AS
(
SELECT smv.SceneID, smt.MetricName, smv.Value, so.ScenarioIndex,
LAG(smv.Value) OVER (PARTITION BY smt.MetricName ORDER BY so.ScenarioIndex) AS PreviousValue
FROM dbo.SceneMetricValues smv
INNER JOIN dbo.SceneMetricTypes smt ON smt.MetricTypeID = smv.MetricTypeID
INNER JOIN ScenarioOrder so ON so.SceneID = smv.SceneID
WHERE smt.MetricName IN (N'Overall Intensity', N'Tension', N'Emotional Weight')
)
INSERT dbo.ScenarioWarnings (ScenarioValidationRunID, ScenarioID, SceneID, EntityType, EntityID, WarningTypeName, SeverityName, Message, Details)
SELECT @RunID, @ScenarioID, SceneID, N'Scene', SceneID, N'Pacing Jump', N'Info',
N'Scenario warning: a pacing/emotional metric changes sharply after this move.',
CONCAT(MetricName, N' changes from ', PreviousValue, N' to ', Value, N'.')
FROM MetricSteps
WHERE PreviousValue IS NOT NULL AND ABS(Value - PreviousValue) >= 6;
DECLARE @WarningCount int = (SELECT COUNT(*) FROM dbo.ScenarioWarnings WHERE ScenarioValidationRunID = @RunID);
UPDATE dbo.ScenarioValidationRuns SET CompletedDate = SYSUTCDATETIME(), WarningCount = @WarningCount WHERE ScenarioValidationRunID = @RunID;
SELECT @WarningCount;
END;
GO
CREATE OR ALTER PROCEDURE dbo.Scenario_Compare
@ScenarioID int
AS
BEGIN
SET NOCOUNT ON;
;WITH MainOrder AS
(
SELECT nso.SceneID, nso.GlobalSceneIndex,
CONCAT(nso.BookTitle, N' / Ch ', nso.ChapterNumber, N' / Scene ', nso.SceneNumber) AS MainPosition
FROM dbo.NarrativeSceneOrder nso
INNER JOIN dbo.ScenarioSceneOrder sso ON sso.SceneID = nso.SceneID AND sso.ScenarioID = @ScenarioID
),
ScenarioOrder AS
(
SELECT sso.SceneID,
ROW_NUMBER() OVER (ORDER BY b.SortOrder, ch.SortOrder, sso.ProposedSortOrder, s.SceneID) AS ScenarioIndex,
CONCAT(b.BookTitle, N' / Ch ', ch.ChapterNumber, N' / Scene ', s.SceneNumber) AS ScenarioPosition,
s.SceneTitle
FROM dbo.ScenarioSceneOrder sso
INNER JOIN dbo.Scenes s ON s.SceneID = sso.SceneID
INNER JOIN dbo.Chapters ch ON ch.ChapterID = ISNULL(sso.ProposedChapterID, s.ChapterID)
INNER JOIN dbo.Books b ON b.BookID = ch.BookID
WHERE sso.ScenarioID = @ScenarioID
)
SELECT so.SceneID, so.SceneTitle, mo.MainPosition, so.ScenarioPosition,
mo.GlobalSceneIndex AS MainIndex, so.ScenarioIndex, CAST(CASE WHEN mo.GlobalSceneIndex <> so.ScenarioIndex OR mo.MainPosition <> so.ScenarioPosition THEN 1 ELSE 0 END AS bit) AS HasMoved
FROM ScenarioOrder so
INNER JOIN MainOrder mo ON mo.SceneID = so.SceneID
WHERE mo.GlobalSceneIndex <> so.ScenarioIndex OR mo.MainPosition <> so.ScenarioPosition
ORDER BY so.ScenarioIndex;
SELECT ScenarioWarningID, ScenarioValidationRunID, ScenarioID, SceneID, EntityType, EntityID, WarningTypeName, SeverityName, Message, Details, CreatedDate
FROM dbo.ScenarioWarnings
WHERE ScenarioID = @ScenarioID
ORDER BY CASE SeverityName WHEN N'Critical' THEN 4 WHEN N'Warning' THEN 3 WHEN N'Info' THEN 2 ELSE 1 END DESC, CreatedDate DESC;
END;
GO
CREATE OR ALTER PROCEDURE dbo.Scenario_ApplyToMain
@ScenarioID int
AS
BEGIN
SET NOCOUNT ON;
UPDATE s
SET ChapterID = ISNULL(sso.ProposedChapterID, s.ChapterID),
SortOrder = sso.ProposedSortOrder,
UpdatedDate = SYSUTCDATETIME()
FROM dbo.Scenes s
INNER JOIN dbo.ScenarioSceneOrder sso ON sso.SceneID = s.SceneID
WHERE sso.ScenarioID = @ScenarioID;
;WITH affected AS
(
SELECT DISTINCT ChapterID FROM dbo.Scenes s INNER JOIN dbo.ScenarioSceneOrder sso ON sso.SceneID = s.SceneID WHERE sso.ScenarioID = @ScenarioID
),
ordered AS
(
SELECT s.SceneID, ROW_NUMBER() OVER (PARTITION BY s.ChapterID ORDER BY s.SortOrder, s.SceneID) * 10 AS NextSortOrder
FROM dbo.Scenes s
INNER JOIN affected a ON a.ChapterID = s.ChapterID
WHERE s.IsArchived = 0
)
UPDATE s
SET SortOrder = ordered.NextSortOrder,
UpdatedDate = SYSUTCDATETIME()
FROM dbo.Scenes s
INNER JOIN ordered ON ordered.SceneID = s.SceneID;
UPDATE dbo.Scenarios
SET IsAppliedToMain = 1,
UpdatedDate = SYSUTCDATETIME()
WHERE ScenarioID = @ScenarioID;
END;
GO
CREATE OR ALTER PROCEDURE dbo.Scenario_Archive
@ScenarioID int
AS
BEGIN
SET NOCOUNT ON;
UPDATE dbo.Scenarios SET IsArchived = 1, UpdatedDate = SYSUTCDATETIME() WHERE ScenarioID = @ScenarioID;
END;
GO

View File

@ -317,6 +317,40 @@ public sealed class StoryDynamicsViewModel
public IReadOnlyList<DynamicsIssue> Issues { get; set; } = [];
}
public sealed class ScenarioListViewModel
{
public Project Project { get; set; } = new();
public IReadOnlyList<Scenario> Scenarios { get; set; } = [];
}
public sealed class ScenarioCreateViewModel
{
public int ProjectID { get; set; }
public int? BookID { get; set; }
public string ScenarioName { get; set; } = string.Empty;
public string? Description { get; set; }
public IReadOnlyList<SelectListItem> BookOptions { get; set; } = [];
}
public sealed class ScenarioTimelineViewModel
{
public Project Project { get; set; } = new();
public Scenario Scenario { get; set; } = new();
public IReadOnlyList<Book> Books { get; set; } = [];
public IReadOnlyList<Chapter> Chapters { get; set; } = [];
public IReadOnlyList<ScenarioScene> Scenes { get; set; } = [];
public IReadOnlyList<ScenarioWarning> Warnings { get; set; } = [];
public IReadOnlyList<SelectListItem> ChapterOptions { get; set; } = [];
}
public sealed class ScenarioCompareViewModel
{
public Project Project { get; set; } = new();
public Scenario Scenario { get; set; } = new();
public IReadOnlyList<ScenarioComparisonRow> MovedScenes { get; set; } = [];
public IReadOnlyList<ScenarioWarning> ScenarioWarnings { get; set; } = [];
}
public sealed class TimelineViewModel
{
public Project Project { get; set; } = new();

View File

@ -15,6 +15,7 @@
</div>
<nav class="context-nav" aria-label="Project navigation">
<a asp-controller="Timeline" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID">Timeline</a>
<a asp-controller="Scenarios" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID">Scenarios</a>
<a asp-controller="Writer" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID">Writer Workspace</a>
<a asp-controller="StoryBible" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID">Story Bible</a>
<a asp-controller="Analytics" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID">Story health</a>
@ -39,6 +40,7 @@
<div class="button-row">
<a class="btn btn-outline-secondary" asp-action="Edit" asp-route-id="@Model.Project.ProjectID">Edit project</a>
<a class="btn btn-outline-primary" asp-controller="Timeline" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID">Timeline</a>
<a class="btn btn-outline-primary" asp-controller="Scenarios" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID">Scenarios</a>
<a class="btn btn-outline-primary" asp-controller="Writer" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID">Writer Workspace</a>
<a class="btn btn-outline-primary" asp-controller="StoryBible" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID">Story Bible</a>
<a class="btn btn-outline-primary" asp-controller="PlotLines" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID">Plot lines</a>

View File

@ -0,0 +1,90 @@
@model ScenarioCompareViewModel
@{
ViewData["Title"] = "Compare Scenario";
}
<section class="scenario-banner">
<div>
<p class="eyebrow">Scenario Comparison</p>
<h1>@Model.Scenario.ScenarioName</h1>
<p>Review moved scenes and sandbox warnings before applying anything to the main order.</p>
</div>
<div class="button-row">
<a class="btn btn-outline-secondary" asp-action="Timeline" asp-route-id="@Model.Scenario.ScenarioID">Back to scenario</a>
<a class="btn btn-outline-secondary" asp-controller="Timeline" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID" asp-route-bookId="@Model.Scenario.BookID">Main timeline</a>
</div>
</section>
@if (TempData["ScenarioMessage"] is string message)
{
<div class="alert alert-warning">@message</div>
}
<section class="story-bible-section">
<div class="story-bible-section-heading">
<div>
<p class="eyebrow">Moved scenes</p>
<h2>Main vs Scenario</h2>
</div>
<span class="soft-count">@Model.MovedScenes.Count moved</span>
</div>
@if (!Model.MovedScenes.Any())
{
<p class="muted">No scene movements yet.</p>
}
else
{
<div class="scenario-compare-list">
@foreach (var row in Model.MovedScenes)
{
<article>
<strong>@row.SceneTitle</strong>
<span>Main: @row.MainPosition</span>
<span>Scenario: @row.ScenarioPosition</span>
</article>
}
</div>
}
</section>
<section class="story-bible-section">
<div class="story-bible-section-heading">
<div>
<p class="eyebrow">Warnings added</p>
<h2>Scenario Warnings</h2>
</div>
<span class="soft-count">@Model.ScenarioWarnings.Count</span>
</div>
@if (!Model.ScenarioWarnings.Any())
{
<p class="muted">No scenario warnings currently recorded.</p>
}
else
{
<div class="attention-list">
@foreach (var warning in Model.ScenarioWarnings)
{
<article>
<span class="badge text-bg-light">@warning.SeverityName</span>
<strong>@warning.WarningTypeName</strong>
<p>@warning.Message</p>
@if (!string.IsNullOrWhiteSpace(warning.Details))
{
<p class="muted">@warning.Details</p>
}
</article>
}
</div>
}
</section>
<section class="story-bible-section danger-zone">
<h2>Apply Scenario To Main</h2>
<p class="muted">This updates the real scene chapter/order using the scenario order. Linked story data stays attached to the same scenes.</p>
<form asp-action="Apply" method="post" class="plotline-form">
<input type="hidden" name="id" value="@Model.Scenario.ScenarioID" />
<label class="form-label">Confirmation text</label>
<input name="confirmation" class="form-control" value="" placeholder="This will replace the current scene order with the scenario order. Continue?" />
<button class="btn btn-danger mt-2" type="submit">Apply Scenario</button>
</form>
</section>

View File

@ -0,0 +1,37 @@
@model ScenarioCreateViewModel
@{
ViewData["Title"] = "Create Scenario";
}
<div class="page-heading">
<div>
<p class="eyebrow">Scenario Sandbox</p>
<h1>Create Scenario</h1>
<p class="lead-text">Copies the current scene order into a sandbox. Scenes and story data are not duplicated.</p>
</div>
</div>
<section class="story-bible-section">
<form asp-action="Create" method="post" class="plotline-form">
<input type="hidden" asp-for="ProjectID" />
<div class="mb-3">
<label asp-for="ScenarioName" class="form-label">Scenario name</label>
<input asp-for="ScenarioName" class="form-control" placeholder="Reveal Later Draft" />
<span asp-validation-for="ScenarioName" class="text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="BookID" class="form-label">Scope</label>
<select asp-for="BookID" asp-items="Model.BookOptions" class="form-select">
<option value="">Whole project</option>
</select>
</div>
<div class="mb-3">
<label asp-for="Description" class="form-label">Description</label>
<textarea asp-for="Description" class="form-control" rows="4"></textarea>
</div>
<div class="button-row">
<button class="btn btn-primary" type="submit">Create Scenario</button>
<a class="btn btn-outline-secondary" asp-action="Index" asp-route-projectId="@Model.ProjectID">Cancel</a>
</div>
</form>
</section>

View File

@ -0,0 +1,67 @@
@model ScenarioListViewModel
@{
ViewData["Title"] = "Scenarios";
}
<section class="context-header">
<div class="context-title">
<span class="eyebrow">Scenario Sandbox</span>
<strong>@Model.Project.ProjectName</strong>
</div>
<nav class="context-nav" aria-label="Workspace navigation">
<a asp-controller="Projects" asp-action="Details" asp-route-id="@Model.Project.ProjectID">Project</a>
<a asp-controller="Timeline" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID">Main timeline</a>
<a asp-controller="Analytics" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID">Story health</a>
</nav>
</section>
<div class="page-heading">
<div>
<p class="eyebrow">Planning sandbox</p>
<h1>Scenarios</h1>
<p class="lead-text">Test structural changes without changing the main story order.</p>
</div>
<a class="btn btn-primary" asp-action="Create" asp-route-projectId="@Model.Project.ProjectID">Create Scenario</a>
</div>
<section class="story-bible-section">
@if (!Model.Scenarios.Any())
{
<p class="muted">No scenarios yet. Create one from the current order to test a structural idea.</p>
}
else
{
<div class="story-bible-grid story-bible-grid-compact">
@foreach (var scenario in Model.Scenarios)
{
<article class="story-bible-card scenario-card">
<div class="story-bible-card-title">
<h3>@scenario.ScenarioName</h3>
@if (scenario.IsAppliedToMain)
{
<span>Applied</span>
}
</div>
<p class="muted">@scenario.Description</p>
<div class="story-bible-meta">
<span>@scenario.SceneCount scenes</span>
<span>@scenario.WarningCount warnings</span>
@if (!string.IsNullOrWhiteSpace(scenario.BookTitle))
{
<span>@scenario.BookTitle</span>
}
</div>
<div class="button-row mt-2">
<a class="btn btn-outline-primary btn-sm" asp-action="Timeline" asp-route-id="@scenario.ScenarioID">Open timeline</a>
<a class="btn btn-outline-secondary btn-sm" asp-action="Compare" asp-route-id="@scenario.ScenarioID">Compare</a>
<form asp-action="Archive" method="post">
<input type="hidden" name="id" value="@scenario.ScenarioID" />
<input type="hidden" name="projectId" value="@Model.Project.ProjectID" />
<button class="btn btn-outline-danger btn-sm" type="submit">Archive</button>
</form>
</div>
</article>
}
</div>
}
</section>

View File

@ -0,0 +1,152 @@
@model ScenarioTimelineViewModel
@{
ViewData["Title"] = $"{Model.Scenario.ScenarioName} Scenario";
ViewData["ShellClass"] = "plotline-shell-wide";
var scenesByChapter = Model.Scenes.GroupBy(x => x.ProposedChapterID).ToDictionary(x => x.Key, x => x.OrderBy(s => s.ProposedSortOrder).ToList());
var orderedScenes = Model.Scenes.OrderBy(x => x.ProposedSortOrder).ToList();
}
<section class="scenario-banner">
<div>
<p class="eyebrow">Scenario Mode</p>
<h1>@Model.Scenario.ScenarioName</h1>
<p>@Model.Scenario.Description</p>
</div>
<div class="button-row">
<a class="btn btn-outline-secondary" asp-controller="Timeline" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID" asp-route-bookId="@Model.Scenario.BookID">Return to Main Timeline</a>
<form asp-action="Validate" method="post">
<input type="hidden" name="id" value="@Model.Scenario.ScenarioID" />
<button class="btn btn-outline-primary" type="submit">Validate Scenario</button>
</form>
<a class="btn btn-outline-primary" asp-action="Compare" asp-route-id="@Model.Scenario.ScenarioID">Compare</a>
<form asp-action="Archive" method="post">
<input type="hidden" name="id" value="@Model.Scenario.ScenarioID" />
<input type="hidden" name="projectId" value="@Model.Project.ProjectID" />
<button class="btn btn-outline-danger" type="submit">Archive Scenario</button>
</form>
</div>
</section>
@if (TempData["ScenarioMessage"] is string message)
{
<div class="alert alert-info">@message</div>
}
<section class="scenario-warning-strip">
<strong>@Model.Warnings.Count scenario warning@(Model.Warnings.Count == 1 ? "" : "s")</strong>
<span>These warnings belong to the sandbox and do not affect the main warning board.</span>
</section>
<section class="scenario-board">
<div class="scenario-scroll">
@foreach (var chapter in Model.Chapters)
{
var chapterScenes = scenesByChapter.GetValueOrDefault(chapter.ChapterID, []);
if (!chapterScenes.Any())
{
continue;
}
<article class="scenario-chapter">
<header>
<span>Chapter @chapter.ChapterNumber:g</span>
<strong>@chapter.ChapterTitle</strong>
</header>
<div class="scenario-scene-row">
@foreach (var scene in chapterScenes)
{
var previous = orderedScenes.TakeWhile(x => x.SceneID != scene.SceneID).LastOrDefault();
var next = orderedScenes.SkipWhile(x => x.SceneID != scene.SceneID).Skip(1).FirstOrDefault();
<article class="scenario-scene-card">
<div class="scenario-scene-title">
<span>Scene @scene.SceneNumber:g</span>
<strong>@scene.SceneTitle</strong>
</div>
<div class="story-bible-meta">
<span>@scene.RevisionStatusName</span>
<span>@scene.TimeLabel</span>
@if (scene.WarningCount > 0)
{
<span class="text-danger">@scene.WarningCount warning@(scene.WarningCount == 1 ? "" : "s")</span>
}
</div>
<p>@scene.Summary</p>
<div class="scenario-mini-data">
@if (!string.IsNullOrWhiteSpace(scene.ThreadLabels)) { <span>Threads: @scene.ThreadLabels</span> }
@if (!string.IsNullOrWhiteSpace(scene.AssetLabels)) { <span>Assets: @scene.AssetLabels</span> }
@if (!string.IsNullOrWhiteSpace(scene.CharacterLabels)) { <span>Characters: @scene.CharacterLabels</span> }
@if (!string.IsNullOrWhiteSpace(scene.LocationLabel)) { <span>Location: @scene.LocationLabel</span> }
</div>
<div class="scenario-move-actions">
@if (previous is not null)
{
<form asp-action="MoveScene" method="post">
<input type="hidden" name="scenarioId" value="@Model.Scenario.ScenarioID" />
<input type="hidden" name="sceneId" value="@scene.SceneID" />
<input type="hidden" name="anchorSceneId" value="@previous.SceneID" />
<input type="hidden" name="position" value="Before" />
<button class="btn btn-outline-secondary btn-sm" type="submit">Move up</button>
</form>
}
@if (next is not null)
{
<form asp-action="MoveScene" method="post">
<input type="hidden" name="scenarioId" value="@Model.Scenario.ScenarioID" />
<input type="hidden" name="sceneId" value="@scene.SceneID" />
<input type="hidden" name="anchorSceneId" value="@next.SceneID" />
<input type="hidden" name="position" value="After" />
<button class="btn btn-outline-secondary btn-sm" type="submit">Move down</button>
</form>
}
</div>
<form asp-action="MoveScene" method="post" class="scenario-chapter-form">
<input type="hidden" name="scenarioId" value="@Model.Scenario.ScenarioID" />
<input type="hidden" name="sceneId" value="@scene.SceneID" />
<input type="hidden" name="position" value="End" />
<select name="targetChapterId" class="form-select form-select-sm">
@foreach (var chapterOption in Model.Chapters)
{
<option value="@chapterOption.ChapterID" selected="@(chapterOption.ChapterID == scene.ProposedChapterID)">Ch @chapterOption.ChapterNumber:g</option>
}
</select>
<button class="btn btn-outline-primary btn-sm" type="submit">Move to chapter</button>
</form>
<a asp-controller="Scenes" asp-action="Edit" asp-route-id="@scene.SceneID">Open scene</a>
</article>
}
</div>
</article>
}
</div>
</section>
<section class="story-bible-section">
<div class="story-bible-section-heading">
<div>
<p class="eyebrow">Scenario warnings</p>
<h2>Validation Notes</h2>
</div>
<span class="soft-count">@Model.Warnings.Count</span>
</div>
@if (!Model.Warnings.Any())
{
<p class="muted">Run scenario validation to check this order.</p>
}
else
{
<div class="attention-list">
@foreach (var warning in Model.Warnings)
{
<article>
<span class="badge text-bg-light">Scenario warning</span>
<strong>@warning.WarningTypeName</strong>
<p>@warning.Message</p>
@if (!string.IsNullOrWhiteSpace(warning.Details))
{
<p class="muted">@warning.Details</p>
}
</article>
}
</div>
}
</section>

View File

@ -36,6 +36,7 @@
<nav class="context-nav" aria-label="Workspace navigation">
<a asp-controller="Projects" asp-action="Details" asp-route-id="@Model.Project.ProjectID">Project</a>
<a asp-controller="Timeline" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID" asp-route-bookId="@Model.SelectedBookID">Timeline</a>
<a asp-controller="Scenarios" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID">Scenarios</a>
<a asp-controller="Analytics" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID" asp-route-bookId="@Model.SelectedBookID">Story health</a>
<a asp-controller="Warnings" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID" asp-route-bookId="@Model.SelectedBookID">Warnings</a>
<a asp-controller="Characters" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID">Characters</a>
@ -73,6 +74,7 @@
}
<a class="btn btn-outline-secondary" asp-controller="Warnings" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID" asp-route-bookId="@Model.SelectedBookID">Warnings</a>
<a class="btn btn-outline-secondary" asp-controller="Analytics" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID" asp-route-bookId="@Model.SelectedBookID">Story health</a>
<a class="btn btn-outline-primary" asp-controller="Scenarios" asp-action="Create" asp-route-projectId="@Model.Project.ProjectID" asp-route-bookId="@Model.SelectedBookID">Create Scenario</a>
</div>
</div>

View File

@ -2794,6 +2794,148 @@ body.dragging-location [data-drag-type="location"] {
background: #7c5fb0;
}
.scenario-banner {
display: flex;
justify-content: space-between;
gap: 18px;
align-items: flex-start;
border: 1px solid rgba(103, 80, 164, 0.22);
border-radius: 12px;
padding: 16px;
margin-bottom: 16px;
background: linear-gradient(135deg, rgba(103, 80, 164, 0.10), rgba(255, 255, 255, 0.86));
box-shadow: 0 12px 28px rgba(36, 32, 26, 0.08);
}
.scenario-banner h1 {
margin: 0 0 4px;
}
.scenario-banner p {
margin: 0;
color: var(--plotline-muted);
}
.scenario-card {
border-color: rgba(103, 80, 164, 0.18);
}
.scenario-warning-strip {
display: flex;
gap: 10px;
align-items: center;
border: 1px solid var(--plotline-line);
border-radius: 10px;
padding: 10px 12px;
margin-bottom: 14px;
background: rgba(255, 255, 255, 0.76);
color: var(--plotline-muted);
}
.scenario-warning-strip strong {
color: var(--plotline-ink);
}
.scenario-board {
border: 1px solid var(--plotline-line);
border-radius: 12px;
padding: 12px;
background: rgba(247, 245, 240, 0.74);
overflow: hidden;
margin-bottom: 18px;
}
.scenario-scroll {
display: flex;
gap: 14px;
overflow-x: auto;
padding-bottom: 8px;
}
.scenario-chapter {
min-width: 360px;
max-width: 520px;
border: 1px solid rgba(82, 75, 65, 0.14);
border-radius: 10px;
background: rgba(255, 255, 255, 0.86);
}
.scenario-chapter header {
display: grid;
gap: 2px;
border-bottom: 1px solid var(--plotline-line);
padding: 10px 12px;
}
.scenario-chapter header span {
color: var(--plotline-muted);
font-size: 0.78rem;
font-weight: 800;
text-transform: uppercase;
}
.scenario-scene-row {
display: grid;
gap: 10px;
padding: 10px;
}
.scenario-scene-card {
display: grid;
gap: 8px;
border: 1px solid rgba(82, 75, 65, 0.12);
border-radius: 9px;
padding: 10px;
background: white;
}
.scenario-scene-title {
display: grid;
gap: 2px;
}
.scenario-scene-title span,
.scenario-mini-data {
color: var(--plotline-muted);
font-size: 0.78rem;
}
.scenario-mini-data {
display: grid;
gap: 2px;
}
.scenario-move-actions,
.scenario-chapter-form,
.scenario-compare-list article {
display: flex;
gap: 8px;
flex-wrap: wrap;
align-items: center;
}
.scenario-chapter-form select {
min-width: 160px;
width: auto;
}
.scenario-compare-list {
display: grid;
gap: 10px;
}
.scenario-compare-list article {
justify-content: space-between;
border: 1px solid var(--plotline-line);
border-radius: 9px;
padding: 10px;
background: rgba(255, 255, 255, 0.82);
}
.danger-zone {
border-color: rgba(163, 61, 61, 0.24);
}
@media print {
header,
.breadcrumb-trail,