This commit is contained in:
Nick Beckley 2026-06-01 12:25:47 +01:00
parent 30845f05cd
commit 8f56934dd8
70 changed files with 2105 additions and 174 deletions

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSU227cNhCGX0Xg9VLmSTzozrBb1HXTLGKjLXpHcYa2EC21oKhNA2PfPaB2i8S9650w0D+Hb/7hG0n+gKQnt8fjTydMZSE7UsYtJJjQlGnK+DNnvZQ9t63uDBNC/012ZHzEr6QnRoXAg0MqUSNVwDo6yBgoADrOODDRqZrTvyykfyN+bAFPY8B2Xv7AvIxzIj35c0wwf6m1/diGaV6hzfOED2kpPoXazO8Pd4/0t9v988f95a8FlypuRyA9iTrGTmpNbScsVcwaOoho6DAohUoxGzm/yMZUMCc/tQt8/l4f5pKwhF60QraMCkfOOwK++Nry4Bd8/nqsXWyI7mt8t4W3z/6NnDCTXuz+hXlJdxOm8abMxwlPOB19XjDfhPlw8AnIjhzzfMRcRtywbJNOky/jnJprsbtpxFTIjnx8avaTL3HOh3esni4IHv4HgRPmoTYI6AHBerColJMySofaCG+t1aDc4Dkz0oKxAyDnbhARTZBBdBGCgrrQl2ke/PTrsvFzgg0WmBFaoEInlNYuMJQWY2eQgdd60DY4kNYIZcFK77znbkATnA413wcfXseEzcM96QnjKEBapFFwTZUFT30nHdVa6og8OCPqNHdzKmNa53VpHlLBl7zxIz352U8LXsjd5vA6FgxlzRXqX7oW2+cZ1lCa7w7grGWtZKyK1nJcS/MJYcwYCla8z3m95vuPRGixie7WnDGVZu/La/OLX14rFW8D966OqkOI2DGMDJjXzABq0AhKgAHlFGjlWNDKD1yB7/gQa4TsCFbDNZvHuZYQ3RBp6LSmSntBrdNIBaDq9NBxofg7js3Hqep8sEwhSOowAlWOe+qUlBRk543Q0RgRf0ByNR9nNRng6XKtWwcawCJoToXiHVXOcWpdZ+hg7OAGbbELtedHzAmnH0B9GEOelzmW5urd5h26T2uqL06z+fjLmOg/247u5/AZc1N37MdUD+y61vOOHNAva8bD9mD1b2Rfr6t53t4tYdrOcb4jT8Xnsh6vYa3artPqfD6fvwEAAP//AwAyyd9e9QQAAA==

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSUXW/bNhSG/4rAa1EhKYoSdZc5K2akbdzGa4fdUeShQ0QhDYpKFwT+7wNlB7Fb7GJ3whHf8/Gcl3xFXj0B6tH1fv/7M/g0oRIlt4QYYQITgQndUtLXdU9lxQQRrZB/oxK5W3hBPWq51lRLwDUIwNyQBg+11dgYkJRQQ1jDc061m1D/ipSrDDw7DVWYvkGcXPCoR9+dN+FHrq1cpccwmyqGEdZ+Ssrr3Mzn9eoWf7zebO82x1MTTFlcOYN6ZIW1TS0E7hrWYU66Fg/MtngYOAfOSWcpPcqcTxC9GqvJPL7XNyF5SLpnFasrgplEhxIZlVRueVATbF/2uYsF0U2Ol0t4+exf0TNE1LPyDeYx3ZUe3dXTNMxuNFdj2O2c362Ct243R5Vy4RLtY9hDTA4WOL85r+LLx7Db5XzogxonQCXaQnxyXo3HHzdg1Tym+zDHhcxkHv/rDOqRmlNAJVpIjuNStjgNsxod+PSL9qzw3X2xGVWyIT5dLOn+yH79P9B/UvrBeSjWN6hHhAIzdQfYMiow74zCqqklFqIWFqiWLcuaVfDJ+TnMU7H2CXYnahf9XUf94BLoNMc8018im20Tg5l1Kt4XTElFqpqQLJrTfk7FVzAugk6Qh9jG+ZTvJwkTbBF9cCP8gmc1xwg+FRuVHoo/1PSAeiRVp6mSSioltLbQELDEECVIa0AYAYYz0xouuRFcEi24Gig3qqGDzRFUIsgmKxZfQwbUDRoTagfMBwVYUc4wpQOta8qoge4CbnE3Zp3SHeFgaizBGswlVVjyusamblTLhG1bZs84bY+GoCRTPzfhnxOYk5c+H639NruB5+M9XvoUxnRgBMWM0wZzKSnuZNPioe0GOYgOGp0nWwU/hTeQ3yAOYXIpPyJfZgfZiRcH3vdyC9HDeLabT07HMAWbipMpi4ttfZ19fsOKxaA/nMf/LLa4CfoRYpFtpZw/W+XhcDj8CwAA//8DAEHbGBYKBQAA

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACoxUTW/bOBD9KwLPpkJSFPUB7MFwWqyRpjESd3exN4ocxkRk0qAop0GR/76gLNdO0cPehNF8vHnvDX8gJ/eAWrQ8HD4dwcUBLVC0U4gRJjARmNAtJW1RtLTJeVUwRot/0QLZO3hDLaq4UlQ1gAsQgLkmJe4Ko7DW0FBCNWElTz3l84DaH0jaXMPRKsj98BeEwXqHWvS3ddq/ptnS5qr3o86D72HthiidSmC+rld3+Mtys33YnLIGGFJxbjVqkRHGlIUQuC5ZjTmpK9wxU+Gu4xw4J7Wh9FRmXYTgZJ8P+uUyX/voIKqW5azICWYNel8gLaNMkDs5wPbtkFBMFN2m+GIKT5/tD3SEgFq2OJN5anejenuzH7rR9vomyvAM0QS5h1cfXuAoe7RAh+APEKKFiZuHp+wCiZKc5EwwQtAC3Uu1sw6y9W320KeFpaoJB13gBozGvKESN7wosC5KWTFhqooZtEB3EBz0V13vrQp+8CZmM+XZhzkr76J1ox+HbO0iPAcZT3WfZT8AWqDN2PV22D2C1G9b/zimf27s0y6rMQRwMdvIuMv+lMMOtaiRtaKykY2UQikDJQFDNJGCVBqEFqA505XmDdeCN0QJLjvKtSxpZ1IELdDDGA9jPPP//QrENtj9HvQFwXbieNPLaHzYrzW4aI1N0pwzJkP1/bRVNvdc9RZc/EAyahGhwHRRAzaMCsxrLbEsiwYLUQgDVDUVS45aeWfs8/iTJ2KM5oUp61p0TUcJMFPVnFSmI1qZQjW67KBiFalqqBtFSFdyWneNIhpoyXXaLng9qvirFYpJoMfRpevM1mntV+vwd8EvjGyCj175Pglme3h6GyLsf6fayu8PfrAx7W9mab8NcO9d+jvf/8zZXLz08RKDdAfZdHpV09GqqAtMea0wp5zgDiRg01W16CQpjGRogW69eoGQJYNJ6yZNzp56eMqWQe1sBBXHkCb/My11kvPz+WQufORfP21XPsDycFjM0T+OiaRTs7MBPrwr34aUvvND2iKG8cpHT9CbM64rN2k4nl4qm4JC6xq0oJhxWmLeNBTXTVnhrqq7phM1lOri1uwRtA2g4tRve5o2E/s7Wy5DtEaqOKTb+eLV5KbZnnPKryCvVPtZfRp+pdzspLkTJcmxT6d3czLQ/3o239/f3/8DAAD//wMAbueSACcGAAA=

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSUUW/kJhDHv4rF8+IDjDH4LUpadZVes7qL2qovqzEMWRoHbzHO9RTtdz/h3SiX3lPf7NH8h5kf/+GFRHhC0pOr4/GnZ4x5JhuSwxoSTCjKFGX8nrO+aXrBaiV4K1TzF9mQcItfSU86aS23BmmDCql0rKVD4y11Dg1n3DHRylITHmbSvxAItcPnYLGe5t8xzWGKpCd/hOimL+VsCLUdp8XVaRpxG+cM0ZZmftte39Jfr3b3d7tz1oxzEdfBkZ545X3bKEV1KzSVTHd0EL6jwyAlSsm05/wsCzFjijDWs3t8O99NOWK2vahFUzMqDDltiIMMpeUBZrz/eixdrIhuSnyzhtfP/oU8YyK92LzCPJf7YMfwIS2RbMgxTUdMOeDKYB1rHCGHKVaXytdjwJjPqX+jzft1LqFtZ9xgUMrOOgRsG2eZE5I1qN3APZfSNmi0ZUbyllnPtADvRavB6wHJhtx9rnYjZD+lp3egP5/5bf8Hvo9gDyFitb0hPWEchWs0Ui+4olI7oNA2hirVKI/cmk4UzfUUc4jLtMzVNmZ8SOvUpCc/wzhf+rtK9hAy2rykguJPVQwzwhLtYX9Mkw8j7sO8d+hhGXPp96LdpcktNldvF8lZzeqGsVJ4ycclV5/QhYQ2Yxn0Pi2XM/8jEUqsouslJYy52kE+VL/AfCA9MaAtBwMGQFnrsWXomWOgWOdQOYVOCtc5aaRT0jCrJAxcOmj54EuEbAgW31TrlQLnjbGaU+OlotILoKbVgg5M2qHpwBgQ71hXd+Oqs5pJdA016B2VhgM1smmoa1rohPJdJ3yx+PG4z2dLvTqpGNV9R+viOM74j5gT/rPgfIZ14b1fznKHz+fNXcdQzml0ilMheUulMZxq03Z06PRgBqWxtWXwW0wRx+9ofww2TfPkc3WxYvWO/6clltenWm35JUT672qGm8k+YqqKmSDEsmwX/5w25AlhXhI+rY9X/0KOYB/hoYziMWG0uLfTEvO6n7N7fP3jb6v2QyY7nU6nbwAAAP//AwCS5D12GwUAAA==

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRUXW/cKBT9KxbPxuHL2PgtmrTaUbabUTvqrvYNw6VBcWAEOG0UzX9f4ZlVm6fVviB0zbmce87BbyjoZ0ATuj2dPrxAKBm1qPitxAiTmEhM6JGSifOJkY4MsidU/o1a5O/hFU1oEMZQowBzkICFJT2euTPYWlCUUEtYL2pP/S2j6Q1p31l48Qa6mL9Cyj4GNKE/fbDxe71b+84scbVdigvsQy46mErmj/3uHv9+ezg+HC6nMuQK7rxFE3LSuZ5LiceejViQccAzcwOeZyFACDI6Si8wHwqkoJcu26ef99tYAhQzsY7xjmCm0LlFVhddKc86w/H1VFlsEt3VeruVt+30hl4goYm1/4p5aXdjFn/znOfVL/bmshadn/K2Om1KTK+oRacUT5CKh02f25zheV5ejzo/fbycqdv84QeYtYDdxTUUNCHKe9SiTaBl0cXH0Fw57hYPoWwfS/Ihe/NfvThq0cOX5rDo4mJ6fufHl4vM+/+h8idtHn2AZn+HJkQoMMtHwI5RicVoNdY9V1hKLh1QowZWMbsYig9rXHOzDwW+pW0kNKGPeslw4XebzKMvYMqa6px/yZqrQ4p2NaX56SUlHek4IRW0ltNams9gfQJToA5xTOu139f3ECbZBtqtKUEozUGXx+Y3nR/RhJQeDdVKK62lMQ56Ao5YoiUZLEgrwQpmByuUsFIoYqTQMxVW93R2tYJaBDU6zZbWQTDKBykwJUZjocYeK8MUZvMsCFdumPn4TsfmYak4bUYiwHKswFksFNVYCc6x5b0emHTDwNwvklzzQEkV2MLL5d1tDKS1I1hJMRO0x0IpikfVD3gexlnNcoTeVM73kAIsvwj1yZsUc3SluSakeSfd5zXUf0ezpeW7D/jH5tFdNE+Qmuqx9qE+laut5/P5/A8AAAD//wMAG9hLKYIEAAA=

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRTTW/UMBD9K5HP69R2HMfOrdqCWBXoClaAuDmeMbWaOivHKaBq/ztydhGUGxfLGvnNvI/xM4n2EUlPro/HV08Y80w2JIe1JJhQlCnK+IGzvml6wWrWGcl185VsSLjFn6QnnXSOO4O0QYVUAmvp0HhHAdBwxoGJVpae9ttM+mdiQw34FBzW0/wJ0xymSHryOUSYvpfZNtRunBao0zTiLs7ZRlfIvN9tb+nb6/3hbn9+NeNcwHUA0hOvvG8bpahuhaaS6Y4Ownd0GKREKZn2nJ9hIWZM0Y71DA9/5sOUI2bXi1o0NaPCkNOGgM22UB7sjIefx8Jiteim1Ddreb32z+QJE+nF5reZ53ZXbgxXj/OwhBGuzme280NReUzTEVMOuHqyyhxHm8MUq8uk7RgwZrIhdx+r/Wizn9LjC6M+nvXv/kP+oUx/9QPdkhG20xIz6QlvNNmQd9bdh4jV7ob0hHEU0GikXnBFpQZLbdsYqlSjPHJnOlHabaeYQ1ymZa52MeO3tAogPXltxxnP1K+Tuw8ZXV5SUfVFlV3YpwkWl6s//nNWs7phrICWfFxy9QEhJHQZi75DWi79/oEIJVbQdkkJY672Nt9Xb+x8T3pirHbcGmusVc55bBl6Bswq1gEqUAhSQAfSSFDSMKekHbgE2/LBlwrZECxxV+uGWSs19y3QwXtBZQOcGq49VUwyrTvmwbz0sbobV5zTTCI01KAHKg231MimodC0thPKd53wf1lySZ+zYjDg0/mvrAwUgEZQnArJWyqN4VSbtqNDpwczKI2tK5xvMUUc/zLqXXBpmiefq8vyVC+s+7DE8t+rdZG+h0h/rBndTO4BU1UytiGW9b7EejqdTr8AAAD//wMAMdT3xzYEAAA=

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACoxU227cNhD9FYGvFRXeRJF6c20HXaSOjeyiLfo2Ikc2a5laUJSTwPC/F9TauaJAXwRhSM45c+bMPJEID0h6cnY8Xj5izAupSQ5bSDChKdOU8QNnvZS9YA0zQlut/iY1Ce/wM+lJp5zjziKVqJEqz1o6yNFR79Fyxj0TrSo54XYh/ROB0Hh8DA6befkD0xLmSHryZ4h+/liwITRumlffpHnCXVwyRFfIvN+dv6O/n90crm9OtxZcyuMmeNKTUY9jK7WmphWGKmY6Ooixo8OgFCrFzMj56VmIGVOEqVn8/Vd8P+eI2fWiEbJhVFjyXBMPGQrlARY8fD4WFptEFyVeb+Htt38ij5hIL+pXMU/p3rgpvHlYhjVM/s32JTU5pvmIKQfc1LjeV19JcNawRmjBGKnJFbi7ELHaXVTXUykRnGEKvaQWR0+V5UCtkpJ62UIn9Nh1YiQ1eYcp4vRN1qvg0rzMY65eRK6+w9mfnV9GGCYsGG9hWpDUZLfsM0QP0xzx8hO6NZ9yHdJajs/nmENc53WpdjHjbYKX89f352tKGHN1A/mu+g2WO9ITC8ZxsGABtHMjtgxH5hlo1nnUXqNXwndeWeW1ssxpBQNXHlo+jCVCavJr0XC/OofL8pXNZpJp2jhUL306nwLG/J2MpCeMo/DSIB0F11QZDxRaaanWUo/Ine0Ef4U5QLrFXKxlhmFgzg1CWe5AWJB+ZNLr0QrRdaBG2XZKaN2yDpzvOsGVlx06kKNkRpCa3KTZry7/2Gq5NeAmzf+gy0Uq0hPN2tY4GAahFTdgDSquldEWO6tGYEq6AXhrYBg4g5Y5a4QDri2owbfjF5Uu422IeBGW4wSfv8E1jW7kLw7A8BHUD9ffJnjAj3O6f3+ycfP+8kBq8mGNZSNUu2KRjyHST7pM9NU65XC4Swge/dXs8WcfYRmXaptQN9gRteVUm5ZTpUegRrZIbWfkgNJrhyXpxezuMVXFYRBimaovya731VlydyGjy2sq/P7aeFzvq5sJ8jinh+82yTeF/ShAIyRnpTMeH0/baOOovTfoNadC8ZYqazk1tu3o0JnBDtpg64pi12s+rrn6gD4kdHmr+MWLG+b5Hbr7n8V4dcGLRTkrXtufttgm7f9aYs81eUBY1oQP27bun8guRkwb8sV6GsVdvArTFBZ0c/QL6Y3kje20fSH4n9fatuk6K56fn5//BQAA//8DAPehEukZBgAA

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/zQzbBbdOGmXsSLNuiN4gxjIVppQVGbBsb+94LaLRL3lpsw0My888w7fCOTPyLpyP3p9MsZp7yQmuRhCwkmNGWaMn7grJOyU6qR0mlnxN+kJsMTfiMdMSoEHhxSiRqpAtbSXsZAAdBxxoGJVpWa/vNCujfihwbwPARs5uVPTMswT6Qjfw0TzF9Lbz80YZxXaNI84m5asp9CEfPH7uGJ/n6/Pzzvr38tuJTkZgDSEa4VF7wHGnmIVDkhqXVaU40SgnbBeQ/XtGHKmCY/Ngt8+d4f5jxhDp1ohGwYFY5cagI++yK59wsevp2Kig3RY4nXW3j77N7IGRPpRP0fzGu5uzAOd8vaj3jG8eTTgukuzMejn4qWU5pPmPKAG5Yzpn5ehlyIhmC8tZyD9Ua1BqxshTQgGYJlpgUHzmulowhtG6G3zqioQHAFGpXqDanJBm4cfR7mqbppfxgHnDKpyfNLtR99jnM6vkP/ciW6+wmgRXaZF9ADgvVgUSknZZQOtRHeWqtBud5zZqQFY3tAzl0vIpogg2gjBAXFHx98eB0mrHaPpCOMowBpkUbBNVUWPPWtdFRrqSPy4IzgpCYP85SHaZ3XpdpNGT+nbV7SkV/9uOB10vsUXoeMIa+pQPikS7N9mmENufpuAM4a1kjGStKaT2uuPiIMCUPGguOQ1lu9/6UILbakhzUlnHK19/m1+s0vr6QjrTaiVUwF30qOIkQnWGTMOilACQxGAxohQq+UizLKNvQ+9MbGHkH2pkyIxW/VZnEJrQTDDGXQAlXCeGq5c1S5FpmyPUbQ7zhWz2PJ88EyhSCpwwhUOe6pU1JSkK03QkdjRPwByc0snJX2gOfrsW4KNIBF0JwKxVuqnOPUutbQ3tje9dpiGxypyROmCccfQH0YQpqXOebq5rXqHbqP61QenGrz3ddhov9sO3qcwxdMVdmxH6ZyX7e1XmpyRL+sCY/be9W9kX05ruqwPVvCNpwrXZOX7FNeT7ewdI2RQl0ul8u/AAAA//8DANzcjJX0BAAA

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRUTW/cNhT8KwLPS5lfokTd3HWDLpzETrxNit4o8nFNWCYXFOXUMPzfC2rX8G6CHnoTqDdv3psZ8gUF/QioR5f7/e9PEPKEVij75YgRJjGRmNAtJT3nvRB1J6WglPyNVshfwzPqUSuMoUYB5iABC0saPHBnsLWgKKGWsEaUnno3of4FaV9bePIG6jh9gzT5GFCPvvtg44/CrX1txjjbOsURNmHKOpgyzOfN+hp/vLzd3tweqiaYCrj2FvWISkEZHSx21DgsFOO4U1JiCdwaqYzS2h5gPmRIQY/1ZB/e+W3MAbLpWc14TTBT6HWFrM66jDzoCbbP+zLFItFVOV8tx8tn/4KeIKGerd7EPLS7MKO/eJyG2Y/2Yoy7nQ+7dQzO7+akcyFeoX2Ke0jZwyLObz7o9Pwx7nalH/qgxwnQCm0hPfqgx8OPK3B6HvNdnNOizGQf/qsG9UjPOaIVWpQcx4W2Oi6zHj2E/Av2hPjmrroddXYxPZ6ZdHfQfvM/pP+kzb0PUG2uUI8IBWZ5B9gxKrHorMa64QpLyaUDalTLKFqhdQzZhznOU7UJGXZH1c7mu0zm3mcweU5lp79kCdttinY2uXo3mJKa1JyQAprzfs7VV7A+gclQltim+djvJwiTbAF98CP8Is96TglCrm51vq/+0NM96lEjW9YIIoxuOAVmnGLEEdIpzqxgYFppoWXMDEIoxx1vzKDN0HZuAMuHtqwNJWTVkmtonOycpFi2ncCCcIYHRzm2rVWtsta2Q3MmbnUzFpw2HRFgOVbgLBaKaqwE59jyRrdMurZl7kSn7SEQlBT60xD+OYE9ZunzIdpvu1t4OtzjZU5pbQdWUswEbbBQiuJONS0e2m5Qg+ygMepg6BTfhPwGaYiTz+UR+TJ7KEk8K3j35RpSgPHEm0/epDhFl6tjKKszt77Oobxh1RLQHz7gf5ZYXEXzAKkqsdI+nFj5+vr6+i8AAAD//wMA0bGPpgoFAAA=

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSUTW/cNhCG/4rA81Lml/ihm2G3qOumWcRGW/Q2Ioe2EC21oKhNA2P/e0DtFol7600Y6J0ZPvPOvJEEByQ9uT0efzphKgvZkTJuIcGEpkxTxp8566XslWolU9IJ9jfZkfERv5KeGOU99w6pRI1UBdbRQUZPQ0DHGQ9MdKrmhJeF9G8ExjbgafTYzssfmJdxTqQnf44pzF9qbRhbP81raPM84UNaCiRfm/n94e6R/na7f/64v/y14FLF7RhIT7hWXPAh0Mh9pMoJSa3TmmqUwWvnHUC4yMZUMCeY2iV8/l4/zCVh8b1ohWwZFY6cdyRAgdryAAs+fz3WLjZE9zW+28LbZ/9GTphJL3b/wryku/HTeFPm44QnnI6QF8w3fj4cINVejnk+Yi4jbli2l04TlHFOzbXY3TRiKmRHPj41+wlKnPPhHaunC4KH/0HghHmoDQaEgMFCsKiUkzJKh9oIsNbqoNwAnBlpg7FDQM7dICIaL73oYvAq1IG+TPMA06/Lxs8JNtjAjNACFTqhtHaeobQYO4MsgNaDtt4FaY1QNlgJDoC7AY132td8H8C/jgmbh3vSE8ZRBGmRRsE1VTYAhU46qrXUEbl3RnCyI3dzKmNa53VpHlLBl7zxIz35GaYFL+Rus38dC/qy5gr1L12L7fMcVl+a7w7grGWtZKyK1nJcS/MJw5jRF6x4n/N6zfcfidBiE92tOWMqzR7Ka/MLLK+kJ502olNMeegkR+GjEywyZp0UQQn0Rgc0QvhBKRdllJ0fwA/GxgGDHEx9IVbDNZvHQYFTAwTqFTCqBHYUGNeUu6i7LqIG9p5j83HadN4yhUFShzFQ5ThQp6SkQXZghI7GiPgDkqv5OKvlA54u27p1oEOwGDSnQvGOKuc4ta4zdDB2cIO22HlHduQRc8LpB1AfRp/nZY6luXq3eYfu05rqxWk2H38ZE/1nm9H97D9jbuqMYUx1wa5jPe/IAWFZMx62g9W/kX3druZ5u1vCtpwrvSNPBXJZj9ewdK2RQp3P5/M3AAAA//8DAEQ8Blv1BAAA

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACoxU227cNhD9FYGvFRXeRIp6c20HXaSOjeyiLfo2JEc2a5laUJSTwPC/F9LauaJAXwRhyJlzeObMPJEED0h6cnY8Xj5iKjOpSYlbSDChKdOU8QNnvZS9ahttOmG5+ZvUJL7Dz6QnRnnPvUUqUSNVgbXUycHTENByxgMTrVprwu1M+icCsQn4GD020/wH5jlOifTkz5jC9HHFhtj4cVpCk6cRd2kukPxK5v3u/B39/ezmcH1zujXjvCY3MZCecK244C7QgfuBKisk7azWVKMMXltvAcIpLaaCOcHYzOH+K36YSsLie9EI2TAqLHmuSYACK2UHMx4+H1cWm0QXa7zewttv/0QeMZNe1K9insq98WN88zC7JY7hzfYlNTnm6Yi5RNzUuN5XX0lw1rBGaMEYqckV+LuYsNpdVNfj+kTwHVMYJLU4BKosB2qVlDTIFozQgzFiIDV5hznh+E3Vq+jzNE9DqV5Err7D2Z+dXyZwI64Yb2GckdRkN+8LpADjlPDyE/qlnGod8rIen0+pxLRMy1ztUsHbDC/nr/nnS86YSnUD5a76DeY70pNWG9Eqpjy0kqPwgxVsYKyzUgQl0Bsd0AjhnVJ2kINsvQPvTDc4DNIZTmry66rhfvEe5/krm80k47hxqF76dD5GTOU7GUlPGEcRZId0EFxT1QWg0EpLtZZ6QO6tEV9gDpBvsZCeDJ1zjnnvhLLcg7Agw8Bk0IMVwhhQg2yNElq3zIAPxgiugjToQQ6SdYLU5CZPYfHlx1bLrQE3efoHfVmlIj3RrG07D84JrXgHtkPFteq0RWPVAExJ74C3HTjHGbTM20544NqCcqEd7Cv9y3QbE17E+TjC529wu0Y38hcP0PEB1A/X32Z4wI9Tvn9/snHz/vJAavJhSetGqHarRT7GRD/pdaKvlrHEw11GCBiupoA/+wjXcam2CTXMtuC0pO2gJVUDN9SBZNSABoZaCGCO1ORi8veYq9VhENM6VV+KXe+rs+zvYkFflrzy+2vjcb2vbkYow5Qfvtsk3zzsRwEaITlbOxPw8bSNNo46hA6D5lQo3lJlLaedbQ11pnPW6Q5bvyp2vZTjUqoPGGJGX7YXv3hxwzy/Q3//sxivLnixKGer1/anLbZJ+7+W2HNNHhDmJePDtq37J7JLCfOGfLGcRnGXruI4xhn9lMJM+k7wRgshXwj+5zUlG6Vs9/z8/PwvAAAA//8DADP4f00ZBgAA

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRUy27cOBD8FaHPosyXKFE3Y5xgB96sB8kgG+RGka2YsEwNSMqJYfjfF9TMwvFpsReCoFjd1VVFvUAwjwgDXJ9OH54w5AQ1ZL8dccoVoYpQdmR0EGKQbaM6xgTrvkMN/hafYYBOWsusRiJQIZGOtmQUkyXOoWaUOcpbWWqaHwmGFzC+cfjkLTZL+oox+SXAAH/74JafpbfxjZ2X1TVxmXEfUjbBFjJ/7Xe35M/rw/HucL6VMBVw4x0MwJRknI2OTMxORGouSK+VIgqFs0pbbYw7w3zIGIOZm+Qe3vq7JQfMduANFw0lXMNrDc5kUyiPJuHx+VRYbBLdlPN6O962wws8YYSB1/+KeS53ZWd/9ZjG1c/u6rxmkx7Stk7G5iU+Qw2nuJwwZo+bPtcp4eM4Px9Nevh4vlO26cMvtGtGt1vWkMvEooUaNoHm2WS/hOrCcTd7DHn7mKMPydv/qiWghrsv1WE2eVri4zs/vpxl3v8PlT8Ze+8DVvsbGIAy5E70SCbOFJG9M8S0QhOlhJqQWd1xBjXslpB9WJc1VfuQ8UfcRoIBPpo54ZnfdbT3PqPNayxzflMlV4e4uNXm6s1LRhvaCEoLaM2nNVef0fmINmMZ4hjXS72v7yFc8Q20W2PEkKuDyffVHybdwwCt6ngrqbSmFQy5nTSnE6W9FtxJjrZTDjvO7SilnsQkWjsaO3b9NKITY1cmxBKdaktrL0Y3Wm1IK7QjUlBO+l51BC3VveiZQlksedOxupsLztieSnSCaJwckZoZoqUQxInWdFxNXcen3yS55IHR0t7h0/ndbQyUcz06xQiXrCVSa0Z63XZk7PpRj6rH1mqo4RZjwPk3oT55G5e0TLm6JKR6J93nNZR/R7Wl5acP5Nfm0c1iHzBWxWPjQ3kqF1tfX19f/wEAAP//AwD694M2ggQAAA==

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACoxUyW7jOBD9FYFnU+GmFZiD4XRjjHQ6RuKeGcyNSzEmIosGRSUdBPn3AWU5dhp9mJtQquXVe6/4hnq5B9Si5eHw5Rn6OKAFim4KMcJKTEpM6JaSlvNWFDmpalKV7F+0QO4GXlGLKqE11Q1gDiVgYUiBFbcaGwMNJdQQVojUUz4OqH1D0uUGnp2G3A9/QRic71GL/na98S9ptnS57vxo8uA7WPdDlL1OYL6vVzf423KzvdscswYYUnHuDGoRLQVlVBlsqbZYNIzjuilLXAI3umx0I6U5lrk+Quhllw/m6Tzf+NhD1C3LGc8JZg16XyAjo0yQlRxg+3pIKCaKrlN8MYWnz/YNPUNALVucyDy2u9Kdu9oPanSduYoyPEK0Qe7hxYcneJYdWqBD8AcI0cHEzd1DdoZESU5yVjJC0ALdSr1zPWTr6+yuSwtLXRMBhuMGrMGioRI3gnNseCErVtqqYhYt0A2EHrqLrrdOBz94G7OZ8uzTnJXvo+tHPw7Zuo/wGGQ81n2V3QBogTaj6tywuwdpXrf+fkz/+rFLu6zGEKCP2UbGXfanHHaoRUVZsUIQoWXBKTBtG0YsIXXDmREMdFUaqBjTSojGcssLraRWVW0VGK4qihboboyHMZ74/3kBYhvcfg/mjGA7cbzpZLQ+7NcG+uisS9KcMiZDdd20VTb3XHUO+viJZNQiQoEZXgO2jJZY1EZiWfAGlyUvLVDdVCyhW/neusfxgydirRHcFnVdqkZRAsxWtSCVVcRoy3VjCgUVq0hVQ91oQlQhaK0aTQzQQiSTboI3o46/WoFPAt2PfbrObJ3WfnE9/lmKMyOb4KPXvkuCuQ4eXocI+9+ptvL7gx9cTPvbWdofA9z6Pv2d73/mbC5e+niOQbqDbDo9QutCCEWxqlWBRWUKrIxKN2SJpFaQknO0QNdeP0HIksGk6ydNTp66e8iWQe9cBB3HkCb/My11lPPr6WTOfOTfv2xXPsDycFjM0T+eE0nHZicDfHpXfgwpfeeHtEUM44WPHqCzJ1wXbjLwfHypXAqWxtRgSoqZoAUWTUNx3RQVVlWtGlXWUOjmw63ZPRgXQMep3/Y4bSb2d7Zchuis1HFIt/PN68lNsz3nlF9BXqj2UX0cfqHc7KS5EyXJsQ/Hd3My0P96Nt/f39//AwAA//8DADQAEnAnBgAA

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRTS2/UMBD+K5HPcepXHDu3agtiVaCrsgLEzY8JtZo6K8fpQ9X+d+TsogI3LpY18jfzPcavKJoHQD26PBzePULMM6pRDmuJESYxkZjQPSU9571oG9kJSbT8gWoUruEF9agTzlGnAXOQgIUnLbZ8cNh70JRQT1grSk/zc0b9KzKh8fAYHDTT/BXSHKaIevQtRD89ldkmNG6cFt+kaYRtnLOJrpD5vN1c44+Xu/3N7vRqhrmAm+BRj6gUlFHr8UDdgIVmHCstJZbAvZPaaWP8CRZihhTN2Mz+/m2+n3KE7HrWMN4QzDQ61sibbApla2bYvxwKi9Wiq1Kv1/J67V/RIyTUs/q3mad2F24MFw+zXcLoL05nNvN9UXlI0wFSDrB6ssocR5PDFKvzpM0YIGZUo5sv1W40eZjSw19GfTnp3/6H/H2Z/u4Z3JLBb6Yl5oLlCtXok3F3IUK1vUI9IhSY5wrwwKjEQnmDTcs1lpLLAajTHaOoRpsp5hCXaZmrbczwM60CUI/em3GGE/XL5O5CBpeXVFR9l2UXdmnyi8vVm/+UNKThhBTQkg9Lrm7BhwQuQ9G3T8u53z8QJtkK2iwpQczVzuS76oOZ71CPWtmxVhDhTMspMDdoRgZClObMCwaukx46xpwVQg984K2zxtlODRY8t11RCCXuat0wq6yyzigMjA1YUK+w9lRh3jLBtQXlbaH45mN1MxaccYoI8BxrGDwWmhqsBefY89Z0TA5dx4Y/LDmnT0kZ7+Hx9FdWBtJ7BV5SzARtsdCaYqXbDttOWW2lgtZpVKNrSBHGP4z6FFya5mnI1Xl5qr+su11i+e/VukhPIeLnNaOryd1DqkrGJsSy3udYj8fj8RcAAAD//wMAYDGxDjYEAAA=

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSUTW/cNhCG/4rAs6jwS5Som2G36MJNvUiMtuhlQQ2HXtYypVKU08DY/x5Iu4bj5tSbNOA7Hw/f4QuJ9glJR66m6adnjHkmJclhCwkmNGWaMn7PWSdlp3QlhOGNaf4iJQm3+JV0pFEAHAxSiRqpcqymvfRAnUPDGXdM1GrNaR9m0r0QGyqHzwGwGuffMc1hjKQjf4Toxi9rbRsqGMbFVWkccBfnbCOszfy2u76lv17t7+/251Mzzqu4Co50hGvFBe8d9Rw8VUZI2hqtqUbpQBsw1rqzLMSMKdqhmt3jW3035ogZOlEJWTEqDDmVxNls15Z7O+P912ntYkN0s8bLLbx9di/kGRPpRPkK85zuAwzhQ1oiKcmUxglTDrgx2MYaBpvDGItL5ushYMzno38j5MM2l2ihMa43qFQDDi3W0gFzQjGJreu550qBRNMCM4rXDDxrhfVe1K31bY+kJHefi/1gsx/T0zvQn8/8dv8D30cLxxCx2N2QjjCOwskWqRdcU9U6S20tDdVaao8cTCM4Kcn1GHOIy7jMxS5mfEjb1KQjP9thvvR3leAYMkJe0oriT70aZrBLhONhSqMPAx7CfHDo7TJk0hF/0e7T6BbIxdtFclaxSjK2Jl7ytOTiE7qQEDKug96n5VLzPxKhxSa6XlLCmIu9zcfiFzsfSUdq3YhaMQW2lhwFeCOYZ6w1UjglEBrtsBECeqWMl17W0Fvom9b36GTfrBRw9U2xXallvbTgDXWt9lS5VlNbO0elbrzpWb9e9TvWxd2w6aBlCp2kBr2jynBLjZKSOlnbRmjfNMKvFp+mQz5b6tVJq1Hdd7QujuOM/4g54T8LzmdYF96H5Sx3+Hze3G0M7VyLTnMqFK+pMobT1tQN7Zu2N71usQZDSnKLKeLwHe2PAdI4jz4XFysW7/h/WuL6+hSbLb+ESP/dzHAzwiOmYjWTDXFdtot/TiV5QjsvCZ+2x6t7IZOFR/uwjuIxYQQ8wLjEvO3n7B5f//jbqv1wkp1Op9M3AAAA//8DAEDxRpkbBQAA

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRTXW/UMBD8K5Gf49RfceK8VVcQpwI90QoQb4695ixS++Q4pVV1/x05d6gtb7xZK8/s7MzuMwr6HtCALg+Hdw8Q8oxqlP1aYoRJTCQm9I6SgYuB8aYTnWS9+IFq5K/hCQ2oE8ZQowBzkICFJS0euTPYWlCUUEtYKwqn/jmj4Rlp31h48AaaOH+FNPsY0IC++WDj79Jb+8ZMcbFNihNsw5x1MEXM5+3mGn+83N3d7E6/ZpgLuPEWDYhKQRkdLXbUOCwU47hXUmIJ3BqpjNLanmA+ZEhBT81sf730tzEHyGZgDeMNwUyhY42szrpIHvUMd0+HomK16KrU67W8Podn9AAJDaz+a+aJ7sJM/sLE+3sd7IXzwc97VKNDigdI2cNqxzrhNOnsY6jOTTaTh5BRjW5uq92ks4vp/o1Ht6fRt/8x+Sdt9j5Atb1CAyIUmOU9YMeoxKK3GuuWKywllw6oUR2jqEabGLIPS1zmahsy/EyrSjSg93qa4aTvMpm9z2Dykor077JkDY8+b6ItBVyIdinaxeTqxW9KGtJwQgrJkg9Lrr6A9QlMhjLUXVrO/P9AmGQraLOkBCFXO5331Qc979GAWtmxVhBhdMspMOMUI46QXnFmBQPTSQsdY2YUQjnueGtGbcaudyNYPnZFKJR4q3WjBG1BqI5gSjXFQlCLR2dHPBrRgRRC9oa/8bW6mQpOm54IsBwrcBYLRTVWgnNseas7Jl3XMffKknPklJT2Fh5Ot7EqkNb2YCXFTNAWC6Uo7lXb4bHrRzXKHlqjUI2uIQWYXhn1yZsU5+hydd6Y6o11X5ZQ7rtat+e3D/hxzewqml+QqpK59qGs8znm4/F4/AMAAP//AwBbRwxXJgQAAA==

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRUTW/bOBT8KwLPosIvUaJuWWeLNdI2buNtF3ujyCeHiEIaFJVuEPi/Lyg7iN1iD3sTqDdv3psZ8hV5/QSoQ9f7/e/P4NOESpTccsQIk5hITOiWko6LjotK1aJumfwblcjdwgvqUCOMoUYB5iABC0tq3PPBYGtBUUItYbXIPfVuQt0r0q6y8OwMVGH6BnFywaMOfXfehh+ZW7vKjGG2VQwjrP2UtDd5mM/r1S3+eL3Z3m2OVRNMGVw5izpEWMsk1TWGVrRYSAZYi7bGmtp2kIY3qrVHmPMJotdjNdnHd34bkodkOlYxXhHMFDqUyOqk88i9nmD7ss9TLBLd5PNyOV4+u1f0DBF1rHwT89juyozu6mnqZzfaqzHsds7vVsEPbjdHnTJxifYx7CEmB4s4vzmv48vHsNvlfuiDHidAJdpCfHJej8cfNzDoeUz3YY6LMpN9/K8a1CE9p4BKtCg5jgttcVpmNTrw6RfsGfHdfbEZdRpCfLow6f6o/fp/SP9JmwfnoVjfZAwFZnkLeGBUYtFajXXNFZaSywGoUQ2jqESr4JPzc5inYu0T7E6qXcx3Hc2DS2DSHPNOf8kctk0MdjapeDeYkopUnJAMmtN+TsVXsC6CSZCX2Mb51O8nCJNsAX1wI/wiz2qOEXwqNjo9FH/o6QF1qJYNqwURRtecAjODYmQgpFWcWcHANNJCw5jphVADH3htem36ph16sLxv8tqQQ1YsuW6bnnLW11jZAbBg1OC25g1WhA4EgNZCX4pb3I0Zp01LBFiOFQwWC0U1VoJzbHmtGyaHpmHDmU7bYyAoyfTnIfxzAnvK0udjtN92t/B8vMfLnNLaFqykmAlaY6EUxa2qG9w3ba962UJt1NHQKbwJ+Q1iHyaX8iPyZXaQk3hR8O7LLUQP45k3n5yJYQpDKk6hLC7c+jr7/IYVS0B/OI//WWJxE8wjxCLHSjt/ZuXhcDj8CwAA//8DAGAI+UoKBQAA

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSU227cNhCGX0Xg9VLm+aA7w25R102ziI226B3FGdlCtNSCojYNjH33gNotEveud8JA/xy++YdvJIUDko7cHo8/nTCVhexIGbeQYMJQZijjz5x1UnVStdJrzZ39m+zI+IhfSUesipFHj1SiQaqAadrLIVIA9JxxYEKrmjO8LKR7I2FsAU9jxHZe/sC8jHMiHflzTDB/qbXD2MZpXqHN84QPaSkhxdrM7w93j/S32/3zx/3lrwWXKm5HIB1hwgnDg6bolKPKCKRBOU0DBzeYKK13cJGNqWBOYWoX+Py9PswlYYmdaIVsGRWenHcEQgm15T4s+Pz1WLvYEN3X+G4Lb5/dGzlhJp3Y/Qvzku4mTuNNmY8TnnA6hrxgvonz4RBS7eWY5yPmMuKGZZt0mkIZ59Rci91NI6ZCduTjU7OfQhnmfHjH6umC4OF/EDhh7muDgAEQXACHSnkpB+nRWBGccwaU7wNnVjqwrgfk3PdiQBtlFHqAqKAu9GWa+zD9umz8vGC9A2aFEajQC2WMjwylw0FbZBCM6Y2LHqSzQjlwMvgQuO/RRm9izfchxNcxYfNwX6fhKEA6pIPghioHgQYtPTVGmgF59FZwsiN3cypjWud1aR5SwZe88SMd+TlMC17I3eb4OhaMZc0V6l+mFtvnGdZYmu8O4KxlrWSsitZyXEvzCWHMGAtWvM95veb7j0QYsYnu1pwxlWYfymvzS1heSUe0sUIrpmLQkqOIgxdsYMx5KUAJjNYAWiFir5Qf5CB17EPsrRt6BNnbOiFWwzWbxwNoqFug3EpDlWKW9gw0BSkE9zZoJcU7js3HadNFxxSCpB4HoMrzQL2SkoLUwQozWCuGH5BczcdZLQ94ulzr1oEBcAiGU6G4psp7Tp3XlvbW9b43DnX0ZEceMSecfgD1YYx5XuahNFfvNu/QfVpTfXGazcdfxkT/2XZ0P8fPmJu64zCmemDXtZ535IBhWTMetgereyP7el3N8/ZuCdUyy/WOPJWQy3q8hqVtjeL+fD6fvwEAAP//AwAL3gdL9QQAAA==

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSUTW/cNhCG/4rA81Lm94duht2ihpt6ERtt0BvFGcZCtNKCojYNjP3vBbVbJO4tN2GgmXnnmXf4RqZwQNKR2+PxlxNOZSE7UoYtJJgwlBnK+AtnnVSdVK2Smisv/yY7MjziN9IRq2Lk0SOVaJAqYJr2MkUKgJ4zDkxoVWuGzwvp3kgYWsDTELGdlz8xL8M8kY78NUwwf629w9DGcV6hzfOID9NSwhSrmD8e7h7p77f7l6f95a8Fl5rcDkA6woQThgdN0SlHlRFIg3KaBg4umSitd3BJG6aCeQpju8CX7/1hLhOW2IlWyJZR4cl5RyCUUCX3YcGXb8eqYkN0X+O7Lbx9dm/khJl0YvcfzEu5mzgON8vaj3jC8RjygvkmzodDmKqWY56PmMuAG5YT5n5ehlKJxmiDc5yDC1ZpC05qIS1IhuCY1eDBB6NMElHrBL3zViUFgiswqFRvyY5s4MYxlGGemqv2u3HAqZAdeXpu9mMoac6Hd+ifL0QffgJolV3nBQyA4AI4VMpLmaRHY0VwzhlQvg+cWenAuh6Qc9+LhDbKKHSCqKD640OIr8OEzcN97c5RgHRIk+CGKgeBBi09NUaahDx6KzjZkbt5KsO0zuvSPEwFP+dtXtKRX8O44GXS2xxfh4KxrLlC+GRqs32eYY2l+W4AzlrWSsZq0lqOa2k+IgwZY8GK4yWv13r/SxFGbEl3a844lWYfymvzW1heSUe0sUIrpmLQkqOIyQuWGHNeClACozWAVojYK+WTTFLHPsTeutQjyN7WCbH6rdkszrkLyUdHpWOCKsk1dazXNCaRvHEKjO7fcWyexpoXomMKQVKPCajyPFCvpKQgdbDCJGtF+gHJ1Syc1faAp8uxbgoMgEMwnArFNVXec+q8trS3rve9caijJzvyiHnC8QdQH4aY52VOpbl6rXmH7uM61Qen2Xz3dZjoP9uO7uf4BXNTdxyGqd7Xda3nHTlgWNaMh+296t7Ivh5X87I9W0K1zHK9I88l5LIer2FpW6O4P5/P538BAAD//wMAXUY/gfQEAAA=

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACoxUTW/bOBD9KwLPokKRlEQa2IPhtFgjTWMk7u5ibxQ5jInIokFRSYMg/31BWY6dooe9CaP5ePPeG76hXu0BLdDycPjyDH0cUI6im0KU0BqTGpNyW5IF4wtWFSWvqobKf1GO3A28ogVquNalloAZ1IC5IRVumdXYGJAlKQ2hFU891eOAFm9IucLAs9NQ+OEvCIPzPVqgv11v/EuarVyhOz+aIvgO1v0QVa8TmO/r1Q3+ttxs7zbHrAGGVFw4gxaIUEHrUlUYBBeY1xSw4qLCqjTC1po1UphjmesjhF51xWCezvONjz1EvaAFZQXBVKL3HBkVVYLcqgG2r4eEYqLoOsXzKTx9Lt7QMwS0oPmJzGO7K925q/3Qjq4zV1GFR4g2qD28+PAEz6pDOToEf4AQHUzc3D1kZ0glKUhBa0oIytGt0jvXQ7a+zu66tLDSgnAwDEuwBnNZKiw5Y9iwSjW0tk1DLcrRDYQeuouut04HP3gbs5ny7NOcle+j60c/Dtm6j/AYVDzWfVXdAChHm7Ht3LC7B2Vet/5+TP/6sUu7rMYQoI/ZRsVd9qcadmiBqrqhFSdcq4qVQLWVlFhChGTUcAq6qQ00lOqWc2mZZZVulW4bYVswrG1KlKO7MR7GeOL/5wWIbXD7PZgzgu3E8aZT0fqwXxvoo7MuSXPKmAzVddNW2dxz1Tno4yeSk6NKoIYJwJaWNebCKKwqJnFds9pCqWVDE7qV7617HD94ItYazmwlRN3KtiRAbSM4aWxLjLZMS1O10NCGNAKE1IS0FS9FKzUxUFY8mXQTvBl1/NUKbBLofuzTdWbrtPaL6/HPmp8Z2QQfvfZdEsx18PA6RNj/TrWV3x/84GLa387S/hjg1vfp73z/M2dz8dLHcwzSHWTT6cmqrbWgFeYiOVFzi6UFiQ0YyTTRpCUM5eja6ycIWTKYcv2kyclTdw/ZMuidi6DjGNLkf6aljnJ+PZ3MmY/i+5ftygdYHg75HP3jOZF0bHYywKd35ceQ0nd+SFvEMF746AE6e8J14SYDz8eXyqVgbYwAU5eY8rLCXMoSC1k1uG1EK9taQKXlh1uzezAugI5Tv+1x2kzs72y5DNFZpeOQbueb15ObZnvOKb+CvFDto/o4/EK52Ulzp5Ikxz4c383JQP/r2Xx/f3//DwAA//8DACepZYcnBgAA

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRUTW/cNhD9KwLPoswv8UM3Y52gCzf1IlmkRW8UOYoJy9SCpJwYxv73gtotEp+KXghiyDfz5s0j31C0z4AGdHs6fXiBWDJqUQlbiBEmMZGY0CMlAxcD7zslBFHa/I1aFO7hFQ1ICeeoM4A5SMDCkx6PfHLYezCUUE9YL2pO+y2j4Q3Z0Hl4CQ66JX+FlMMS0YD+DNEv32ttGzo3L6vv0jLDPuZio6tk/tjv7vHvt4fjw+FyK0Ou4C54NCDCNJPU9hi00FhIBtgK3WNLvZ6k48pof4GFWCBFO3fZP/2s75cSobiBdYx3BDODzi3ytthKebQZjq+nymKT6K7G2y28bYc39AIJDaz9V8xLuhs3h5vnPK5h9jeXtdj8lLd1sq4s6RW16JSWE6QSYNPnNmd4HufXo81PHy936jZ/+AFuLeB3yxoLGhDlPWrRJtA82xKW2Fw57uYAsWyHJYWYg/uvXBy16OFLc5htmZb0/G4eXy4y7/+Hyp+sewwRmv1dxVBgnmvAE6MSC+0ttj03WEouJ6DOKEZRi3ZLLCGuy5qbfSzwLW0toQF9tHOGC7/b5B5DAVfWVPv8S1ZfHdLiV1ean7OkpCMdJ6SC1nJaS/MZfEjgCtQmjmm95vv6HsIk20C7NSWIpTnY8tj8ZvMjGlAvFesFEc72nAJzk2FkIkQbzrxg4JT0oBhzoxBm4hPv3WjdqPQ0guejqh1CtU6zuZV7TaRmEybKeSy0NXgcqcKCKuJ7KrkY3+vYPMwVZ50mAjzHBiaPhaEWG8E59ry3islJKTb9IsnVD5TU8h5eLu9uYyC91+AlxUzQHgtjKNamV3hUejSj1NA7g1p0DynC/ItQn4JLS16m0lwd0ryT7vMa69/RbG75HiL+sc3obnFPkJo6YxtifSrXsZ7P5/M/AAAA//8DAKDmvtCCBAAA

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACoxU227cNhD9FYGvFRXeRenNtR10kTo2sou26NuQHNqsZWohUU4Cw/9eSGvnigJ9EQRyOOfMmTPzRDI8IOnJ2fF4+Yi5zKQmJW1HgglDmaGMHzjrpeqlblotmBH8b1KT9A4/k560ynvuO6QSDVIVmKZORk9DwI4zHpjQas0JtzPpnwikJuBj8tiM8x84zWnMpCd/phzGjys2pMYP4xKaaRxwl+cC2a9k3u/O39Hfz24O1zenqBnn9XGTAukJE1YYDpqiVZYqI5CCspoCDzYaL9vOhtOzlAtOGYZmDvdf8cNYMhbfi0bIhlHRkeeaBCiwUnYw4+HzcWWxSXSxntfb8fbbP5FHnEgv6lcxT+ne+CG9eZjdkobwZvuSmhyn8YhTSbipcb2vvpLgrGGNMIIxUpMr8HcpY7W7qK6HtUTwlikMknYYA1UdB9opKWmQGlphYtuKSGryDqeMwzdZr5KfxnmMpXoRufoOZ392fpnBDbhivIVhRlKT3bwvkAMMY8bLT+iXcsp1mJb1+nzMJeVlXOZqlwveTvBy//r+fJkmzKW6gXJX/QbzHemJNq3QiikPWnIUPnaCRcZsJ0VQAn1rArZCeKdUF2WU2jvwrrXRYZCu5aQmv64a7hfvcZ6/stlMMgwbh+qlT+dDwly+k3F1CUcRpEUaBTdU2QAUtOyoMdJE5L5rxReYA0y3WEhPonXOMe+dUB33IDqQITIZTOyEaFtQUepWCWM0a8GHthVcBdmiBxkls4LU5GYaw+LLj62WWwNupvEf9GWVivTEMK2tB+eEUdxCZ1Fxo6zpsO1UBKakd8C1Bec4A818Z4UHbjpQLujYvdK/zLcp40WajwN8/gbXNqaRv3gAyyOoH8LfTvCAH8fp/v3Jxs37ywOpyYclrxuh2q0W+Zgy/WTWib5ahpIOdxNCwHA1BvzZR7iOS7VNqLNMaS0k5SpGqkIbqRNoqHSOgXeOn1p8Mfp7nKrVYZDyOlVfkl3vq7PJ36WCvizTyu+vjcf1vroZoMRxevhuk3xT2I8CNEJytnYm4ONpG20cTQgWg+FUKK6p6jpObadb6lrrOmcsar8qdr2U41KqDxjShL5sFb94ccM8v0N//7MYry54sShna7370xbbpP1fS+y5Jg8I8zLhw7at+yeyyxmnDfliOY3iLl+lYUgz+jGHmfSW6UbKVr8Q/M8wYRothX5+fn7+FwAA//8DAOIV/fMZBgAA

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRTy27cOBD8FYFnUeZbpG7GOIsdeBMPkkF2sTeKbMaEZWpAUU4CY/49oGaCxHvbC0E0WF1d1cVXlOwzoAHdnk7vXiCVBbWoxK3ECFOYKEzokZKBi4HLrhe9ZtL8i1oU7+E7GlAvnKPOAOagAAtPJB55cNh7MJRQT5gUtaf9sqDhFdnYeXiJDrp5+Qx5iXNCA/o7Jj9/rdw2dm6aV9/leYJ9WopNrg7zYb+7x3/dHo4Ph8urBZYK7qJHAyJMM0WtxKCFxkIxwFZoiS31OijHe6P9BRZTgZzs1C3+6Re/n0uC4gbWMd4RzAw6t8jbYuvIo13g+P1Up9gsuqv1ditv1+EVvUBGA2t/mnlpd+OmePO8jGuc/M3lLHZ5qipPeT5BLhE2TzaZ02RLnFNzZdpNEVJBLXr41BwmW8Kcn98Y9emif/8/5B8r+7tv4NYCfjevqaABUa5Ri95b9xgTNPu72o4C81wDDowqLLS32EpusFJcBaDO9IyiFu3mVGJa53Vp9qnAl7wJQAP6w04LXEa/ze4xFnBlzVXVP6pm4ZBnv7rS/PKfko50nJAKWstpLc1H8DGDK1D1HfN67fcfCFNsA+3WnCGV5mDLY/OnXR7RgKTqmRREOCs5BeaCYSQQog1nXjBwvfLQM+ZGIUzggUs3Wjf2Oozg+dhXhVDX3WwJG0kIVmmFrZIKC9pLPAYtcBDaa80dU3x842PzMFWcdZoI8BwbCB4LQy02gnPsubQ9U6HvWfjNkuv2Kan0Hl4uf2WbQHmvwSuKmaASC2Mo1kb2eOz1aEalQTqDWnQPOcH0m1Hvo8vzMofSXMPTvLHu45rqf2+2IH2NCX/bdnQ3uyfITd2xjanG+7rW8/l8/gEAAP//AwCsEOr4NgQAAA==

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSUy27cNhSGX0XgWlR4E0VqZ9gtOnBTDxKjLboZUOShh7VMqRTlNDDm3QNqxnDcrLqTiPOfy8f/8AVF8wSoR1fz/NMzxLygGuWwHTHCJCYSE3pPSc9Fz2XDlBRSdX+hGoVb+Ip61AlrqdWAOUjAwpEWD9xb7BxoSqgjrBUlp3lYUP+CTGgcPAcLzbT8DmkJU0Q9+iNEN30ptU1o7DitrknTCLu4ZBNtaea33fUt/vVqf3+3P0ctsBRxExzqEWGKSWpaDEooLCQDbIRqsaFOeWl5p5U7y0LMkKIZm8U9vtV3U46Qbc8axhuCmUanGjmTTWl5MAvcf51LFxuim3Jeb8fbZ/+CniGhntWvMM/pPtgxfEhrRDWa0zRDygE2BttY42hymGJ1yXw9Boj5HPo32HzY5mLKdtoNGoTorAMDLXeWOCYIB+UG6qkQloNWlmhBW2I9Ucx4z1plvBoA1ejuc7UfTfZTenoH+vOZ3+5/4Pto7DFEqHY3RUOBOa4Ae0YlFsoZbFqusZRceqBWd4yiGl1PMYe4TutS7WKGh7RNjXr0sxmXS39XyR5DBpvXVFD8KYthRrNGezzMafJhhENYDg68WceMeuQv2n2a3Gpz9XaRlDSk4YSUxGue11x9AhcS2Axl0Pu0Xmr+R8Ik20TXa0oQc7U3+Vj9YpYj6lErO9YKIqxpOQVmvWbEE6I0Z04wsJ100DFmByG05563djB26JQfwPGhKxSg+KbarpRqaRVXFvuOdFh46bBmrsXMU8U5HbiQ7TvW1d1YdMYqIsBxrME7LDQ1WAvOseOt6Zj0Xcd8sfg8H/LZUq9OKkZ139G6OI4S+iPmBP+ssJxhXXgf1rPcwfN5c7cxpHMKnKSYCdpioTXFSrcdHjo16EEqaK1GNbqFFGH8jvbHYNO0TD5XFytW7/h/WmN5farNll9CxP9uZriZ7COkqpjJhFiW7eKfU42ewCxrgqft8epf0Gzso3koo3hIEC0c7LTGvO3n4h5f/+jbqv0QSU6n0+kbAAAA//8DAO62JwsbBQAA

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRTy27UMBT9lcjrOPUrjp1dNQUxKtARrQCxc+wbxiK1R45TWlXz78iZQW3ZsYuucs49j+tnFMw9oB5dHg7vHiDkGdUo+3XECJOYSEzoHSU9F72QDSWaKkZ/oBr5a3hCPeqEtdRqwBwkYOFIiwc+WuwcaEqoI6wVhdP8nFH/jIxvHDx4C02cv0KafQyoR998cPF32W18Y6e4uCbFCbZhzibYIubzdnONP17u7m52p79mmAu48Q71iDDFJDUtBiUUFpIBNkK12FCnRml5p5U7wXzIkIKZmtn9etnvYg6Qbc8axhuCmUbHGjmTTZE8mBnung5FxRrRVZnX63j97J/RAyTUs/pvmCe6Czv5Cxvv701wF6MPft6jGh1SPEDKHtY4VofTZLKPoTov2UweQkY1urmtdpPJY0z3bzK6PVnf/ofzT8bufYBqe1UwFJjjCvDIqMRCOYNNyzWWkssRqNUdo6hGmxiyD0tc5mobMvxMq0rUo/dmmuGk7zLZvc9g85KK9O+ydA2PPm+iKwNciHYpusXm6iVvShrScEIKyZIPS66+gPMJbIZi6i4tZ/5/IEyyFbRZUoKQq53J++qDmfeoR63sWCuIsKblFJgdNSMjIUpz5gQD20kHHWN2EEKPfOStHYwdOjUO4PjQFaFQ6q3Wi5JaDm4kHDPCOixIK7EajMGiZUzygTNGi9WXXKubqeCMVUSA41jD6LDQ1GAtOMeOt6Zjcuw6Nr6K5Fw5JWW9g4fT2zgpcE6BkxQzQVsstKZY6bbDQ6cGPUgFrdWoRteQAkyvgvrkbYpzHHN1vpjqTXRfllDed7Vez28f8OPa2VW0vyBVpXPjQznnc83H4/H4BwAA//8DAKOT5R4mBAAA

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+JFWvRGcka2EK20oKhNA2P/e0DtBol7600Y6J0ZPvPOvJHJH5B05PZ4/OWEU15ITfKwhQQTmjJNGd9z1knVtappW90qxv4hNRke8SvpiFEx8uiQStRIFbCWBtlHCoCOMw5MtKrk9C8L6d6IHxrA0xCxmZdPmJZhnkhH/hommL+U2n5o4jiv0KR5xIdpyX6KpZk/H+4e6R+3u/3T7vLXgksRNwOQjlirFbOcU9lyoKo1gTrTBxqtNFq7IKMQF9kwZUyTH5sFPv+oD3OeMMdONEI2jApHzjUBn31pOfgF91+PpYsN0X2J11t4++zeyAkT6UT9HeYl3U0ch5s8H0c84Xj0acF0E+fDwU9AanJM8xFTHnDDsr10HH0e5qm6FrsbB5wyqcnTc7Ubfe7ndHjH6vmC4OF/EDhhCqVBQA8I1oNFpZyUvXSojfDWWg3KBc+ZkRaMDYCcuyB6NFFG0fYQFZSBvoxz8OPvy8bPCRYsMCO0QIVOKK1dZCgt9q1BBl7roG10IK0RyoKV3nnPXUATnY4l3wcfX4cJq4d70hHGUYC0SHvBNVUWPPWtdFRrqXvk0RnBSU3u5ikP0zqvS/UwZXxJGz/SkV/9uOCF3G2Kr0PGmNdUoP6tS7FdmmGNufrhAM4a1kjGimjNxzVXHxGGhDFjwbtP6zXffyRCi010t6aEU652Pr9Wv/nllXSk1Ua0iqnoW8lRxN4J1jNmnRSgBEajAY0QMSjletnLNgYfg7F9QJDBlBdiMVy1eVxhrwG4oD4yS5XqA/VRAkXjuYkxBAz9O47V01h0PlqmECR12ANVjnvqlJQUZOuN0L0xoui+I7maj7NSHvB02datAw1gETSnQvGWKuc4ta41NBgbXNAW2+hITR4xTTj+BOrDENO8zH2urt6t3qH7uE7l4lSbj78ME/13m9H9HD9jqsqM/TCVBbuO9VyTA/plTXjYDlb3RnZlu6r9drckb1Rra/Kcfcrr8RpVomklU+fz+fwNAAD//wMAh8LN9fQEAAA=

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSU227cNhCGX0XgtSjzfNCdYbfowk29iBdt0DuKM4qFaKUFRW0aGPvuBbVbJO5d7oSB/jl88w/fyBSOSFpyfzr9csYpL6QmedhCgglDmaGMHzhrpWq1agxjwjn3N6nJ8ITfSEusipFHj1SiQaqAadrJPlIA9JxxYEKrkjN8Xkj7RsLQAJ6HiM28/IlpGeaJtOSvYYL5a6kdhiaO8wpNmkfcTUsOUyzN/LF7eKK/3+8Pz/vrXwsuRdwMQFrinFHMcU6l5kCVth31tu9odNIa4zsZhbjKhiljmsLYLPDle32Y84Q5tqIRsmFUeHKpCYQcSstdWPDw7VS62BA9lni9hbfP9o2cMZFW1P/BvKa7i+Nwt6zdiGccTyEtmO7ifDyGCUhNTmk+YcoDbljOmLp5GXIhGqMNznEOLlilLTiphbQgGYJjVoMHH4wyvYha99A5b1WvQHAFBpXqLKnJBm4cQx7mqbr1/jAOOGVSk+eXaj+G3M/p+A79y5Xo7ieAlrbLvIABEFwAh0p5KXvp0VgRnHMGlO8CZ1Y6sK4D5Nx3okcbZRS6h6ig+ONDiK/DhNXukbSEcRQgHdJecEOVg0CDlp4aI02PPHorOKnJwzzlYVrndal2U8bPaZuXtOTXMC54nfQ+xdchY8xrKhA+mVJsn2ZYY66+G4CzhjWSsSJa82nN1UeEIWHMWHAc0nrL9z+JMGITPawp4ZSrfciv1W9heSUt0cYKrZiKQUuOIvZesJ4x56UAJTBaA2iFiJ1Svpe91LELsbOu7xBkZ8uEWPxWbRaPDgClclR2UlGlhaVeR0m9dRBCFCYw945j9TwWXYiOKQRJPfZAleeBeiUlBamDFaa3VvQ/ILmZhbNSHvB8PdatAwPgEAynQnFNlfecOq8t7azrfGcc6uhJTZ4wTTj+AOrDENO8zH2ubl6r3qH7uE7lwak2330dJvrPtqPHOX7BVJUdh2Eq93Vb66UmRwzLmvC4vVftG9mX46oO27MleaO0q8lLDimvp1tUiUZLpi6Xy+VfAAAA//8DAHpkYp/zBAAA

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRTS2/cNhD+KwLPosw3Rd2MdYou3MSLZJEWvfExignL1IKinATG/veA2g2c9NYLQQz4zXyP4StK9hnQgG5Pp3cvkMqCWlTiVmKEKUwUJvRIycDFIGVnJDGG039Ri+I9fEcD0sJ76g1gDgqwCERix0ePQwBDCQ2ESVF72i8LGl6RjV2Al+ihm5fPkJc4JzSgv2MK89c628bOT/MaujxPsE9LsclXMh/2u3v81+3h+HC4vFpgqeAuBjSgvleC9JRiLmnAQmqHjR4d9j3XShnHPWMXWEwFcrJTt4Snt/lhLgmKH1jHeEcwM+jcomCLrZSdXeD4/VRZbBbd1Xq7lbfr8IpeIKOBtT/NvLS78VO8eV7cGqdwczmLXZ6qylOeT5BLhM2TTeY02RLn1Fwn7aYIqaAWPXxqDpMt45yffzPq00X//n/IP9bp776BXwuE3bymggZEeY9a9N76x5ig2d+hAREKLPAe8MiowqIPFlvJDVaKqxGoN5pR1KLdnEpM67wuzT4V+JI3AWhAf9hpgQv12+wfYwFf1lxV/aPqLhzyHFZfmjf/KelIxwmpoLWc1tJ8hBAz+AJV3zGv137/gTDFNtBuzRlSaQ62PDZ/2uURDUgqzaQgwlvJKTA/GkZGQnrDWRAMvFYBNGPeCWFGPnLpnfVO96ODwJ2uCqHG3Wwbprx1wjqFvegdFk5T7HqisCCSUzdqzmQl8uZj8zBVnPU9ERA4NjAGLAy12AjOceDSaqZGrdn4iyXX9Cmp4wO8XP7KhUEIPQRFMRNUYmEMxb2RGjvdO+NUD9Ib1KJ7yAmmX4x6H32el3kszXV5mt+s+7im+t+bbZG+xoS/bRndzf4JclMztjHV9b7Gej6fzz8AAAD//wMAV+GhYTYEAAA=

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRUTW/bOBT8KwLPosIvUaJuWWeLNdI2buNtF3ujyCeHiEIaFJVuEPi/Lyg7iN1iD3sTqDdv3psZ8hV5/QSoQ9f7/e/P4NOESpTccsQIk5hITOiWko6Lrq4rWgvVCPU3KpG7hRfUoUYYQ40CzEECFpbUuOeDwdaCooRawmqRe+rdhLpXpF1l4dkZqML0DeLkgkcd+u68DT8yt3aVGcNsqxhGWPspaW/yMJ/Xq1v88XqzvdscqyaYMrhyFnWobaUgLaWY19RiUTc9Vs3QY9PyRkrVc8PYEeZ8guj1WE328Z3fhuQhmY5VjFcEM4UOJbI66TxyryfYvuzzFItEN/m8XI6Xz+4VPUNEHSvfxDy2uzKju3qa+tmN9moMu53zu1Xwg9vNUadMXKJ9DHuIycEizm/O6/jyMex2uR/6oMcJUIm2EJ+c1+Pxxw0Meh7TfZjjosxkH/+rBnVIzymgEi1KjuNCW5yWWY0OfPoFe0Z8d19sRp2GEJ8uTLo/ar/+H9J/0ubBeSjWN6hDhAKzvAU8MCqxaK3GuuYKS8nlANSohlFUolXwyfk5zFOx9gl2J9Uu5ruO5sElMGmOeae/ZA7bJgY7m1S8G0xJRSpOSAbNaT+n4itYF8EkyEts43zq9xOESbaAPrgRfpFnNccIPhUbnR6KP/T0gDpUy4bVggija06BmUExMhDSKs6sYGAaaaFhzPRCqIEPvDa9Nn3TDj1Y3jd5bcghK5ZcKwKDaIXFfc0sFoPUuK2BY9X3TAxWKt2YC3GLuzHjtGmJAMuxgsFioajGSnCOLa91w+TQNGw402l7DAQlmf48hH9OYE9Z+nyM9tvuFp6P93iZU1rbgpUUM0FrLJSiuFV1g/um7VUvW6iNOho6hTchv0Hsw+RSfkS+zA5yEi8K3n25hehhPPPmkzMxTGFIxSmUxYVbX2ef37BiCegP5/E/SyxugnmEWORYaefPrDwcDod/AQAA//8DAM8mzccKBQAA

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACoxU227cNhD9FYGvFRXeL3pzbQddpI6N7KIt+sbLyGYtUwuKchIY/vdCWjtXFOiLIAw5cw7PnJknlN0DoB6dHY+Xj5DrjFpU0xZihClMFCb0QEnPRS9lZ6WgjKu/UYvSO/iMeqRFCDRYwBwUYBGJxJ4PAccIlhIaCZNireluZ9Q/IZe6CI8pQDfNf0CZ05RRj/5MOU4fV2yXujBOS+zKNMIuz9XlsJJ5vzt/h38/uzlc35xuzTCvyV2KqEfGKEEMpZhLGrGQ2mOrB4+D4Vop63lg7JSWcoWS3djN8f4rfpxqhhp61jHeEcwsem5RdNWtlL2b4fD5uLLYJLpY4+0W3n77J/QIBfWsfRXzVO5NGNObh9kvaYxvti9q0bFMRyg1wabG9b75SoKSjnRMMUJQi65cuEsZmt1Fcz2uT3TBEAGRYwtDxMJSh63gHEcunWZq0JoNqEXvoGQYv6l6lUKZ5mmozYvIzXc4+7Pzy+z8CCvGWzfOgFq0m/fV5ejGKcPlJwhLPdU6lGU9Pp9yTXmZlrnZ5Qq3xb2cv+afL6VArs2Nq3fNb26+Qz2SSjMpiAhOcgosDJaRgRBjOYuCQdAqgmYseCHswAcug3fBazN4iNxrilr066rhfgkB5vkrm80k47hxaF76dD4myPU7GVGPCAUWuQE8MKqwMNFhJ7nFSnE1AA1Wsy8wB1duoaIeDcZ7T0LwTFgaHLOOx4HwqAbLmNZODFxqwZSSRLsQtWZURK4hOD5wYlbb3ZQpLqH+2Gq+NeCmTP9AqKtUqEeKSGmC854pQY2zBgRVwigL2orBEcGDd1Qa5z0lTpJgDQuOKuuEj3Kwr/Qv823KcJHm4+g+f4NrOtXxX4Jzhg5O/HD9bXEP8HEq9+9PNu7eXx5Qiz4sed0IzW61yMeU8Se1TvTVMtZ0uCvgIsSrKcLPPoJ1XJptQgX1joHUONigsVDUY++9w4MdgjCGaEk1atHFFO6hNKvDXMrrVH0pdr1vzkq4SxVCXcrK76+Nx/W+uRldHaby8N0m+eZhPwrQMU7J2pkIj6dttHFUMRqIimImqMTCWoqNlRp7bbz1yoAMq2LXSz0utfkAMRUIdXvxixc3zPM7CPc/i/HqgheLUrJ6bX/aYpu0/2uJPbfoAdy8FHjYtnX/hHY5Q9mQL5bTKO7yVRrHNEOYcpxRbyjpKJHiheB/XuOsE5qy5+fn538BAAD//wMAY5toPBkGAAA=

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRUTW/cNhD9KwLPosxviboZ6wRduKkXySIteuPHKCYsUwuScmIY+98LardIfCp6IYgh38ybN498Q9E8AxrR7en04QViyahFJWwhRpjCRGFCj5SMXIxSdloMjA7kb9SicA+vaES9cI46DZiDAiw8kdjyyWHvQVNCPWFS1JzmW0bjGzKh8/ASHHRL/gophyWiEf0Zol++19omdG5eVt+lZYZ9zMVEV8n8sd/d499vD8eHw+VWhlzBXfBoRMOgBBkoxVxSj4XsLdb9ZLEbeK+UttwxdoGFWCBFM3fZP/2s75cSobiRdYx3BDONzi3ypphK2ZoMx9dTZbFJdFfj7RbetuMbeoGERtb+K+Yl3Y2bw81ztmuY/c1lLSY/5W2djCtLekUtOqXlBKkE2PS5zRme7fx6NPnp4+VO3eYPP8CtBfxuWWNBI6JcohZtAs2zKWGJzZXjbg4Qy3ZYUog5uP/KxVGLHr40h9mUaUnP7+bx5SLz/n+o/Mm4xxCh2d+hEREKzPMB8MSowmLwBhvJNVaKqwmo0z2jqEW7JZYQ12XNzT4W+Ja2ltCIPpo5w4XfbXKPoYAra6p9/qWqrw5p8asrzc9ZUtKRjhNSQWs5raX5DD4kcAVqE8e0XvN9fQ9him2g3ZoSxNIcTHlsfjP5EY1Iqp5JQYQzklNgbtKMTIQMmjMvGLheeegZc1YIPfGJS2eNs/0wWfDc9rVDqNZpNrdao+XAnMYwTYAFEIEHazS2Wg5eSQqyd+90bB7mijNuIAI8xxomj4WmBmvBOfZcmp6pqe/Z9IskVz9QUst7eLm8u42B8n4AryhmgkostKZ40LLHth+stmoA6TRq0T2kCPMvQn0KLi15mUpzdUjzTrrPa6x/R7O55XuI+Mc2o7vFPUFq6oxNiPWpXMd6Pp/P/wAAAP//AwAaq/UgggQAAA==

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACoxUyW7jOBD9FYFnU+GijQLmYDjdGCOdTpC4ZwZz41KMiciiQVFOB0H+fUBZjp1GH+YmlGp59d4rvqFe7gC1aLnffzlAHwe0QNFNIUZYhUmFCd1Q0vKiLcucV7RpBP0XLZC7gVfUorrQmmoBmEMFuDCkxIpbjY0BQQk1hJVF6imfBtS+IelyAwenIffDXxAG53vUor9db/xLmi1drjs/mjz4Dtb9EGWvE5jv69UN/ra839zdH7MGGFJx7gxqUdNUBWkoxbykBhdlrbCorcK64XVVCcU1Y8cy10cIvezywTyf5xsfe4i6ZTnjOcFMoPcFMjLKBFnJATav+4Rioug6xRdTePps39ABAmrZ4kTmsd2V7tzVblCj68xVlOEJog1yBy8+PMNBdmiB9sHvIUQHEzd3j9kZEiU5yVnFCEELdCv11vWQra+zuy4tLHVDCjAcC7AGF4JKLArOseGlrFll65pZtEA3EHroLrreOh384G3MZsqzT3NWvo+uH/04ZOs+wlOQ8Vj3VXYDoAW6H1Xnhu0DSPO68Q9j+tePXdplNYYAfczuZdxmf8phi1pUVjUrC1JoWXIKTFvBiCWkEZyZgoGuKwM1Y1oVhbDc8lIrqVXdWAWGq5qiBbob436MJ/5/XoDYBLfbgTkj2Ewc33cyWh92awN9dNYlaU4Zk6G6btoqm3uuOgd9/EQyahGhwAxvAFtGK1w0RmJZcoGrilcWqBY1S+hWvrfuafzgiVhrCm7LpqmUUJQAs3VTkNoqYrTlWphSQc1qUjfQCE2IKgvaKKGJAVoWJm0XvBl1/NUKfBLoYezTdWbrtPaL6/HPqjgzch989Np3STDXwePrEGH3O9VWfrf3g4tpfztL+2OAW9+nv/P9z5zNxUsfzzFId5BNpweyVpYyiUlDLS5UbbCsuMK8IrVoqpoJlZx47fUzhCwZTLp+0uTkqbvHbBn01kXQcQxp8j/TUkc5v55O5sxH/v3LZuUDLPf7xRz945BIOjY7GeDTu/JjSOlbP6QtYhgvfPQInT3hunCTgcPxpXIpWBnTgKkoZgUtcSEExY0oa6zqRglVNVBq8eHW7AGMC6Dj1G9znDYT+ztbLkN0Vuo4pNv55vXkptmec8qvIC9U+6g+Dr9QbnbS3ImS5NjH47s5Geh/PZvv7+/v/wEAAP//AwDMb2aaJwYAAA==

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSUy27cNhSGX0XgWlR4FUntDLtFB27qQWK0RTcDijz0sJYplaKcBsa8eyDNGI6bVXcScf5z+fgfvqBknwB16GqafnqGVGZUoxK3I0ZYi0mLCb2npOOik20jNOfc6L9QjeItfEUdUsI56gxgDi1g4YnEPQ8Oew+GEuoJk2LNaR9m1L0gGxsPz9FBM86/Q57jmFCH/ojJj1/W2jY2bhgX3+RxgF2ai01ubea33fUt/vVqf3+3P0fNMK/iJnrUIa1bQTSlmEvqsZCqx0aFHjvNVduanjvGzrKYCuRkh2b2j2/1/VgSFNexhvGGYGbQqUbeFru23NsZ7r9Oaxcbopv1vN6Ot8/uBT1DRh2rX2Ge031wQ/yQl4RqNOVxglwibAy2sYbBljim6pL5eoiQyjn0b3DlsM3FtFPG9waEUM6DBcm9I54JwkH7ngYqhONgtCNGUElcIJrZEJjUNugeUI3uPlf7wZYw5qd3oD+f+e3+B76P1h1jgmp3gzpEKDDPNeDAaIuF9hZbyQ1uW94GoM4oRlGNrsdUYlrGZa52qcBD3qZGHfrZDvOlv6vsjrGAK0teUfzZroYZ7JLc8TDlMcQBDnE+eAh2GQrqULho93n0iyvV20VS0pCGE7ImXsq0lOoT+JjBFVgHvc/LpeZ/JKxlm+h6yRlSqfa2HKtf7HxEHZKtYlIQ4azkFJgLhpFAiDacecHAqdaDYsz1QpjAA5eut65XOvTgea9WCrD6ptquVFrbu+AVFhIIFlz22EoqMKNWGcW4IRzesa7uhlVnnSYCPMcGgsfCUIuN4Bx7Lq1ibVCKhdXi03QoZ0u9Omk1qv+O1sVxlNAfMWf4Z4H5DOvC+7Cc5R6ez5u7jdF6r8G3FDNBJRbGUKyNVLhXujd9q0E6g2p0CznB8B3tj9HlcR5DqS5WrN7x/7Sk9fWpNlt+iQn/u5nhZnSPkKvVTDamddku/jnV6AnsvGR42h6v7gVN1j3ah3WUABmSg4Mbl1S2/Zz94+sffVu1HyLJ6XQ6fQMAAP//AwB7Idn4GwUAAA==

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRTXW/UMBD8K5Gf49RfseO8VVcQpwI90QoQb4694SxS++Q4pVV1/x05d6gtb7xZK8/s7MzuMwrmHlCPLg+Hdw8Q8oxqlP1aYoRJTCQm9I6Snrc9UY0gTDIqf6Aa+Wt4Qj1SwlpqNWAOErBwpMUDHy12DjQl1BHWisJpfs6of0bGNw4evIUmzl8hzT4G1KNvPrj4u/Q2vrFTXFyT4gTbMGcTbBHzebu5xh8vd3c3u9OvGeYCbrxDPeo6KUhHKeYtdVi0asBajQO2HVdS6oFbxk4wHzKkYKZmdr9e+ruYA2Tbs4bxhmCm0bFGzmRTJA9mhrunQ1GxWnRV6vVaXp/9M3qAhHpW/zXzRHdhJ39h4/29Ce5i9MHPe1SjQ4oHSNnDasc64TSZ7GOozk02k4eQUY1ubqvdZPIY0/0bj25Po2//Y/JPxu59gGp7hXpEKDDHO8AjoxKLzhlsWq6xlFyOQK1WjKIabWLIPixxmattyPAzrSpRj96baYaTvstk9z6DzUsq0r/LkjU8+ryJrhRwIdql6Babqxe/KWlIwwkpJEs+LLn6As4nsBnKUHdpOfP/A2GSraDNkhKEXO1M3lcfzLxHPWqlYq0gwpqWU2B21IyMhHSaMycYWCUdKMbsIIQe+chbOxg7qG4cwPFBFaFQ4q3WjWKckZZriTvFHRZcO2xIS/HIneNaM6Fp98bX6mYqOGM7IsBxrGF0WGhqsBacY8dbo5gclWLjK0vOkVNS2jt4ON3GqkA614GTFDNBWyy0prjTrcKD6gY9yA5aq1GNriEFmF4Z9cnbFOc45uq8MdUb674sodx3tW7Pbx/w45rZVbS/IFUlc+NDWedzzMfj8fgHAAD//wMAG25qdSYEAAA=

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSUTW/cNhCG/4rAsyjzS6Som2G3qOumXsSLtOiN5IxsIVpqQVGbBsb+94DaDRL31psw0Dsfz7zDNxLdAUlPbo/HX04Y80JqksctJJjQlGnK+J7zXvCes4ZJLUUr/yE1GR/xK+mJUSHwYJFK1EgVsJZ6OQQKgJYzDky0quR0Lwvp34gbG8DTGLCZl0+YlnGOpCd/jRHmL6W2G5swzSs0aZ7wIS7ZxVCa+fPh7pH+cbvbP+0ufy24FHEzAukJ81xxi4x6azVVEgztDHd0QPBdGLTWwVxkY8yYopuaBT7/qA9zjphDLxohG0aFJeeagMuutOzdgvuvx9LFhui+xOstvH32b+SEifSi/g7zku4mTONNno8TnnA6urRgugnz4eAikJoc03zElEfcsGyTTpPL4xyra7G7acSYSU2enqvd5PIwp8M7Vs8XBA//g8AJkyc9kayV2hhUUkpntRcMweigtfDBMTN4gTwga60ylhvGpRXMylabzoBjNpCavEyzd9Pvy8bPCuY7YEZogQqtUFrbwFB2OLQGGTitve6CBdkZoTropLPOcevRBKtDMcgHF17HiNXDfZmGowDZIR0E11R14KhrpaVaSz0gD9YITmpyN8c8xnVel+ohZnxJGz/Sk1/dtOCF3G0Kr2PGkNdUoP6tS7FdmmENufrhgOLtRjJWRGs+rrn6iDAmDBkL3n1ar/n+IxFabKK7NSWMudq5/Fr95pbXQsV1gTtbRtUhDNgyHBgwp5kB1KARlAADyirQyrKglfNcgWu5H0qE1ASL4arN485yJQZsKUJrqOqCoz4AUPSSMZTetvCeY/U0bbrQMYUgqcUBqLLcUaukpCBbZ4QejBHDT0iu5uOsAAY8Xa5160ADdAiaU6F4S5W1nHa2NdSbzluvO2xD6fkRU8TpJ1AfxpDmZR5ydfVu9Q7dxzWWF6fafPxljPTfbUf3c/iMqSo7dmMsB3Zd67kmB3TLmvCwPVj9G9mV66r227slTMNlJ2vynF3K6/EalraxUprz+Xz+BgAA//8DABTuqPX1BAAA

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+TkHlCLlofDpyO4OKAFinYKFaTgmHBM6JbStqAtJTlrOKVV+S9aIHsHb6hFNVOKKgG4BA6YaVLhrjQKaw2CEqpJUbHUUz4PqP2BpM01HK2C3A9/QRisd6hFf1un/WuaLW2uej/qPPge1m6I0qm0zNf16g5/WW62D5tT1gBDKs6tRi0iHWVUAMGdEByzUte4qanEBnTXKMM5V/WpzLoIwck+H/TLZb720UFUbZEXZU5wIdD7AmkZZVq5kwNs3w5pi4mi2xRfTOHps/2BjhBQWyzOZJ7a3aje3uyHbrS9vokyPEM0Qe7h1YcXOMoeLdAh+AOEaGHi5uEpu6xESU7ygheEoAW6l2pnHWTr2+yhT4ClaggDXWIBRmMmqMSClSXWZSXrgpu6LgxaoDsIDvqrrvdWBT94E7OZ8uzDnJV30brRj0O2dhGeg4ynus+yHwAt0GbsejvsHkHqt61/HNM/N/YJy2oMAVzMNjLusj/lsEMtErJRVAoppORKGagIGKKJ5KTWwDUHzQpdayaY5kwQxZnsKNOyop1JEbRAD2M8jPHM//erJbbB7vegLxtsJ443vYzGh/1ag4vW2CTNOWMyVN9PqLK556q34OIHkpOjKBS6bACbgnLMGi2xrEqBOS+5AapEXdATYcY+jz95IsZoVpqqaXgnOkqgMHXDSG06opUpldBVB3VRk7qBRihCuorRphOKaKAV0wld8HpU8VcrlJNAj6NL15mtE+xX6/B3zi6MbIKPXvk+CWZ7eHobIux/p9rK7w9+sDHhN7O03wa49y79ne9/5mwuXvp4iUG6g2w6PUEqTXklMFXQYcaMxp2uOyxEaaQBU/A6obr16gVClgwmrZs0OXvq4SlbBrWzEVQcQ5r8zwTqJOfn88lc+Mi/ftqufIDl4bCYo38cE0mnZmcDfHhXvg0pfeeHeAV5hvYEvTkvdmUnDcfTU2VTkGvdgOYUF4xWmAlBcSOqGnd104mON1Cpi12zR9A2gIpTv20Y07SZ2d/5chmiNVLFIR3PF68mO83+nFN+XfJKtp/Vp+FX0s1WmjtRkiz7dHo4Jwf9r3fz/f39/T8AAAD//wMAnt4S3SgGAAA=

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSUTW/cNhCG/4rAsyjzS6Som2G36MJNvYgXbdDbiDOKhWilBUVtGhj73wtqt0jcW27CQDPzzjMv541NcCTWsvvT6ZczTWlhJUvDFlJCWS4sF/IgZatkK0UlnLFCqr9ZyYYn+sZa5kwIMnjimixxg6Lmne4DRyQvhUShapNrwueFtW8MhgrpPASq5uVPisswT6xlfw0Tzl9zbxiqMM4rVnEeaTctCaaQxfyxe3jiv9/vD8/7618LLTm5GpC1THTSSE+Cd95bbjQ63jgJvCfsmtBba4O7pg1TojjBWC345Xt/nNNEKbSqUroSXHl2KRlCgiy5g4UO305ZxYboMcfLLbx9tm/sTJG1qvwP5rXcXRiHu2XtRjrTeIK4ULwL8/EIE7KSneJ8opgG2rCcKXbzMqRMNAQHTSMlNuBM7bDRtdIOtSBshKvRowdrbK9CXffYNd6Z3qCSBi0Z0+VBN3DjCGmYp+Km/WEcaEqsZM8vxX6E1M/x+A79y5Xo7ieAZtmsZVrU2jpHRmsN3nZKEDobrFVdAOH6TpEMJGpvnJdOSO2V8Lq2rnEIwgdWsg8QXoeJit1j7i5JoW6I90pabhoEDrX23Fpte5LBOyVZyR7mKQ3TOq9LsZsSfY7bvKxlv8K40HXS+xheh0QhrTFD+GSzGfdxxjWk4rsBsrUrLUROWtNpTcVHwiFSSJRxHOJ6q/e/FGXVlvSwxkhTKvaQXovfYHllLfPQBAkePIANoadaUC9QgBUOyaIlNAodGm/QGi+CNdBJg1DLrs8RVjLKfis2i4faUm2U5x30PTekDIcAxI1H4YPoXK3oHcfiecx5EBphCDX31CM3XgL3RmuOuganbO+c6n9AcjOLFBkw0vn6WDcFFrEhtJIrI2tuvJe88bXjnWs639mG6pA1P1GcaPwB1IchxHmZ+1TcvFa8Q/dxnfLBKTbffR0m/s+2o8c5fKFY5B3DMOX3dVvrpWRHgmWNdNzuVfvG9vlxFYftbClXSd3okr0kiGk93cLaV15rd7lcLv8CAAD//wMAfUhktfQEAAA=

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRTwW7bOBD9FYFnUSEpihJ1C5xd1Ei7MdqgXextxBnVRBXKoKi0ReB/X1B20aa33ohHvpk37w1fWIAnYj27PZ3+eqaQFlay5DdICWW4MFzIRyl7JXvZVJ2sO6Hq/1jJ/D19Zz1rtXPSWeI1GeIaRcOHenQckawUEoVqdK4JnxfWvzDwFdKzd1TNy0eKi58D69knH3D+mnuDr9w0r1jFeaJ9WBIEl8X8s9/d87e3h8eHw+XVQksmVx5Zz8QgtbQk+GCt4brGlnetBD4SDp0bjTGuvdB8SBQDTNWCX372xzkFSq5XlaorwZVl55IhJMiSB1jo8fspq9gsust4ucHbsX9hzxRZr8ofZl7K3bjJ37j56QkC3ow++OXISnaK84li8rTZsU04TZD8HIprk93kKSRWsocPxWGCNM7x6ZVHHy6j7/9g8nfgjj5Qsb/LHEkK6474qKThukPg0NSWG1ObkaSzrZKsZLs5JB/WeV2KfUj0OW4qWc/+hmmhi77b6I4+kUtrzNL/NTlr+ubTbsYMCFayQ5xxdan4abcUlahqkS8f1nRaU/Ge0EdyifJMj3G9lv+NoozaSLs1RgqpOEA6Fm9gObKeWeicBAsWwDg3UiNoFCjAiBbJoCHUClvUVqPRVjijYZAaoZHDmJGsO6dbXBYKGzsMI/EOFHFtleEWB8tNLRAGQLSDe2Vr8TBlHrhOaMKaWxqRayuBW13XHOsGWmXGtlXjL5ZcE5ci+430fPkamwKD2BEayZWWDdfWSt7ZpuVD2w12MB01Lmu+pxho+sWod97FeZnHVFwXpnhl3fs15O9dbMvz1Qf+bYvsbnZfKBY5cvAhb/M15fP5fP4fAAD//wMAHpFmlyUEAAA=

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRTTW/cNhD9KwLPokxSFCXqZqxTdOEmXiSLtOhtxBnFhGVqQVFOAmP/e0DtBk5664UgBnwz72P4ygI8E+vZ7en07oVCWljJkt9KSijDheFCHqXslexlU3VSKqmbf1nJ/D19Zz1rtXPSWeI1GeIaRcOHenQckawUEoVqdO4JXxbWvzLwFdKLd1TNy2eKi58D69nfPuD8Nc8GX7lpXrGK80T7sCQILpP5sN/d879uD8eHw+XVQksGVx5Zz8QgtbQk+GCt4brGlnetBD4SDp0bjTGuvcB8SBQDTNWCT2/zcU6BkutVpepKcGXZuWQICTLlARY6fj9lFptFd7lebuXt2r+yF4qsV+VPMy/tbtzkb56XYfUT3lzOBMtTVnmK84li8rR5ssmcJkh+DsV10m7yFBIr2cOn4jBBGuf4/JtRny769/9D/jFPf/eN3JoId/MaEuuZrC0r2Xtwjz5Qsb/L7SQprDvio5KG6w6BQ1NbbkxtRpLOtkqyku3mkHxY53Up9iHRl7gJYD37A6aFLtRvo3v0iVxaY1b1j8m7cIgzri4Vb/5LUYmqFiKD1nRaU/GR0EdyibK+Y1yv/f4DUUZtoN0aI4VUHCA9Fn/C8sh6ZqFzEixYAOPcSI2gUaAAI1okg4ZQK2xRW41GW+GMhkFqhEYOY66wklGOu9g2TCoYYFSSkxoF1w0MfOiM5LUgRa4ZZNdmIm8+Fg9TxoHrhCasuaURubYSuNV1zbFuoFVmbFs1/mLJNX0pssFIL5e/sjEwiB2hkVxp2XBtreSdbVo+tN1gB9NR4zLne4qBpl+Meu9dnJd5TMV1eYrfrPu4hvzfi22RvvrAv20Z3c3uiWKRMwYf8npfYz2fz+cfAAAA//8DAHg1vGQ2BAAA

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACoxUy27kNhD8FYHXiDJfokTdHNuLDBw/sDNIgtyaZMtmLFMDirJ3YfjfA2ns9WMRIBdBaJJd1dXV/UQi3CPpyPF+f/aAMU+kJDmsIcGEpkxTxnecd4J3vK5aLo3R8m9SknCO30lHGuUcdwapRI1UeVZTK3tHvUfDGfdM1GrJCTcT6Z4IhMrjQ3BYjdMfmKYwRtKRP0P04+OCDaFywzj7Ko0DbuKUIbqFzOXm5Jz+fny9u7o+3JpwWh5XwZOOMMsVN8ioNUZTJX1D24YD7dHb1vVaa9ccnoWYMUUYqsnfveH7MUfMrhOVkBWjwpDnknjIsFC2MOHu+35hsUp0usTLNbz+dk/kARPpRPkq5iHdkRvC0f1k5zD4o/VLSrJP4x5TDriqcbUt3khwVrFKaMEYKckFuNsQsdicFlfDUiK4lin0khrsPVWGAzVKSuplDY3QfdOInpTkHFPE4V3Wi+DSOI19Ll5ELj7gbI9PziLYAReMLzBMSEqymbYZoodhjHj2Dd2cD7l2aV6OT8aYQ5zHeSo2MeNNgpfz1/cnc0oYc3EN+bb4DaZb0hEDreNgwABo53qsGfbMM9Cs8ai9Rq+Eb7wyymtlmNMKLFceam77JUJK8uui4XZ2Dqfpjc1qkmFYORQvfToZAsb8QcbFJRyFly3SXnBNVeuBQi0N1VrqHrkzjeCvMDtIN5hJR/rWWsucs0IZ7kAYkL5n0uveCNE0oHpZN0poXbMGnG8awZWXDTqQvWStICW5TqOfXf7cark24DqN/6DLi1SkI5rVdevAWqEVb8G0qLhWrTbYGNUDU9JZ4HUL1nIGNXOmFQ64NqCsr/sfKp3FmxDxNEz7Ab6/w20rXclfHEDLe1Cfrn9JcI+PY7q7PNi4ujzbkZJ8neOyEYrNYpHHEOk3vUz0xTzksLtNCB79xejxZx/hMi7FOqHcIlimLLVaaKq4kdSwHqljgjGrFce2JSU5Hd0dpmJxGIS4TNWPZFfb4ji525DR5Tkt/P5aeVxti+sBcj+m+w+b5F1hnwWohORs6YzHh8M2Wjlq71v0mlOheE2VMZy2pm6obVprrG6xdotiV3Pez7n4ij4kdHmt+MWLK+bJLbq7n8V4dcGLRTlbvLY9bLFV2v+1xJ5Lco8wzQnv123dPZFNjJhW5NP5MIqbeBGGIUzoxugn0tUNbyuhpXph+N/3GlHx2vDn5+fnfwEAAP//AwCXJBKUGwYAAA==

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRU0W7cKBT9FYtn4wDG2PgtmrTaUbabUTvqrvYNc68bFAdGgNNG0fz7Cs+s2jyt9gWha87l3HMOfiPePCMZye3p9OEFfU6kJtltJcGEokxRxo+cj4KPvGsGNgyKib9JTdw9vpKR9NJabjXSFhVSCayjUztbCoCaMw5MdLL0NN8SGd+IcQ3gi7PYhPQVY3LBk5H86TyE7+Vu4xq7hBWaGBbc+5SNt4XMH/vdPf399nB8OFxOJUwF3DggI2ETl1wjo5PWisoWejr03NAZYRrsrJSy/QXmfMbozdIkePp5P4TsMdtRNKJtGBWanGsCJptCeTIJj6+nwmKT6K7U6628bcc38oKRjKL+V8xLuxu7uJvnNK1ugZvLmk16Sts6G5tDfCU1OcVwwpgdbvrcpoTP0/J6NOnp4+VM2aYPP9CuGWEXVp/JSHirSE02gZbFZBd8deW4Wxz6vH3M0fnk7H/1aklNHr5Uh8XkOcTnd358uci8/x8qfzL20Xms9ncFw1FAOyCdBVdUDmCo6VpNlWrVjNzqXnBSk13w2fk1rKna+4zf4jYSGclHsyS88LuN9tFltHmNZc6/VMnVIQZYba5+eslZw5qWsQJa82nN1WcEF9FmLEMc43rt9/U9RCixgXZrjOhzdTD5sfrNpEcyEm0Gy4022hhl7Ywdw5kBM4r1gAoUghTQg9QSlNTMKmkmLsF0fJpLhdQES3SqLa0aejPA0NOug45K0fd0kJ2iknM7Gd2ynsM7HauHpeCMHZhEaKnGGajU3FAt25ZC25leqLnvxfyLJNc8cFYEBny5vLuNgQIYEBSnQvKOSq05HXTX06kfJj2pATtbON9j9Lj8ItQnZ2NIYc7VNSHVO+k+r778O6otLd+dpz82j+6CfcJYFY+N8+WpXG09n8/nfwAAAP//AwDwEAjXggQAAA==

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSU227cNhCGX0Xg9VLm+aA7w25R102ziI226B3JGdlCtNKCojYNjH33gNotEveud8JA/xy++YdvZAoHJB25PR5/OuFUFrIjZdhCgglDmaGMP3PeCd4J23KhBWPyb7IjwyN+JR2xKiWePFKJBqkCpmmUfaIA6DnjwIRWNWd4WUj3RsLQAp6GhO28/IF5GeaJdOTPYYL5S60dhjaN8wptnkd8mJYSplSb+f3h7pH+drt//ri//LXgUsXtAKQj2qXeRB2ot0JThQ6ogxio1lwxLrVXhl9kw1QwT2FsF/j8vT7MZcKSOtEK2TIqPDnvCIQSassxLPj89Vi72BDd1/huC2+f3Rs5YSad2P0L85LuJo3DTZmPI55wPIa8YL5J8+EQJiA7cszzEXMZcMOyTTqOoQzz1FyL3Y0DToXsyMenZj+G0s/58I7V0wXBw/8gcMIca4OAARBcAIdKeSl76dFYEZxzBpSPgTMrHVgXATn3UfRok0xC95AU1IW+jHMM46/Lxs8LFh0wK4xAhV4oY3xiKB322iKDYEw0LnmQzgrlwMngQ+A+ok3epJrvQ0ivw4TNwz3pCOMoQDqkveCGKgeBBi09NUaaHnnyVtRp7uapDNM6r0vzMBV8yRs/0pGfw7jghdxtTq9DwVTWXKH+ZWqxfZ5hTaX57gDOWtZKxqpoLce1NJ8QhoypYMX7nNdrvv9IhBGb6G7NGafS7EN5bX4Jy2vdibFCK6ZS0JKjSL0XrGfMeSlACUzWAFohUlTK97KXOsWQonV9RJDR1gmxGq7ZPC41GsVUT9EKRhVoTb0TjDIAlXw0BhK849h8HKsuJMcUgqQee6DK80C9kpKC1MEK01sr+h+QXM3HWS0PeLpc69aBAXAIhlOhuKbKe06d15ZG66KPxqFOnuzII+YJxx9AfRhSnpe5L83Vu807dJ/Wqb44zebjL8NE/9l2dD+nz5ibuuMwTPXArms978gBw7JmPGwPVvdG9vW6muft3RK29ZKJHXkqIZf1eA1r31rh9fl8Pn8DAAD//wMAJXTPRPUEAAA=

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSU227cNhCGX0Xg9VLm+aA7w25Rw029iI026B3FGcZCtNKCojYNjH33gtotEvcud8JA/xy++YdvZAoHJB25PR5/OeFUFrIjZdhCgglDmaGMv3DeCd4J23IjnXH+b7IjwyN+Ix2xKkYePVKJBqkCpmkvU6QA6DnjwIRWNWf4vJDujYShBTwNEdt5+RPzMswT6chfwwTz11o7DG0c5xXaPI/4MC0lTLE288fD3SP9/Xb/8rS//LXgUsXtAKQj2sVkeh2ot0JThQ6ogz5QrbliXGqvDL/IhqlgnsLYLvDle32Yy4QldqIVsmVUeHLeEQgl1Jb7sODLt2PtYkN0X+O7Lbx9dm/khJl0YvcfzEu6mzgON8vaj3jC8RjygvkmzodDmIDsyDHPR8xlwA3LCXM/L0OpRGO0wTnOwQWrtAUntZAWJENwzGrw4INRJomodYLeeauSAsEVGFSqt2RHNnDjGMowT82197txwKmQHXl6bvZjKGnOh3fony9EH34CaG27zgsYAMEFcKiUlzJJj8aK4JwzoHwfOLPSgXU9IOe+FwltlFHoBFFB9ceHEF+HCZuHe9IRxlGAdEiT4IYqB4EGLT01RpqEPHoravW7eSrDtM7r0jxMBT/nbV7SkV/DuOBl0tscX4eCsay5QvhkarF9nmGNpfluAM5a1krGqmgtx7U0HxGGjLFgxfGS12u+/0mEEZvobs0Zp9LsQ3ltfgvLa2VorNCKqRi05Chi8oIlxpyXApTAaA2gFSL2Svkkk9SxD7G3LvUIsrd1Qqx+azaLW6GSs8lRC32iikdOe+0DxeCs9MaEqOw7js3TWHUhOqYQJPWYgCrPA/VKSgpSBytMslakH5BczcJZLQ94uhzr1oEBcAiGU6G4psp7Tp3XlvbW9b43DnX0ZEceMU84/gDqwxDzvMypNFevNe/QfVyn+uA0m+++DhP9Z9vR/Ry/YG7qjsMw1fu6rvW8IwcMy5rxsL1X3RvZ1+NqXrZnS9jWSyZ25LmEXNbjNax9a4XX5/P5/C8AAAD//wMAcuMez/QEAAA=

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSUXW/bNhiF/4rAa1PhlyhRd5mzYkbaJm28dtgdRb50iCikQVHpgiD/faDsIHaLXexOoHjej+cc6QUF/QioR5f7/e9PEPKEVij75YgRJjGRmNAtpT2jPWtr2QrVSvU3WiF/Dc+oR60whhoFmIMELCxp8MCdwdaCooRawhpRaurdhPoXpH1t4ckbqOP0DdLkY0A9+u6DjT9Kb+1rM8bZ1imOsAlT1sGUYT5v1tf44+Xt9ub2cGuCqYhrb1GPms44OTQaq5Y1WEBncWcHjZuGCkJ5o4SkB5kPGVLQYz3Zh/f+NuYA2fSsZrwmmCn0ukJWZ11GHvQE2+d9mWJBdFXOV8vx8ti/oCdIqGerN5iHchdm9BeP0zD70V6McbfzYbeOwfndnHQujVdon+IeUvawwPnNB52eP8bdrtRDH/Q4AVqhLaRHH/R4eHEFTs9jvotzWshM9uG/7qAe6TlHtEILyXFc2lbHZdajh5B/0Z40vrmrbkedXUyPZybdHdhv/gf6T9rc+wDV5gr1iFBglneAHaMSi85qrBuusJRcOqBGtaxo1jFkH+Y4T9UmZNgdqZ3Nd5nMvc9g8pzKTn/JErbbFO1scvVuMCU1qTkhRTTn/Zyrr2B9ApOhLLFN87HeTxIm2SL64Ef4Bc96TglCrm51vq/+0NN9wSFb1ggijG44BWacYsQR0inOrGBgWmmhZcwMQijHHW/MoM3Qdm4Ay4e2rA0lZNWSa+DSAucEUyscFppYPAAD3DlrXNMRRR07g1vdjEWnTUcEWI4VOIuFohorwTm2vNEtk65tmTvhtD0EgpLS/jSEf05gj1n6fIj22+4Wng7f8TKntLYDKylmgjZYKEVxp5oWD203qEF20Bh1MHSKbyC/QRri5HP5iXyZPZQknl149+UaUoDxxJtP3qQ4RZerYyirM7e+zqH8w6oloD98wP8ssbiK5gFSVWKlfTix8vX19fVfAAAA//8DAItNHA8KBQAA

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACoxUTW/bOBD9KwLPpkJSlEQa2IPhtFgjTWMk7u5ibxQ5jInIokFRToPA/31BWY6dooe9CaP5ePPeG76jTu0AzdFiv/9ygC72aIaiG0OMsAqTChO6oXTO6JzVuZBlUVXiXzRD7g7e0BzVXGuqJeACKsDckBI3hdXYGJCUUENYyVNP9dyj+TtSLjdwcBpy3/8FoXe+Q3P0t+uMf02zlct16weTB9/Cquuj6nQC8321vMPfFuvNw/qU1UOfinNn0ByVQtuqKRWWNSsxB2GwMI3CZUk5oUUpeUVPZa6LEDrV5r15ucw3PnYQ9ZzlrMgJZhIdZ8ioqBLkRvWwedsnFCNFtyk+G8Pj5/wdHSCgOZudyTy1u9Gtu9n1zeBacxNVeIZog9rBqw8vcFAtmqF98HsI0cHIzcNTdoFESU5yVjFC0AzdK711HWSr2+yhTQsrLQgHU2AJ1mAuqcKSFwU2RalqVtm6ZhbN0B2EDtqrrvdOB997G7OJ8uzTnKXvousGP/TZqovwHFQ81X1VbQ9ohtZD07p++wjKvG3845D+dUObdlkOIUAXs7WK2+xP1W+TLlXNSk64VmVBgWkrGbGECFkwwxnoujJQM6YbzqUtbFHqRummFrYBUzR10uxhiPshnvn/eQViE9xuB+aCYDNyvG5VtD7sVga66KxL0pwzRkO17bhVNvVctg66+IlkNEeEAjOFAGwZrTAXRmFVFhJXVVFZoFrWLKFb+s665+GDJ2Kt4YUthaga2VACzNaCk9o2xGhbaGnKBmpWk1qAkJqQpuRUNFITA7TkJm0XvBl0/NUKxSjQ49Cl68xWae1X1+GfFb8wsg4+eu3bJJhr4emtj7D7nWpLv9v73sW0v52k/dHDve/S3+n+J86m4oWPlxikO8jG0wNVc8NAYqaAYy4KwELSGlsJohCNJpKmrW69foGQJYMp142anD318JQtgt66CDoOIU3+Z1zqJOfX88lc+Mi/f9ksfYDFfj+bon8cEkmnZmcDfHpXfvQpfev7tEUMw5WPnqC1Z1xXbjJwOL1ULgUrYwSYimLGaYm5lBQLWda4qUUjm0pAqeWHW7NHMC6AjmO/zWnaROzvbLkI0VmlY59u55vXo5sme04pv4K8Uu2j+jT8SrnJSVMnSpJjn07v5mig//VsHo/H438AAAD//wMAmj/F9ScGAAA=

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACoxU227cNhT8FYGvXcq8i9SbazvownVsZBdt0TdeDm3WMrWQKCeB4X8vqLUTO0GBvggCL2eGM3POE8r2AVCPTg+Hi0fIZUYbVNK6xAhTmChM6J7SntGe6VZqaYTq/kYblC7hK+pRJ7yn3gDmoACLQCR2PHocAhhKaCBMilrT3s6of0I2tQEek4d2nP+AaU5jRj36M+Uwfq7YNrV+GJfQTuMA2zwXm30l83F7dol/P73ZX98cT80w18ttCqhHUvuonLTYdExiATpgHZzFUlJBKK+c6fFaygWmbId2Dvff8cNYMhTfs5bxlmBm0PMGBVtspezsDPuvh8pilei8rm/W5fW3f0KPMKGebV7FPJY78UM6eZjdkoZwsn7RBh2m8QBTSbCqcb1rvpOgpCUtU4wQtEFX1t+lDM32vLke6hOt10RA4NhADFgYarERnOPApe2Yil3HItqgS5gyDG+qXiU/jfMYS/MicvMOZ3d6dpGtG6BifLDDDGiDtvOu2BzsMGa4+AJ+Kcda+2mp22djLikv4zI321zgdrIv+6/3z5ZpglyaG1vumt/sfFctUh2TgghvJafAfDSMREK04SwIBr5TATrGvBPCRB659M561+noIHDXVft+rRruFu9hnr+zWUMyDCuH5sWnsyFBLu9kRD0iFFjgGnBkVGGhg8VWcoOV4ioC9aZj32D2drqFgnoUtXOOeO+YMNRbZiwPkfCgomGs66yIXHaCKSVJZ33oOkZF4B14yyMnmqENupnGsPjyo9V8NeBmGv8BX6pUqEeKSKm9dY4pQbU1GgRVQisDnRHREsG9s1Rq6xwlVhJvNPOWKmOFCzKaV/oX+TZlOE/zYbBf3+DqVrX8F2+tptGKH45/mOwDfB6n+4/HGLcfL/Zogz4tuU6EZlsj8jll/EXVjr5ahpL2dxPYAOFqDPBzjqC2S7N2qI9dpz2NuNqLhRAKGwMex+i0JoZpQyub89Hfw9TUhNmUa1d9K3a9a04nf5cK+LJMld9fK4/rXXMz2BLH6eHdJHnzsB8FaBmnpDoT4PE4jVaOKgQNQVHMBJVYGEOxNrLDrtPOOKVB+srxeimHpTSfIKQJfFlf/JLFFfPsDvz9z2K8puAlopTUrO2OU2yV9n8NsecNegA7LxM8rNO6f0LbnGFakc+XYytu81UahjSDH3OYUW8YbZUW9IXgfx4TspUdYc/Pz8//AgAA//8DAPtdxY4ZBgAA

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRTy27bMBD8FYFnU+Fbom6B06JG2sZIjLbojSKXNRGFMigqDwT+94KyizS99UIQC87uzOzwFUXzAKhDl4fDh0eIeUIrlMNSYoQpTBQmdEdpx2jH2lq2nHBNf6IVCtfwgjrUCGup1YA5KMDCEYl77i12DjQl1BEmRelpfk2oe0Um1A4eg4V6nL5BmsIYUYe+h+jGpzLbhNoO4+zqNA6wiVM20RYyXzfra/z5cru72Z5eTTAVcB0c6pBsrVe9NFg3TGIBrcOt6w2WkgpCudRC0RMsxAwpmqGe3P3bfDfmCNl2rGa8JphpdFwhZ7IplHszwe7lUFgsFl2V+mopL9fuFT1CQh1b/THz1O7CDuHiYernMLiL05nNdF9UHtJ4gJQDLJ4sMofB5DDG6jxpPQSIGa3QzV21HUz2Y3p4Z9TdSf/mP+TvyvQPz2DnDG49zjGjDlFB0Ap9MXYfIlSbK9QhQoE53gL2jCosWmewkVxjpbjyQK1uWGm3HmMOcR7nqdrEDL/SIgB16KMZJjhRv0x2HzLYPKei6ocqWdim0c02V2/+U1KTmpPC5GbOhzlXt+BCApuh6Nul+dzvHwhTbAGt55Qg5mpr8r76ZKZ9MUU1TAoirJGcArNeM+IJaTVnTjCwjXLQMGZ7IbTnnkvbG9s3re/B8b4pCqGsu1oSpjWR0ivARDqCBeceG9oy3GhtGy+4U4y/87G6GQrO2JYIcBxr8A4LTQ3WgnPsuDQNU75pmP/LkvP2KSnjHTye/srCQDnXglMUM0ElFlpT3GrZ4L5pe92rFqTVaIWuIUUY/jLqS7BpnEafq3N4qnfW3c6x/PdqCdJTiPh52dHVaO8hVWXHJsQS7/Naj8fj8TcAAAD//wMA9x2TBjYEAAA=

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRUTW/cNhD9KwLPosxvkboZ6wRduKkXySIteqPIUUxYphYk5cQw9r8X1G6R+FT0QhBDvpk3bx75hqJ9BjSg29PpwwvEklGLSthCjDCFicKEHikdGB2Y7qSmhDHyN2pRuIdXNKBeOEedAcxBARaeSDzyyWHvwVBCPWFS1Jz2W0bDG7Kh8/ASHHRL/gophyWiAf0Zol++19o2dG5eVt+lZYZ9zMVGV8n8sd/d499vD8eHw+VWhlzBXfBoQFK7SY3SYtMziQVoj7UfLZaSCkK5NELRCyzEAinaucv+6Wd9v5QIxQ2sY7wjmBl0bpG3xVbKo81wfD1VFptEdzXebuFtO7yhF0hoYO2/Yl7S3bg53DzncQ2zv7msxeanvK2TdWVJr6hFp7ScIJUAmz63OcPzOL8ebX76eLlTt/nDD3BrAb9b1ljQgCjvUYs2gebZlrDE5spxNweIZTssKcQc3H/l4qhFD1+aw2zLtKTnd/P4cpF5/z9U/mTdY4jQ7O/QgAgF5rkGPDGqsNDeYiu5wUpxNQF1pmcVs1tiCXFd1tzsY4FvaWsJDeijnTNc+N0m9xgKuLKm2udfqvrqkBa/utL8nCUlHek4IRW0ltNams/gQwJXoDZxTOs139f3EKbYBtqtKUEszcGWx+Y3mx9r56pnUhDhrOQUmJsMIxMh2nDmBQPXKw89Y24Uwkx84tKN1o29nkbwfOxrh1Ct02xu9caB11RizX2PhdISa6MI9saDFUqNxKl3OjYPc8VZp4kAz7GByWNhqMVGcI49l7Znaup7Nv0iydUPlNTyHl4u725joLzX4BXFTFCJhTEUayN7PPZ6NKPSIJ1BLbqHFGH+RahPwaUlL1Nprg5p3kn3eY3172g2t3wPEf/YZnS3uCdITZ2xDbE+letYz+fz+R8AAAD//wMAWwyKLoIEAAA=

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSUX2/kJhTFv4rF8+DlrwG/RUmrjtJtRrtRW/VlhOGSoXHwFONsV9F89xWeibLpPvXNRpzLPT/O5QUl+wSoR1fH40/PkMqMNqjEdYkR1mHSYULvKe0Z7ZlpiVJKM/MX2qB4C19Rj5RwjjoDmEMHWHgi8cCDw96DoYR6wqSoNe3DjPoXZGPr4Tk6aKf5d8hznBLq0R8x+elLPdvG1o3T4ts8jbBNc7HJ1WZ+217f4l+vdvd3u/OuGeYqbqNHPZLahW6QFhvFJBagPdZ+sFhKKgjl0oiOnmUxFcjJju3sH9/O91NJUFzPWsZbgplBpw3yttja8mBnuP96rF2siG7q+mZdXj/7F/QMGfVs8wrzXO6DG+OHvCS0Qcc8HSGXCCuD1dY42hKn1FwqX48RUjlv/Rtc2a++mHbK+MGAEMp5sCC5d8QzQThoP9BAhXAcjHbECCqJC0QzGwKT2gY9ANqgu8/NbrQlTPnpHejPZ37b/4Hvo3WHmKDZ3qAeEQrMcw04MNphob3FVnKDu453AagzilXN9ZRKTMu0zM02FXjIq2vUo5/tOF/6u8ruEAu4suSK4s+uBma0S3KH/TFPIY6wj/PeQ7DLWFCPwkW7y5NfXGneLpKSlrSckFp4KcelNJ/AxwyuQDV6n5fLmf+RsI6touslZ0il2dlyaH6x86HS6RSTgghnJafAXDCMBEK04cwLBk51HhRjbhDCBB64dIN1g9JhAM8HVSlAzU2zXmlQnWKaARaSCSwEMGys0tgpbZTojBwG8451czdWnXWaCPAcGwgeC0MtNoJz7Lm0inVBKRZqxI/HfTlH6jVJNaj+O1qXxFFCf8Sc4Z8F5jOsC+/9cpZ7eD5P7mqj816D7yhmgkosjKFYG6nwoPRghk6DdNXGLeQE43e0P0aXp3kKpblEsXnH/9OS6uvTrLH8EhP+dw3DzeQeITc1TDamOmyX/Jw26AnsvGR4Wh+v/gUdrXu0D9VKgAzJwd5NSyrrfM7+8fWPvo3aDzvJ6XQ6fQMAAP//AwDsbjNRGwUAAA==

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSTy27bMBBFf0Xg2lT4lqhd4LSokaYxmqAtuqPIUUxEIQ2KygOB/72g7CJJd90JA92ZO+cOX1EwD4A6dL7ff3qEkCe0QtkvJUaYwkRhQm8p7RjteFMTwbhm7DdaIX8JL6hDjbCWWg2YgwIsHJG454PFzoGmhDrCpCg9zd2EuldkfO3g0Vuo4/QD0uRjQB366YOLT2W28bUd4+zqFEfYhCmbYIuZb5v1Jf56vr293h7/mmAq4to71CHZ2kH10mDdMIkFtA63rjdYSioI5VILRY8yHzKkYMZ6cvdv813MAbLtWM14TTDT6LBCzmRTLPdmgtuXfXGxILoo9dVSXj67V/QICXVs9Rfmsd2ZHf2ZjQ8PJrizwQc/7dAK7VPcQ8oeFhzLhuNoso+hOg1Zjx5CRit0fVNtR5OHmB4+MLo5rr75j82vjN35ANXmAnWIUGCOt4AHRhUWrTPYSK6xUlwNQK1uWNGsY8g+zHGeqk3IcJcWl6hDn804wdHfebI7n8HmORXrv1TJGp59XkdXCrg02qboZpurN96U1KTmhJQmc97PufoOziewGcpSt2k+9f9HwhRbROs5JQi52pq8q76YaVdIqIZJQYQ1klNgdtCMDIS0mjMnGNhGOWgYs70QeuADl7Y3tm/aoQfH+6YYhRJvtVyUEsISwhs88MZgQbjEpqUcG0GVsj21vbQfuFbXY9EZ2xIBjmMNg8NCU4O14Bw7Lk3D1NA0bHiH5BQ5JWW8g8fj2zg6cK4FpyhmgkostKa41bLBfdP2ulctSKvRCl1CCjC+A3XlbYpTHHJ1upjqA7rvcyjvu1qu58kH/LxkdhHtPaSqZG58KOd8ivlwOBz+AAAA//8DAL8FuWgmBAAA

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

@ -4,7 +4,13 @@ using PlotLine.ViewModels;
namespace PlotLine.Controllers;
public sealed class ScenesController(ISceneService scenes, IPlotService plots, IAssetService assets, ICharacterService characters, ILocationService locations) : Controller
public sealed class ScenesController(
ISceneService scenes,
IPlotService plots,
IAssetService assets,
ICharacterService characters,
ILocationService locations,
IWriterWorkspaceService writerWorkspace) : Controller
{
public async Task<IActionResult> Create(int chapterId)
{
@ -272,6 +278,80 @@ public sealed class ScenesController(ISceneService scenes, IPlotService plots, I
return RedirectForScene(model.SceneID, model.ReturnProjectID, model.ReturnBookID);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> SaveWorkflow(SceneWorkflowEditViewModel model)
{
await writerWorkspace.SaveSceneWorkflowAsync(model);
return RedirectForScene(model.SceneID, model.ReturnProjectID, model.ReturnBookID);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> SaveNote(SceneNoteEditViewModel model)
{
if (string.IsNullOrWhiteSpace(model.NoteText))
{
TempData["WriterWorkspaceError"] = "Enter note text before saving.";
return RedirectForScene(model.SceneID, model.ReturnProjectID, model.ReturnBookID);
}
await writerWorkspace.SaveNoteAsync(model);
return RedirectForScene(model.SceneID, model.ReturnProjectID, model.ReturnBookID);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteNote(int id, int sceneId, int? projectId, int? bookId)
{
await writerWorkspace.DeleteNoteAsync(id);
return RedirectForScene(sceneId, projectId, bookId);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> SaveChecklistItem(SceneChecklistItemEditViewModel model)
{
if (string.IsNullOrWhiteSpace(model.ItemText))
{
TempData["WriterWorkspaceError"] = "Enter checklist text before saving.";
return RedirectForScene(model.SceneID, model.ReturnProjectID, model.ReturnBookID);
}
await writerWorkspace.SaveChecklistItemAsync(model);
return RedirectForScene(model.SceneID, model.ReturnProjectID, model.ReturnBookID);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteChecklistItem(int id, int sceneId, int? projectId, int? bookId)
{
await writerWorkspace.DeleteChecklistItemAsync(id);
return RedirectForScene(sceneId, projectId, bookId);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> SaveAttachment(SceneAttachmentEditViewModel model)
{
if (string.IsNullOrWhiteSpace(model.Title))
{
TempData["WriterWorkspaceError"] = "Enter an attachment title before saving.";
return RedirectForScene(model.SceneID, model.ReturnProjectID, model.ReturnBookID);
}
await writerWorkspace.SaveAttachmentAsync(model);
return RedirectForScene(model.SceneID, model.ReturnProjectID, model.ReturnBookID);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteAttachment(int id, int sceneId, int? projectId, int? bookId)
{
await writerWorkspace.DeleteAttachmentAsync(id);
return RedirectForScene(sceneId, projectId, bookId);
}
private IActionResult RedirectForScene(int sceneId, int? projectId, int? bookId)
{
if (projectId.HasValue)

View File

@ -0,0 +1,28 @@
using Microsoft.AspNetCore.Mvc;
using PlotLine.Services;
using PlotLine.ViewModels;
namespace PlotLine.Controllers;
public sealed class WriterController(IWriterWorkspaceService writerWorkspace) : Controller
{
public async Task<IActionResult> Index(int? projectId)
{
var model = await writerWorkspace.GetDashboardAsync(projectId);
return View(model);
}
public async Task<IActionResult> Chapter(int id)
{
var model = await writerWorkspace.GetChapterWorkflowAsync(id);
return model is null ? NotFound() : View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> SaveChapterGoal(ChapterGoalEditViewModel model)
{
await writerWorkspace.SaveChapterGoalAsync(model);
return RedirectToAction(nameof(Chapter), new { id = model.ChapterID });
}
}

View File

@ -190,6 +190,28 @@ public interface IStoryBibleRepository
Task<IReadOnlyList<StoryBibleSearchResult>> SearchAsync(int projectId, string? query);
}
public interface IWriterWorkspaceRepository
{
Task<IReadOnlyList<SceneNoteType>> ListNoteTypesAsync();
Task<IReadOnlyList<SceneAttachmentType>> ListAttachmentTypesAsync();
Task<SceneWorkflow> GetWorkflowAsync(int sceneId);
Task SaveWorkflowAsync(SceneWorkflow workflow);
Task<IReadOnlyList<SceneNote>> ListNotesAsync(int sceneId);
Task<int> SaveNoteAsync(SceneNote note);
Task DeleteNoteAsync(int sceneNoteId);
Task<IReadOnlyList<SceneChecklistItem>> ListChecklistItemsAsync(int sceneId);
Task<int> SaveChecklistItemAsync(SceneChecklistItem item);
Task DeleteChecklistItemAsync(int sceneChecklistItemId);
Task<IReadOnlyList<SceneAttachment>> ListAttachmentsAsync(int sceneId);
Task<int> SaveAttachmentAsync(SceneAttachment attachment);
Task DeleteAttachmentAsync(int sceneAttachmentId);
Task<ChapterGoal> GetChapterGoalAsync(int chapterId);
Task SaveChapterGoalAsync(ChapterGoal goal);
Task<IReadOnlyList<ChapterWorkflowScene>> GetChapterWorkflowAsync(int chapterId);
Task<IReadOnlyList<WriterDashboardScene>> GetWriterDashboardScenesAsync(int? projectId, string queue);
Task<IReadOnlyList<StoryBibleAttentionItem>> GetWriterDashboardRemindersAsync(int? projectId);
}
public sealed class TimelineData
{
public Project? Project { get; init; }
@ -561,6 +583,154 @@ public sealed class StoryBibleRepository(ISqlConnectionFactory connectionFactory
}
}
public sealed class WriterWorkspaceRepository(ISqlConnectionFactory connectionFactory) : IWriterWorkspaceRepository
{
public async Task<IReadOnlyList<SceneNoteType>> ListNoteTypesAsync()
{
using var connection = connectionFactory.CreateConnection();
var rows = await connection.QueryAsync<SceneNoteType>("dbo.SceneNoteType_List", commandType: CommandType.StoredProcedure);
return rows.ToList();
}
public async Task<IReadOnlyList<SceneAttachmentType>> ListAttachmentTypesAsync()
{
using var connection = connectionFactory.CreateConnection();
var rows = await connection.QueryAsync<SceneAttachmentType>("dbo.SceneAttachmentType_List", commandType: CommandType.StoredProcedure);
return rows.ToList();
}
public async Task<SceneWorkflow> GetWorkflowAsync(int sceneId)
{
using var connection = connectionFactory.CreateConnection();
return await connection.QuerySingleAsync<SceneWorkflow>("dbo.SceneWorkflow_Get", new { SceneID = sceneId }, commandType: CommandType.StoredProcedure);
}
public async Task SaveWorkflowAsync(SceneWorkflow workflow)
{
using var connection = connectionFactory.CreateConnection();
await connection.ExecuteAsync(
"dbo.SceneWorkflow_Save",
new
{
workflow.SceneID,
workflow.DraftStatus,
workflow.EstimatedWordCount,
workflow.ActualWordCount,
workflow.DraftedDate,
workflow.LastWorkedOn,
workflow.ReadyForDraft,
workflow.ReadyForRevision,
workflow.ReadyForPolish,
workflow.IsBlocked,
workflow.BlockedReason,
workflow.Priority
},
commandType: CommandType.StoredProcedure);
}
public async Task<IReadOnlyList<SceneNote>> ListNotesAsync(int sceneId)
{
using var connection = connectionFactory.CreateConnection();
var rows = await connection.QueryAsync<SceneNote>("dbo.SceneNote_ListByScene", new { SceneID = sceneId }, commandType: CommandType.StoredProcedure);
return rows.ToList();
}
public async Task<int> SaveNoteAsync(SceneNote note)
{
using var connection = connectionFactory.CreateConnection();
return await connection.QuerySingleAsync<int>(
"dbo.SceneNote_Save",
new { note.SceneNoteID, note.SceneID, note.SceneNoteTypeID, note.NoteTitle, note.NoteText, note.SortOrder, note.IsPinned, note.IsResolved },
commandType: CommandType.StoredProcedure);
}
public async Task DeleteNoteAsync(int sceneNoteId)
{
using var connection = connectionFactory.CreateConnection();
await connection.ExecuteAsync("dbo.SceneNote_Delete", new { SceneNoteID = sceneNoteId }, commandType: CommandType.StoredProcedure);
}
public async Task<IReadOnlyList<SceneChecklistItem>> ListChecklistItemsAsync(int sceneId)
{
using var connection = connectionFactory.CreateConnection();
var rows = await connection.QueryAsync<SceneChecklistItem>("dbo.SceneChecklist_ListByScene", new { SceneID = sceneId }, commandType: CommandType.StoredProcedure);
return rows.ToList();
}
public async Task<int> SaveChecklistItemAsync(SceneChecklistItem item)
{
using var connection = connectionFactory.CreateConnection();
return await connection.QuerySingleAsync<int>(
"dbo.SceneChecklist_Save",
new { item.SceneChecklistItemID, item.SceneID, item.ItemText, item.IsCompleted, item.SortOrder },
commandType: CommandType.StoredProcedure);
}
public async Task DeleteChecklistItemAsync(int sceneChecklistItemId)
{
using var connection = connectionFactory.CreateConnection();
await connection.ExecuteAsync("dbo.SceneChecklist_Delete", new { SceneChecklistItemID = sceneChecklistItemId }, commandType: CommandType.StoredProcedure);
}
public async Task<IReadOnlyList<SceneAttachment>> ListAttachmentsAsync(int sceneId)
{
using var connection = connectionFactory.CreateConnection();
var rows = await connection.QueryAsync<SceneAttachment>("dbo.SceneAttachment_ListByScene", new { SceneID = sceneId }, commandType: CommandType.StoredProcedure);
return rows.ToList();
}
public async Task<int> SaveAttachmentAsync(SceneAttachment attachment)
{
using var connection = connectionFactory.CreateConnection();
return await connection.QuerySingleAsync<int>(
"dbo.SceneAttachment_Save",
new { attachment.SceneAttachmentID, attachment.SceneID, attachment.SceneAttachmentTypeID, attachment.Title, attachment.FilePath, attachment.ExternalUrl, attachment.Notes },
commandType: CommandType.StoredProcedure);
}
public async Task DeleteAttachmentAsync(int sceneAttachmentId)
{
using var connection = connectionFactory.CreateConnection();
await connection.ExecuteAsync("dbo.SceneAttachment_Delete", new { SceneAttachmentID = sceneAttachmentId }, commandType: CommandType.StoredProcedure);
}
public async Task<ChapterGoal> GetChapterGoalAsync(int chapterId)
{
using var connection = connectionFactory.CreateConnection();
return await connection.QuerySingleAsync<ChapterGoal>("dbo.ChapterGoal_Get", new { ChapterID = chapterId }, commandType: CommandType.StoredProcedure);
}
public async Task SaveChapterGoalAsync(ChapterGoal goal)
{
using var connection = connectionFactory.CreateConnection();
await connection.ExecuteAsync(
"dbo.ChapterGoal_Save",
new { goal.ChapterID, goal.GoalSummary, goal.EmotionalGoal, goal.ReaderTakeaway, goal.MustInclude, goal.RisksOrConcerns },
commandType: CommandType.StoredProcedure);
}
public async Task<IReadOnlyList<ChapterWorkflowScene>> GetChapterWorkflowAsync(int chapterId)
{
using var connection = connectionFactory.CreateConnection();
var rows = await connection.QueryAsync<ChapterWorkflowScene>("dbo.ChapterWorkflow_Summary", new { ChapterID = chapterId }, commandType: CommandType.StoredProcedure);
return rows.ToList();
}
public async Task<IReadOnlyList<WriterDashboardScene>> GetWriterDashboardScenesAsync(int? projectId, string queue)
{
using var connection = connectionFactory.CreateConnection();
var rows = await connection.QueryAsync<WriterDashboardScene>("dbo.WriterDashboard_Scenes", new { ProjectID = projectId, Queue = queue }, commandType: CommandType.StoredProcedure);
return rows.ToList();
}
public async Task<IReadOnlyList<StoryBibleAttentionItem>> GetWriterDashboardRemindersAsync(int? projectId)
{
using var connection = connectionFactory.CreateConnection();
var rows = await connection.QueryAsync<StoryBibleAttentionItem>("dbo.WriterDashboard_Reminders", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure);
return rows.ToList();
}
}
public sealed class SceneDependencyRepository(ISqlConnectionFactory connectionFactory) : ISceneDependencyRepository
{
public async Task<SceneDependencyLookupData> GetLookupsAsync()

View File

@ -1147,6 +1147,135 @@ public sealed class StoryBibleSearchResult
public int? SceneID { get; set; }
}
public sealed class SceneNoteType
{
public int SceneNoteTypeID { get; set; }
public string TypeName { get; set; } = string.Empty;
public int SortOrder { get; set; }
public bool IsActive { get; set; }
}
public sealed class SceneNote
{
public int SceneNoteID { get; set; }
public int SceneID { get; set; }
public int SceneNoteTypeID { get; set; }
public string TypeName { get; set; } = string.Empty;
public string? NoteTitle { get; set; }
public string NoteText { get; set; } = string.Empty;
public int? SortOrder { get; set; }
public bool IsPinned { get; set; }
public bool IsResolved { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime UpdatedDate { get; set; }
}
public sealed class ChapterGoal
{
public int ChapterGoalID { get; set; }
public int ChapterID { get; set; }
public string? GoalSummary { get; set; }
public string? EmotionalGoal { get; set; }
public string? ReaderTakeaway { get; set; }
public string? MustInclude { get; set; }
public string? RisksOrConcerns { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime UpdatedDate { get; set; }
}
public sealed class SceneWorkflow
{
public int SceneWorkflowID { get; set; }
public int SceneID { get; set; }
public string DraftStatus { get; set; } = "Planned";
public int? EstimatedWordCount { get; set; }
public int? ActualWordCount { get; set; }
public DateTime? DraftedDate { get; set; }
public DateTime? LastWorkedOn { get; set; }
public bool ReadyForDraft { get; set; }
public bool ReadyForRevision { get; set; }
public bool ReadyForPolish { get; set; }
public bool IsBlocked { get; set; }
public string? BlockedReason { get; set; }
public int? Priority { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime UpdatedDate { get; set; }
}
public sealed class SceneChecklistItem
{
public int SceneChecklistItemID { get; set; }
public int SceneID { get; set; }
public string ItemText { get; set; } = string.Empty;
public bool IsCompleted { get; set; }
public int? SortOrder { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime UpdatedDate { get; set; }
}
public sealed class SceneAttachmentType
{
public int SceneAttachmentTypeID { get; set; }
public string TypeName { get; set; } = string.Empty;
public int SortOrder { get; set; }
public bool IsActive { get; set; }
}
public sealed class SceneAttachment
{
public int SceneAttachmentID { get; set; }
public int SceneID { get; set; }
public int SceneAttachmentTypeID { get; set; }
public string TypeName { get; set; } = string.Empty;
public string Title { get; set; } = string.Empty;
public string? FilePath { get; set; }
public string? ExternalUrl { get; set; }
public string? Notes { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime UpdatedDate { get; set; }
}
public sealed class WriterDashboardScene
{
public int ProjectID { get; set; }
public string ProjectName { get; set; } = string.Empty;
public int BookID { get; set; }
public string BookTitle { get; set; } = string.Empty;
public int ChapterID { get; set; }
public decimal ChapterNumber { get; set; }
public string ChapterTitle { get; set; } = string.Empty;
public int SceneID { get; set; }
public decimal SceneNumber { get; set; }
public string SceneTitle { get; set; } = string.Empty;
public string? Summary { get; set; }
public string DraftStatus { get; set; } = "Planned";
public int? Priority { get; set; }
public bool IsBlocked { get; set; }
public string? BlockedReason { get; set; }
public int WarningCount { get; set; }
public int RemainingChecklistCount { get; set; }
public int UnresolvedNoteCount { get; set; }
public string? ImportantThreads { get; set; }
public string? PinnedNotes { get; set; }
}
public sealed class ChapterWorkflowScene
{
public int SceneID { get; set; }
public decimal SceneNumber { get; set; }
public string SceneTitle { get; set; } = string.Empty;
public string DraftStatus { get; set; } = "Planned";
public int? Priority { get; set; }
public bool ReadyForDraft { get; set; }
public bool ReadyForRevision { get; set; }
public bool ReadyForPolish { get; set; }
public bool IsBlocked { get; set; }
public string? BlockedReason { get; set; }
public int WarningCount { get; set; }
public int RemainingChecklistCount { get; set; }
public int UnresolvedNoteCount { get; set; }
}
public sealed class AnalyticsBusyScene
{
public int SceneID { get; set; }

View File

@ -33,6 +33,7 @@ public class Program
builder.Services.AddScoped<ISceneDependencyRepository, SceneDependencyRepository>();
builder.Services.AddScoped<IAnalyticsRepository, AnalyticsRepository>();
builder.Services.AddScoped<IStoryBibleRepository, StoryBibleRepository>();
builder.Services.AddScoped<IWriterWorkspaceRepository, WriterWorkspaceRepository>();
builder.Services.AddScoped<IProjectService, ProjectService>();
builder.Services.AddScoped<IBookService, BookService>();
builder.Services.AddScoped<IChapterService, ChapterService>();
@ -45,6 +46,7 @@ public class Program
builder.Services.AddScoped<IContinuityValidationService, ContinuityValidationService>();
builder.Services.AddScoped<IAnalyticsService, AnalyticsService>();
builder.Services.AddScoped<IStoryBibleService, StoryBibleService>();
builder.Services.AddScoped<IWriterWorkspaceService, WriterWorkspaceService>();
var app = builder.Build();

View File

@ -150,6 +150,20 @@ public interface IStoryBibleService
Task<StoryBibleViewModel?> GetAsync(int projectId, string? query);
}
public interface IWriterWorkspaceService
{
Task<WriterDashboardViewModel> GetDashboardAsync(int? projectId);
Task<ChapterWorkflowViewModel?> GetChapterWorkflowAsync(int chapterId);
Task SaveChapterGoalAsync(ChapterGoalEditViewModel model);
Task SaveSceneWorkflowAsync(SceneWorkflowEditViewModel model);
Task<int> SaveNoteAsync(SceneNoteEditViewModel model);
Task DeleteNoteAsync(int sceneNoteId);
Task<int> SaveChecklistItemAsync(SceneChecklistItemEditViewModel model);
Task DeleteChecklistItemAsync(int sceneChecklistItemId);
Task<int> SaveAttachmentAsync(SceneAttachmentEditViewModel model);
Task DeleteAttachmentAsync(int sceneAttachmentId);
}
public sealed class ProjectService(IProjectRepository projects, IBookRepository books) : IProjectService
{
public Task<IReadOnlyList<Project>> ListAsync() => projects.ListAsync();
@ -376,7 +390,8 @@ public sealed class SceneService(
ICharacterRepository characters,
ILocationRepository locations,
IWarningRepository warnings,
ISceneDependencyRepository sceneDependencies) : ISceneService
ISceneDependencyRepository sceneDependencies,
IWriterWorkspaceRepository writerWorkspace) : ISceneService
{
public async Task<SceneEditViewModel?> GetCreateAsync(int chapterId)
{
@ -552,6 +567,17 @@ public sealed class SceneService(
model.DependenciesNeedingThisScene = sceneDependencyRows.Where(x => x.SourceSceneID == model.SceneID).ToList();
model.CharacterKnowledge = model.SceneID == 0 ? [] : await characters.ListKnowledgeBySceneAsync(model.SceneID);
model.RelationshipEvents = model.SceneID == 0 ? [] : await characters.ListRelationshipEventsBySceneAsync(model.SceneID);
var noteTypes = await writerWorkspace.ListNoteTypesAsync();
var attachmentTypes = await writerWorkspace.ListAttachmentTypesAsync();
model.Workflow = model.SceneID == 0 ? new SceneWorkflow() : await writerWorkspace.GetWorkflowAsync(model.SceneID);
model.Notes = model.SceneID == 0 ? [] : await writerWorkspace.ListNotesAsync(model.SceneID);
model.ChecklistItems = model.SceneID == 0 ? [] : await writerWorkspace.ListChecklistItemsAsync(model.SceneID);
model.Attachments = model.SceneID == 0 ? [] : await writerWorkspace.ListAttachmentsAsync(model.SceneID);
model.SceneNoteTypeOptions = ChapterService.ToSelectList(noteTypes, x => x.SceneNoteTypeID, x => x.TypeName);
model.SceneAttachmentTypeOptions = ChapterService.ToSelectList(attachmentTypes, x => x.SceneAttachmentTypeID, x => x.TypeName);
model.DraftStatusOptions = DraftStatuses()
.Select(x => new SelectListItem(x, x, string.Equals(x, model.Workflow.DraftStatus, StringComparison.OrdinalIgnoreCase)))
.ToList();
model.PlotLineOptions = ToOptionalSelectList(plotLines, x => x.PlotLineID, x => x.PlotLineName);
model.PlotThreadOptions = ToOptionalSelectList(plotThreads, x => x.PlotThreadID, x => $"{x.PlotLineName}: {x.ThreadTitle}");
model.ThreadTypeOptions = ToOptionalSelectList(plotLookups.ThreadTypes, x => x.ThreadTypeID, x => x.TypeName);
@ -656,6 +682,30 @@ public sealed class SceneService(
?? dependencyLookups.DependencyTypes.FirstOrDefault()?.SceneDependencyTypeID
?? 0
};
model.NewNote = new SceneNoteEditViewModel
{
SceneID = model.SceneID,
ReturnProjectID = model.ReturnProjectID,
ReturnBookID = model.ReturnBookID,
SceneNoteTypeID = noteTypes.FirstOrDefault(x => x.TypeName == "Drafting Note")?.SceneNoteTypeID
?? noteTypes.FirstOrDefault()?.SceneNoteTypeID
?? 0
};
model.NewChecklistItem = new SceneChecklistItemEditViewModel
{
SceneID = model.SceneID,
ReturnProjectID = model.ReturnProjectID,
ReturnBookID = model.ReturnBookID
};
model.NewAttachment = new SceneAttachmentEditViewModel
{
SceneID = model.SceneID,
ReturnProjectID = model.ReturnProjectID,
ReturnBookID = model.ReturnBookID,
SceneAttachmentTypeID = attachmentTypes.FirstOrDefault(x => x.TypeName == "Link")?.SceneAttachmentTypeID
?? attachmentTypes.FirstOrDefault()?.SceneAttachmentTypeID
?? 0
};
model.MoveRequest = new SceneMoveRequestViewModel
{
SceneID = model.SceneID,
@ -667,6 +717,18 @@ public sealed class SceneService(
return model;
}
internal static IReadOnlyList<string> DraftStatuses() =>
[
"Planned",
"Ready To Draft",
"Drafting",
"Drafted",
"Revising",
"Polishing",
"Complete",
"On Hold"
];
private static IReadOnlyList<SelectListItem> ToOptionalSelectList<T>(IEnumerable<T> rows, Func<T, int> value, Func<T, string> text)
=> rows.Select(x => new SelectListItem(text(x), value(x).ToString())).ToList();
@ -1726,6 +1788,112 @@ public sealed class StoryBibleService(IProjectRepository projects, IStoryBibleRe
}
}
public sealed class WriterWorkspaceService(
IProjectRepository projects,
IBookRepository books,
IChapterRepository chapters,
IWriterWorkspaceRepository writerWorkspace) : IWriterWorkspaceService
{
public async Task<WriterDashboardViewModel> GetDashboardAsync(int? projectId)
{
var projectRows = await projects.ListAsync();
return new WriterDashboardViewModel
{
ProjectID = projectId,
ProjectOptions = projectRows.Select(x => new SelectListItem(x.ProjectName, x.ProjectID.ToString(), x.ProjectID == projectId)).ToList(),
ContinueWriting = await writerWorkspace.GetWriterDashboardScenesAsync(projectId, "ContinueWriting"),
NeedsAttention = await writerWorkspace.GetWriterDashboardScenesAsync(projectId, "NeedsAttention"),
RevisionQueue = await writerWorkspace.GetWriterDashboardScenesAsync(projectId, "RevisionQueue"),
PolishingQueue = await writerWorkspace.GetWriterDashboardScenesAsync(projectId, "PolishingQueue"),
StoryHealthReminders = await writerWorkspace.GetWriterDashboardRemindersAsync(projectId)
};
}
public async Task<ChapterWorkflowViewModel?> GetChapterWorkflowAsync(int chapterId)
{
var chapter = await chapters.GetAsync(chapterId);
var book = chapter is null ? null : await books.GetAsync(chapter.BookID);
var project = book is null ? null : await projects.GetAsync(book.ProjectID);
if (chapter is null || book is null || project is null)
{
return null;
}
return new ChapterWorkflowViewModel
{
Project = project,
Book = book,
Chapter = chapter,
Goal = await writerWorkspace.GetChapterGoalAsync(chapterId),
Scenes = await writerWorkspace.GetChapterWorkflowAsync(chapterId)
};
}
public Task SaveChapterGoalAsync(ChapterGoalEditViewModel model) => writerWorkspace.SaveChapterGoalAsync(new ChapterGoal
{
ChapterID = model.ChapterID,
GoalSummary = model.GoalSummary,
EmotionalGoal = model.EmotionalGoal,
ReaderTakeaway = model.ReaderTakeaway,
MustInclude = model.MustInclude,
RisksOrConcerns = model.RisksOrConcerns
});
public Task SaveSceneWorkflowAsync(SceneWorkflowEditViewModel model) => writerWorkspace.SaveWorkflowAsync(new SceneWorkflow
{
SceneID = model.SceneID,
DraftStatus = model.DraftStatus,
EstimatedWordCount = model.EstimatedWordCount,
ActualWordCount = model.ActualWordCount,
DraftedDate = model.DraftedDate,
LastWorkedOn = model.LastWorkedOn,
ReadyForDraft = model.ReadyForDraft,
ReadyForRevision = model.ReadyForRevision,
ReadyForPolish = model.ReadyForPolish,
IsBlocked = model.IsBlocked,
BlockedReason = model.BlockedReason,
Priority = model.Priority
});
public Task<int> SaveNoteAsync(SceneNoteEditViewModel model) => writerWorkspace.SaveNoteAsync(new SceneNote
{
SceneNoteID = model.SceneNoteID,
SceneID = model.SceneID,
SceneNoteTypeID = model.SceneNoteTypeID,
NoteTitle = model.NoteTitle,
NoteText = model.NoteText,
SortOrder = model.SortOrder,
IsPinned = model.IsPinned,
IsResolved = model.IsResolved
});
public Task DeleteNoteAsync(int sceneNoteId) => writerWorkspace.DeleteNoteAsync(sceneNoteId);
public Task<int> SaveChecklistItemAsync(SceneChecklistItemEditViewModel model) => writerWorkspace.SaveChecklistItemAsync(new SceneChecklistItem
{
SceneChecklistItemID = model.SceneChecklistItemID,
SceneID = model.SceneID,
ItemText = model.ItemText,
IsCompleted = model.IsCompleted,
SortOrder = model.SortOrder
});
public Task DeleteChecklistItemAsync(int sceneChecklistItemId) => writerWorkspace.DeleteChecklistItemAsync(sceneChecklistItemId);
public Task<int> SaveAttachmentAsync(SceneAttachmentEditViewModel model) => writerWorkspace.SaveAttachmentAsync(new SceneAttachment
{
SceneAttachmentID = model.SceneAttachmentID,
SceneID = model.SceneID,
SceneAttachmentTypeID = model.SceneAttachmentTypeID,
Title = model.Title,
FilePath = model.FilePath,
ExternalUrl = model.ExternalUrl,
Notes = model.Notes
});
public Task DeleteAttachmentAsync(int sceneAttachmentId) => writerWorkspace.DeleteAttachmentAsync(sceneAttachmentId);
}
public sealed class AssetService(IProjectRepository projects, IAssetRepository assets, ILocationRepository locations) : IAssetService
{
public async Task<StoryAssetListViewModel?> GetAssetsAsync(int projectId)

View File

@ -0,0 +1,714 @@
SET ANSI_NULLS ON;
GO
SET QUOTED_IDENTIFIER ON;
GO
IF OBJECT_ID(N'dbo.SceneNoteTypes', N'U') IS NULL
BEGIN
CREATE TABLE dbo.SceneNoteTypes
(
SceneNoteTypeID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_SceneNoteTypes PRIMARY KEY,
TypeName nvarchar(100) NOT NULL,
SortOrder int NOT NULL,
IsActive bit NOT NULL CONSTRAINT DF_SceneNoteTypes_IsActive DEFAULT 1,
CONSTRAINT UQ_SceneNoteTypes_TypeName UNIQUE (TypeName)
);
END;
GO
IF OBJECT_ID(N'dbo.SceneNotes', N'U') IS NULL
BEGIN
CREATE TABLE dbo.SceneNotes
(
SceneNoteID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_SceneNotes PRIMARY KEY,
SceneID int NOT NULL,
SceneNoteTypeID int NOT NULL,
NoteTitle nvarchar(200) NULL,
NoteText nvarchar(max) NOT NULL,
SortOrder int NULL,
IsPinned bit NOT NULL CONSTRAINT DF_SceneNotes_IsPinned DEFAULT 0,
IsResolved bit NOT NULL CONSTRAINT DF_SceneNotes_IsResolved DEFAULT 0,
CreatedDate datetime2 NOT NULL CONSTRAINT DF_SceneNotes_CreatedDate DEFAULT SYSUTCDATETIME(),
UpdatedDate datetime2 NOT NULL CONSTRAINT DF_SceneNotes_UpdatedDate DEFAULT SYSUTCDATETIME(),
CONSTRAINT FK_SceneNotes_Scenes FOREIGN KEY (SceneID) REFERENCES dbo.Scenes(SceneID),
CONSTRAINT FK_SceneNotes_Types FOREIGN KEY (SceneNoteTypeID) REFERENCES dbo.SceneNoteTypes(SceneNoteTypeID)
);
END;
GO
IF OBJECT_ID(N'dbo.ChapterGoals', N'U') IS NULL
BEGIN
CREATE TABLE dbo.ChapterGoals
(
ChapterGoalID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_ChapterGoals PRIMARY KEY,
ChapterID int NOT NULL,
GoalSummary nvarchar(max) NULL,
EmotionalGoal nvarchar(max) NULL,
ReaderTakeaway nvarchar(max) NULL,
MustInclude nvarchar(max) NULL,
RisksOrConcerns nvarchar(max) NULL,
CreatedDate datetime2 NOT NULL CONSTRAINT DF_ChapterGoals_CreatedDate DEFAULT SYSUTCDATETIME(),
UpdatedDate datetime2 NOT NULL CONSTRAINT DF_ChapterGoals_UpdatedDate DEFAULT SYSUTCDATETIME(),
CONSTRAINT FK_ChapterGoals_Chapters FOREIGN KEY (ChapterID) REFERENCES dbo.Chapters(ChapterID),
CONSTRAINT UQ_ChapterGoals_ChapterID UNIQUE (ChapterID)
);
END;
GO
IF OBJECT_ID(N'dbo.SceneWorkflow', N'U') IS NULL
BEGIN
CREATE TABLE dbo.SceneWorkflow
(
SceneWorkflowID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_SceneWorkflow PRIMARY KEY,
SceneID int NOT NULL,
DraftStatus nvarchar(50) NOT NULL CONSTRAINT DF_SceneWorkflow_DraftStatus DEFAULT N'Planned',
EstimatedWordCount int NULL,
ActualWordCount int NULL,
DraftedDate date NULL,
LastWorkedOn date NULL,
ReadyForDraft bit NOT NULL CONSTRAINT DF_SceneWorkflow_ReadyForDraft DEFAULT 0,
ReadyForRevision bit NOT NULL CONSTRAINT DF_SceneWorkflow_ReadyForRevision DEFAULT 0,
ReadyForPolish bit NOT NULL CONSTRAINT DF_SceneWorkflow_ReadyForPolish DEFAULT 0,
IsBlocked bit NOT NULL CONSTRAINT DF_SceneWorkflow_IsBlocked DEFAULT 0,
BlockedReason nvarchar(max) NULL,
Priority int NULL,
CreatedDate datetime2 NOT NULL CONSTRAINT DF_SceneWorkflow_CreatedDate DEFAULT SYSUTCDATETIME(),
UpdatedDate datetime2 NOT NULL CONSTRAINT DF_SceneWorkflow_UpdatedDate DEFAULT SYSUTCDATETIME(),
CONSTRAINT FK_SceneWorkflow_Scenes FOREIGN KEY (SceneID) REFERENCES dbo.Scenes(SceneID),
CONSTRAINT UQ_SceneWorkflow_SceneID UNIQUE (SceneID),
CONSTRAINT CK_SceneWorkflow_DraftStatus CHECK (DraftStatus IN (N'Planned', N'Ready To Draft', N'Drafting', N'Drafted', N'Revising', N'Polishing', N'Complete', N'On Hold')),
CONSTRAINT CK_SceneWorkflow_Priority CHECK (Priority IS NULL OR Priority BETWEEN 1 AND 10),
CONSTRAINT CK_SceneWorkflow_WordCounts CHECK ((EstimatedWordCount IS NULL OR EstimatedWordCount >= 0) AND (ActualWordCount IS NULL OR ActualWordCount >= 0))
);
END;
GO
IF OBJECT_ID(N'dbo.SceneChecklistItems', N'U') IS NULL
BEGIN
CREATE TABLE dbo.SceneChecklistItems
(
SceneChecklistItemID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_SceneChecklistItems PRIMARY KEY,
SceneID int NOT NULL,
ItemText nvarchar(300) NOT NULL,
IsCompleted bit NOT NULL CONSTRAINT DF_SceneChecklistItems_IsCompleted DEFAULT 0,
SortOrder int NULL,
CreatedDate datetime2 NOT NULL CONSTRAINT DF_SceneChecklistItems_CreatedDate DEFAULT SYSUTCDATETIME(),
UpdatedDate datetime2 NOT NULL CONSTRAINT DF_SceneChecklistItems_UpdatedDate DEFAULT SYSUTCDATETIME(),
CONSTRAINT FK_SceneChecklistItems_Scenes FOREIGN KEY (SceneID) REFERENCES dbo.Scenes(SceneID)
);
END;
GO
IF OBJECT_ID(N'dbo.SceneAttachmentTypes', N'U') IS NULL
BEGIN
CREATE TABLE dbo.SceneAttachmentTypes
(
SceneAttachmentTypeID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_SceneAttachmentTypes PRIMARY KEY,
TypeName nvarchar(100) NOT NULL,
SortOrder int NOT NULL,
IsActive bit NOT NULL CONSTRAINT DF_SceneAttachmentTypes_IsActive DEFAULT 1,
CONSTRAINT UQ_SceneAttachmentTypes_TypeName UNIQUE (TypeName)
);
END;
GO
IF OBJECT_ID(N'dbo.SceneAttachments', N'U') IS NULL
BEGIN
CREATE TABLE dbo.SceneAttachments
(
SceneAttachmentID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_SceneAttachments PRIMARY KEY,
SceneID int NOT NULL,
SceneAttachmentTypeID int NOT NULL,
Title nvarchar(200) NOT NULL,
FilePath nvarchar(500) NULL,
ExternalUrl nvarchar(1000) NULL,
Notes nvarchar(max) NULL,
CreatedDate datetime2 NOT NULL CONSTRAINT DF_SceneAttachments_CreatedDate DEFAULT SYSUTCDATETIME(),
UpdatedDate datetime2 NOT NULL CONSTRAINT DF_SceneAttachments_UpdatedDate DEFAULT SYSUTCDATETIME(),
CONSTRAINT FK_SceneAttachments_Scenes FOREIGN KEY (SceneID) REFERENCES dbo.Scenes(SceneID),
CONSTRAINT FK_SceneAttachments_Types FOREIGN KEY (SceneAttachmentTypeID) REFERENCES dbo.SceneAttachmentTypes(SceneAttachmentTypeID)
);
END;
GO
MERGE dbo.SceneNoteTypes AS target
USING (VALUES
(N'Drafting Note', 10), (N'Revision Note', 20), (N'Continuity Note', 30), (N'Research Note', 40),
(N'Emotional Intent', 50), (N'Dialogue Note', 60), (N'Sensory Note', 70), (N'Timeline Note', 80),
(N'Plot Reminder', 90), (N'Character Reminder', 100), (N'TODO', 110)
) AS source (TypeName, SortOrder)
ON target.TypeName = source.TypeName
WHEN MATCHED THEN UPDATE SET SortOrder = source.SortOrder, IsActive = 1
WHEN NOT MATCHED THEN INSERT (TypeName, SortOrder, IsActive) VALUES (source.TypeName, source.SortOrder, 1);
GO
MERGE dbo.SceneAttachmentTypes AS target
USING (VALUES
(N'Image', 10), (N'Link', 20), (N'Research Reference', 30), (N'Historical Reference', 40),
(N'Character Reference', 50), (N'Location Reference', 60), (N'Continuity Reference', 70), (N'General File', 80)
) AS source (TypeName, SortOrder)
ON target.TypeName = source.TypeName
WHEN MATCHED THEN UPDATE SET SortOrder = source.SortOrder, IsActive = 1
WHEN NOT MATCHED THEN INSERT (TypeName, SortOrder, IsActive) VALUES (source.TypeName, source.SortOrder, 1);
GO
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_SceneNotes_SceneID' AND object_id = OBJECT_ID(N'dbo.SceneNotes'))
CREATE INDEX IX_SceneNotes_SceneID ON dbo.SceneNotes(SceneID, IsResolved, IsPinned, SortOrder);
GO
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_SceneChecklistItems_SceneID' AND object_id = OBJECT_ID(N'dbo.SceneChecklistItems'))
CREATE INDEX IX_SceneChecklistItems_SceneID ON dbo.SceneChecklistItems(SceneID, IsCompleted, SortOrder);
GO
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_SceneAttachments_SceneID' AND object_id = OBJECT_ID(N'dbo.SceneAttachments'))
CREATE INDEX IX_SceneAttachments_SceneID ON dbo.SceneAttachments(SceneID, SceneAttachmentTypeID);
GO
CREATE OR ALTER PROCEDURE dbo.SceneNoteType_List
AS
BEGIN
SET NOCOUNT ON;
SELECT SceneNoteTypeID, TypeName, SortOrder, IsActive
FROM dbo.SceneNoteTypes
WHERE IsActive = 1
ORDER BY SortOrder, TypeName;
END;
GO
CREATE OR ALTER PROCEDURE dbo.SceneAttachmentType_List
AS
BEGIN
SET NOCOUNT ON;
SELECT SceneAttachmentTypeID, TypeName, SortOrder, IsActive
FROM dbo.SceneAttachmentTypes
WHERE IsActive = 1
ORDER BY SortOrder, TypeName;
END;
GO
CREATE OR ALTER PROCEDURE dbo.SceneWorkflow_Get
@SceneID int
AS
BEGIN
SET NOCOUNT ON;
IF NOT EXISTS (SELECT 1 FROM dbo.SceneWorkflow WHERE SceneID = @SceneID)
BEGIN
INSERT dbo.SceneWorkflow (SceneID)
VALUES (@SceneID);
END;
IF NOT EXISTS (SELECT 1 FROM dbo.SceneChecklistItems WHERE SceneID = @SceneID)
BEGIN
INSERT dbo.SceneChecklistItems (SceneID, ItemText, SortOrder)
VALUES
(@SceneID, N'Purpose clear', 10),
(@SceneID, N'Emotional payoff clear', 20),
(@SceneID, N'Continuity checked', 30),
(@SceneID, N'POV consistent', 40),
(@SceneID, N'Dialogue pass complete', 50),
(@SceneID, N'Sensory detail pass complete', 60),
(@SceneID, N'Character motivation clear', 70),
(@SceneID, N'Ending hook strong', 80);
END;
SELECT SceneWorkflowID, SceneID, DraftStatus, EstimatedWordCount, ActualWordCount, DraftedDate, LastWorkedOn,
ReadyForDraft, ReadyForRevision, ReadyForPolish, IsBlocked, BlockedReason, Priority, CreatedDate, UpdatedDate
FROM dbo.SceneWorkflow
WHERE SceneID = @SceneID;
END;
GO
CREATE OR ALTER PROCEDURE dbo.SceneWorkflow_Save
@SceneID int,
@DraftStatus nvarchar(50),
@EstimatedWordCount int = NULL,
@ActualWordCount int = NULL,
@DraftedDate date = NULL,
@LastWorkedOn date = NULL,
@ReadyForDraft bit = 0,
@ReadyForRevision bit = 0,
@ReadyForPolish bit = 0,
@IsBlocked bit = 0,
@BlockedReason nvarchar(max) = NULL,
@Priority int = NULL
AS
BEGIN
SET NOCOUNT ON;
IF EXISTS (SELECT 1 FROM dbo.SceneWorkflow WHERE SceneID = @SceneID)
BEGIN
UPDATE dbo.SceneWorkflow
SET DraftStatus = @DraftStatus,
EstimatedWordCount = @EstimatedWordCount,
ActualWordCount = @ActualWordCount,
DraftedDate = @DraftedDate,
LastWorkedOn = @LastWorkedOn,
ReadyForDraft = @ReadyForDraft,
ReadyForRevision = @ReadyForRevision,
ReadyForPolish = @ReadyForPolish,
IsBlocked = @IsBlocked,
BlockedReason = @BlockedReason,
Priority = @Priority,
UpdatedDate = SYSUTCDATETIME()
WHERE SceneID = @SceneID;
END
ELSE
BEGIN
INSERT dbo.SceneWorkflow (SceneID, DraftStatus, EstimatedWordCount, ActualWordCount, DraftedDate, LastWorkedOn, ReadyForDraft, ReadyForRevision, ReadyForPolish, IsBlocked, BlockedReason, Priority)
VALUES (@SceneID, @DraftStatus, @EstimatedWordCount, @ActualWordCount, @DraftedDate, @LastWorkedOn, @ReadyForDraft, @ReadyForRevision, @ReadyForPolish, @IsBlocked, @BlockedReason, @Priority);
END;
END;
GO
CREATE OR ALTER PROCEDURE dbo.SceneNote_ListByScene
@SceneID int
AS
BEGIN
SET NOCOUNT ON;
SELECT n.SceneNoteID, n.SceneID, n.SceneNoteTypeID, nt.TypeName, n.NoteTitle, n.NoteText, n.SortOrder,
n.IsPinned, n.IsResolved, n.CreatedDate, n.UpdatedDate
FROM dbo.SceneNotes n
INNER JOIN dbo.SceneNoteTypes nt ON nt.SceneNoteTypeID = n.SceneNoteTypeID
WHERE n.SceneID = @SceneID
ORDER BY n.IsPinned DESC, n.IsResolved, ISNULL(n.SortOrder, 9999), n.UpdatedDate DESC;
END;
GO
CREATE OR ALTER PROCEDURE dbo.SceneNote_Save
@SceneNoteID int = 0,
@SceneID int,
@SceneNoteTypeID int,
@NoteTitle nvarchar(200) = NULL,
@NoteText nvarchar(max),
@SortOrder int = NULL,
@IsPinned bit = 0,
@IsResolved bit = 0
AS
BEGIN
SET NOCOUNT ON;
IF @SceneNoteID IS NULL OR @SceneNoteID = 0
BEGIN
INSERT dbo.SceneNotes (SceneID, SceneNoteTypeID, NoteTitle, NoteText, SortOrder, IsPinned, IsResolved)
VALUES (@SceneID, @SceneNoteTypeID, @NoteTitle, @NoteText, @SortOrder, @IsPinned, @IsResolved);
SET @SceneNoteID = CAST(SCOPE_IDENTITY() AS int);
END
ELSE
BEGIN
UPDATE dbo.SceneNotes
SET SceneNoteTypeID = @SceneNoteTypeID,
NoteTitle = @NoteTitle,
NoteText = @NoteText,
SortOrder = @SortOrder,
IsPinned = @IsPinned,
IsResolved = @IsResolved,
UpdatedDate = SYSUTCDATETIME()
WHERE SceneNoteID = @SceneNoteID;
END;
SELECT @SceneNoteID;
END;
GO
CREATE OR ALTER PROCEDURE dbo.SceneNote_Delete
@SceneNoteID int
AS
BEGIN
SET NOCOUNT ON;
DELETE dbo.SceneNotes WHERE SceneNoteID = @SceneNoteID;
END;
GO
CREATE OR ALTER PROCEDURE dbo.SceneChecklist_ListByScene
@SceneID int
AS
BEGIN
SET NOCOUNT ON;
SELECT SceneChecklistItemID, SceneID, ItemText, IsCompleted, SortOrder, CreatedDate, UpdatedDate
FROM dbo.SceneChecklistItems
WHERE SceneID = @SceneID
ORDER BY IsCompleted, ISNULL(SortOrder, 9999), SceneChecklistItemID;
END;
GO
CREATE OR ALTER PROCEDURE dbo.SceneChecklist_Save
@SceneChecklistItemID int = 0,
@SceneID int,
@ItemText nvarchar(300),
@IsCompleted bit = 0,
@SortOrder int = NULL
AS
BEGIN
SET NOCOUNT ON;
IF @SceneChecklistItemID IS NULL OR @SceneChecklistItemID = 0
BEGIN
INSERT dbo.SceneChecklistItems (SceneID, ItemText, IsCompleted, SortOrder)
VALUES (@SceneID, @ItemText, @IsCompleted, @SortOrder);
SET @SceneChecklistItemID = CAST(SCOPE_IDENTITY() AS int);
END
ELSE
BEGIN
UPDATE dbo.SceneChecklistItems
SET ItemText = @ItemText, IsCompleted = @IsCompleted, SortOrder = @SortOrder, UpdatedDate = SYSUTCDATETIME()
WHERE SceneChecklistItemID = @SceneChecklistItemID;
END;
SELECT @SceneChecklistItemID;
END;
GO
CREATE OR ALTER PROCEDURE dbo.SceneChecklist_Delete
@SceneChecklistItemID int
AS
BEGIN
SET NOCOUNT ON;
DELETE dbo.SceneChecklistItems WHERE SceneChecklistItemID = @SceneChecklistItemID;
END;
GO
CREATE OR ALTER PROCEDURE dbo.SceneAttachment_ListByScene
@SceneID int
AS
BEGIN
SET NOCOUNT ON;
SELECT a.SceneAttachmentID, a.SceneID, a.SceneAttachmentTypeID, at.TypeName, a.Title, a.FilePath, a.ExternalUrl, a.Notes, a.CreatedDate, a.UpdatedDate
FROM dbo.SceneAttachments a
INNER JOIN dbo.SceneAttachmentTypes at ON at.SceneAttachmentTypeID = a.SceneAttachmentTypeID
WHERE a.SceneID = @SceneID
ORDER BY a.UpdatedDate DESC, a.SceneAttachmentID DESC;
END;
GO
CREATE OR ALTER PROCEDURE dbo.SceneAttachment_Save
@SceneAttachmentID int = 0,
@SceneID int,
@SceneAttachmentTypeID int,
@Title nvarchar(200),
@FilePath nvarchar(500) = NULL,
@ExternalUrl nvarchar(1000) = NULL,
@Notes nvarchar(max) = NULL
AS
BEGIN
SET NOCOUNT ON;
IF @SceneAttachmentID IS NULL OR @SceneAttachmentID = 0
BEGIN
INSERT dbo.SceneAttachments (SceneID, SceneAttachmentTypeID, Title, FilePath, ExternalUrl, Notes)
VALUES (@SceneID, @SceneAttachmentTypeID, @Title, @FilePath, @ExternalUrl, @Notes);
SET @SceneAttachmentID = CAST(SCOPE_IDENTITY() AS int);
END
ELSE
BEGIN
UPDATE dbo.SceneAttachments
SET SceneAttachmentTypeID = @SceneAttachmentTypeID,
Title = @Title,
FilePath = @FilePath,
ExternalUrl = @ExternalUrl,
Notes = @Notes,
UpdatedDate = SYSUTCDATETIME()
WHERE SceneAttachmentID = @SceneAttachmentID;
END;
SELECT @SceneAttachmentID;
END;
GO
CREATE OR ALTER PROCEDURE dbo.SceneAttachment_Delete
@SceneAttachmentID int
AS
BEGIN
SET NOCOUNT ON;
DELETE dbo.SceneAttachments WHERE SceneAttachmentID = @SceneAttachmentID;
END;
GO
CREATE OR ALTER PROCEDURE dbo.ChapterGoal_Get
@ChapterID int
AS
BEGIN
SET NOCOUNT ON;
IF NOT EXISTS (SELECT 1 FROM dbo.ChapterGoals WHERE ChapterID = @ChapterID)
BEGIN
INSERT dbo.ChapterGoals (ChapterID) VALUES (@ChapterID);
END;
SELECT ChapterGoalID, ChapterID, GoalSummary, EmotionalGoal, ReaderTakeaway, MustInclude, RisksOrConcerns, CreatedDate, UpdatedDate
FROM dbo.ChapterGoals
WHERE ChapterID = @ChapterID;
END;
GO
CREATE OR ALTER PROCEDURE dbo.ChapterGoal_Save
@ChapterID int,
@GoalSummary nvarchar(max) = NULL,
@EmotionalGoal nvarchar(max) = NULL,
@ReaderTakeaway nvarchar(max) = NULL,
@MustInclude nvarchar(max) = NULL,
@RisksOrConcerns nvarchar(max) = NULL
AS
BEGIN
SET NOCOUNT ON;
IF EXISTS (SELECT 1 FROM dbo.ChapterGoals WHERE ChapterID = @ChapterID)
BEGIN
UPDATE dbo.ChapterGoals
SET GoalSummary = @GoalSummary,
EmotionalGoal = @EmotionalGoal,
ReaderTakeaway = @ReaderTakeaway,
MustInclude = @MustInclude,
RisksOrConcerns = @RisksOrConcerns,
UpdatedDate = SYSUTCDATETIME()
WHERE ChapterID = @ChapterID;
END
ELSE
BEGIN
INSERT dbo.ChapterGoals (ChapterID, GoalSummary, EmotionalGoal, ReaderTakeaway, MustInclude, RisksOrConcerns)
VALUES (@ChapterID, @GoalSummary, @EmotionalGoal, @ReaderTakeaway, @MustInclude, @RisksOrConcerns);
END;
END;
GO
CREATE OR ALTER PROCEDURE dbo.ChapterWorkflow_Summary
@ChapterID int
AS
BEGIN
SET NOCOUNT ON;
SELECT s.SceneID, s.SceneNumber, s.SceneTitle,
COALESCE(sw.DraftStatus, N'Planned') AS DraftStatus,
sw.Priority,
COALESCE(sw.ReadyForDraft, 0) AS ReadyForDraft,
COALESCE(sw.ReadyForRevision, 0) AS ReadyForRevision,
COALESCE(sw.ReadyForPolish, 0) AS ReadyForPolish,
COALESCE(sw.IsBlocked, 0) AS IsBlocked,
sw.BlockedReason,
ISNULL(warnings.WarningCount, 0) AS WarningCount,
ISNULL(checklist.RemainingChecklistCount, 0) AS RemainingChecklistCount,
ISNULL(notes.UnresolvedNoteCount, 0) AS UnresolvedNoteCount
FROM dbo.Scenes s
LEFT JOIN dbo.SceneWorkflow sw ON sw.SceneID = s.SceneID
OUTER APPLY
(
SELECT COUNT(*) AS WarningCount
FROM dbo.ContinuityWarnings cw
WHERE cw.SceneID = s.SceneID AND cw.IsDismissed = 0
) warnings
OUTER APPLY
(
SELECT COUNT(*) AS RemainingChecklistCount
FROM dbo.SceneChecklistItems sci
WHERE sci.SceneID = s.SceneID AND sci.IsCompleted = 0
) checklist
OUTER APPLY
(
SELECT COUNT(*) AS UnresolvedNoteCount
FROM dbo.SceneNotes sn
WHERE sn.SceneID = s.SceneID AND sn.IsResolved = 0
) notes
WHERE s.ChapterID = @ChapterID AND s.IsArchived = 0
ORDER BY s.SortOrder, s.SceneID;
END;
GO
CREATE OR ALTER PROCEDURE dbo.WriterDashboard_Scenes
@ProjectID int = NULL,
@Queue nvarchar(50)
AS
BEGIN
SET NOCOUNT ON;
WITH SceneBase AS
(
SELECT p.ProjectID, p.ProjectName, b.BookID, b.BookTitle, c.ChapterID, c.ChapterNumber, c.ChapterTitle,
s.SceneID, s.SceneNumber, s.SceneTitle, s.Summary,
COALESCE(sw.DraftStatus, N'Planned') AS DraftStatus,
sw.Priority,
COALESCE(sw.IsBlocked, 0) AS IsBlocked,
sw.BlockedReason,
COALESCE(sw.ReadyForDraft, 0) AS ReadyForDraft,
COALESCE(sw.ReadyForRevision, 0) AS ReadyForRevision,
COALESCE(sw.ReadyForPolish, 0) AS ReadyForPolish,
b.SortOrder AS BookSortOrder,
c.SortOrder AS ChapterSortOrder,
s.SortOrder AS SceneSortOrder
FROM dbo.Projects p
INNER JOIN dbo.Books b ON b.ProjectID = p.ProjectID AND b.IsArchived = 0
INNER JOIN dbo.Chapters c ON c.BookID = b.BookID AND c.IsArchived = 0
INNER JOIN dbo.Scenes s ON s.ChapterID = c.ChapterID AND s.IsArchived = 0
LEFT JOIN dbo.SceneWorkflow sw ON sw.SceneID = s.SceneID
WHERE p.IsArchived = 0 AND (@ProjectID IS NULL OR p.ProjectID = @ProjectID)
)
SELECT TOP (40) sb.ProjectID, sb.ProjectName, sb.BookID, sb.BookTitle, sb.ChapterID, sb.ChapterNumber, sb.ChapterTitle,
sb.SceneID, sb.SceneNumber, sb.SceneTitle, sb.Summary, sb.DraftStatus, sb.Priority, sb.IsBlocked, sb.BlockedReason,
ISNULL(warnings.WarningCount, 0) AS WarningCount,
ISNULL(checklist.RemainingChecklistCount, 0) AS RemainingChecklistCount,
ISNULL(notes.UnresolvedNoteCount, 0) AS UnresolvedNoteCount,
threads.ImportantThreads,
pinned.PinnedNotes
FROM SceneBase sb
OUTER APPLY
(
SELECT COUNT(*) AS WarningCount
FROM dbo.ContinuityWarnings cw
WHERE cw.SceneID = sb.SceneID AND cw.IsDismissed = 0
) warnings
OUTER APPLY
(
SELECT COUNT(*) AS RemainingChecklistCount
FROM dbo.SceneChecklistItems sci
WHERE sci.SceneID = sb.SceneID AND sci.IsCompleted = 0
) checklist
OUTER APPLY
(
SELECT COUNT(*) AS UnresolvedNoteCount
FROM dbo.SceneNotes sn
INNER JOIN dbo.SceneNoteTypes snt ON snt.SceneNoteTypeID = sn.SceneNoteTypeID
WHERE sn.SceneID = sb.SceneID AND sn.IsResolved = 0
AND (@Queue <> N'NeedsAttention' OR snt.TypeName IN (N'TODO', N'Continuity Note'))
) notes
OUTER APPLY
(
SELECT STRING_AGG(CONVERT(nvarchar(max), pt.ThreadTitle), N', ') AS ImportantThreads
FROM
(
SELECT DISTINCT TOP (5) pt.ThreadTitle, pt.Importance
FROM dbo.ThreadEvents te
INNER JOIN dbo.PlotThreads pt ON pt.PlotThreadID = te.PlotThreadID
WHERE te.SceneID = sb.SceneID
ORDER BY pt.Importance DESC, pt.ThreadTitle
) pt
) threads
OUTER APPLY
(
SELECT STRING_AGG(CONVERT(nvarchar(max), COALESCE(sn.NoteTitle, sn.NoteText)), N' | ') AS PinnedNotes
FROM dbo.SceneNotes sn
WHERE sn.SceneID = sb.SceneID AND sn.IsPinned = 1 AND sn.IsResolved = 0
) pinned
WHERE
(@Queue = N'ContinueWriting' AND (sb.DraftStatus IN (N'Ready To Draft', N'Drafting') OR sb.ReadyForDraft = 1 OR ISNULL(sb.Priority, 0) >= 8))
OR (@Queue = N'NeedsAttention' AND (sb.IsBlocked = 1 OR ISNULL(warnings.WarningCount, 0) > 0 OR ISNULL(notes.UnresolvedNoteCount, 0) > 0))
OR (@Queue = N'RevisionQueue' AND (sb.DraftStatus IN (N'Drafted', N'Revising') OR sb.ReadyForRevision = 1))
OR (@Queue = N'PolishingQueue' AND (sb.DraftStatus = N'Polishing' OR sb.ReadyForPolish = 1))
ORDER BY ISNULL(sb.Priority, 0) DESC, sb.BookSortOrder, sb.ChapterSortOrder, sb.SceneSortOrder, sb.SceneID;
END;
GO
CREATE OR ALTER PROCEDURE dbo.WriterDashboard_Reminders
@ProjectID int = NULL
AS
BEGIN
SET NOCOUNT ON;
SELECT TOP (60) N'Chapter Goal' AS Category, N'Info' AS SeverityName,
CONCAT(N'Chapter ', c.ChapterNumber, N' needs intent') AS Title,
N'Goal summary is empty.' AS Detail,
CAST(NULL AS int) AS SceneID, c.ChapterID AS EntityID, N'Chapter' AS EntityType
FROM dbo.Chapters c
INNER JOIN dbo.Books b ON b.BookID = c.BookID AND b.IsArchived = 0
LEFT JOIN dbo.ChapterGoals cg ON cg.ChapterID = c.ChapterID
WHERE c.IsArchived = 0 AND (@ProjectID IS NULL OR b.ProjectID = @ProjectID)
AND NULLIF(LTRIM(RTRIM(cg.GoalSummary)), N'') IS NULL
UNION ALL
SELECT N'Plot Thread', N'Info', pt.ThreadTitle,
CONCAT(N'Unresolved thread in ', pl.PlotLineName, N'.'),
CAST(NULL AS int), pt.PlotThreadID, N'PlotThread'
FROM dbo.PlotThreads pt
INNER JOIN dbo.ThreadStatuses ts ON ts.ThreadStatusID = pt.ThreadStatusID
INNER JOIN dbo.PlotLines pl ON pl.PlotLineID = pt.PlotLineID AND pl.IsArchived = 0
WHERE pt.IsArchived = 0 AND ts.IsOpenStatus = 1 AND (@ProjectID IS NULL OR pl.ProjectID = @ProjectID)
UNION ALL
SELECT N'Writer Note', N'Warning', COALESCE(sn.NoteTitle, s.SceneTitle),
sn.NoteText, s.SceneID, sn.SceneNoteID, N'SceneNote'
FROM dbo.SceneNotes sn
INNER JOIN dbo.SceneNoteTypes snt ON snt.SceneNoteTypeID = sn.SceneNoteTypeID
INNER JOIN dbo.Scenes s ON s.SceneID = sn.SceneID AND s.IsArchived = 0
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID AND c.IsArchived = 0
INNER JOIN dbo.Books b ON b.BookID = c.BookID AND b.IsArchived = 0
WHERE sn.IsResolved = 0 AND snt.TypeName IN (N'TODO', N'Continuity Note', N'Revision Note') AND (@ProjectID IS NULL OR b.ProjectID = @ProjectID);
END;
GO
CREATE OR ALTER PROCEDURE dbo.StoryBible_Search
@ProjectID int,
@Query nvarchar(200) = NULL
AS
BEGIN
SET NOCOUNT ON;
IF NULLIF(LTRIM(RTRIM(@Query)), N'') IS NULL
RETURN;
DECLARE @Term nvarchar(204) = N'%' + LTRIM(RTRIM(@Query)) + N'%';
SELECT N'Character' AS ResultType, ch.CharacterID AS EntityID, ch.CharacterName AS Title,
ch.DefaultDescription AS Detail, CAST(NULL AS int) AS SceneID
FROM dbo.Characters ch
WHERE ch.ProjectID = @ProjectID AND ch.IsArchived = 0
AND (ch.CharacterName LIKE @Term OR ch.ShortName LIKE @Term OR ch.DefaultDescription LIKE @Term)
UNION ALL
SELECT N'Scene', s.SceneID, CONCAT(N'Scene ', s.SceneNumber, N': ', s.SceneTitle),
s.Summary, s.SceneID
FROM dbo.Scenes s
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID AND c.IsArchived = 0
INNER JOIN dbo.Books b ON b.BookID = c.BookID AND b.ProjectID = @ProjectID AND b.IsArchived = 0
WHERE s.IsArchived = 0 AND (s.SceneTitle LIKE @Term OR s.Summary LIKE @Term OR s.ScenePurposeNotes LIKE @Term OR s.SceneOutcomeNotes LIKE @Term)
UNION ALL
SELECT N'Plot Thread', pt.PlotThreadID, pt.ThreadTitle, pt.Summary, CAST(NULL AS int)
FROM dbo.PlotThreads pt
INNER JOIN dbo.PlotLines pl ON pl.PlotLineID = pt.PlotLineID AND pl.ProjectID = @ProjectID AND pl.IsArchived = 0
WHERE pt.IsArchived = 0 AND (pt.ThreadTitle LIKE @Term OR pt.Summary LIKE @Term)
UNION ALL
SELECT N'Story Asset', sa.StoryAssetID, sa.AssetName, sa.Description, CAST(NULL AS int)
FROM dbo.StoryAssets sa
WHERE sa.ProjectID = @ProjectID AND sa.IsArchived = 0 AND (sa.AssetName LIKE @Term OR sa.Description LIKE @Term)
UNION ALL
SELECT N'Location', lp.LocationID, lp.LocationPath, lp.Description, CAST(NULL AS int)
FROM dbo.LocationPaths lp
WHERE lp.ProjectID = @ProjectID AND (lp.LocationName LIKE @Term OR lp.LocationPath LIKE @Term OR lp.Description LIKE @Term)
UNION ALL
SELECT N'Relationship', cr.CharacterRelationshipID, CONCAT(ca.CharacterName, N' - ', cb.CharacterName),
CONCAT(rt.TypeName, N'. ', ISNULL(cr.Notes, N'')), CAST(NULL AS int)
FROM dbo.CharacterRelationships cr
INNER JOIN dbo.Characters ca ON ca.CharacterID = cr.CharacterAID
INNER JOIN dbo.Characters cb ON cb.CharacterID = cr.CharacterBID
INNER JOIN dbo.RelationshipTypes rt ON rt.RelationshipTypeID = cr.RelationshipTypeID
WHERE cr.ProjectID = @ProjectID AND cr.IsArchived = 0
AND (ca.CharacterName LIKE @Term OR cb.CharacterName LIKE @Term OR rt.TypeName LIKE @Term OR cr.Notes LIKE @Term)
UNION ALL
SELECT N'Warning', cw.ContinuityWarningID, cw.Message, cw.Details, cw.SceneID
FROM dbo.ContinuityWarnings cw
WHERE cw.ProjectID = @ProjectID AND cw.IsDismissed = 0
AND (cw.Message LIKE @Term OR cw.Details LIKE @Term OR cw.EntityType LIKE @Term)
UNION ALL
SELECT N'Scene Note', sn.SceneNoteID, COALESCE(sn.NoteTitle, CONCAT(snt.TypeName, N' for ', s.SceneTitle)),
sn.NoteText, sn.SceneID
FROM dbo.SceneNotes sn
INNER JOIN dbo.SceneNoteTypes snt ON snt.SceneNoteTypeID = sn.SceneNoteTypeID
INNER JOIN dbo.Scenes s ON s.SceneID = sn.SceneID AND s.IsArchived = 0
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID AND c.IsArchived = 0
INNER JOIN dbo.Books b ON b.BookID = c.BookID AND b.ProjectID = @ProjectID AND b.IsArchived = 0
WHERE sn.NoteTitle LIKE @Term OR sn.NoteText LIKE @Term OR snt.TypeName LIKE @Term
UNION ALL
SELECT N'Chapter Goal', cg.ChapterGoalID, CONCAT(N'Chapter ', c.ChapterNumber, N': ', c.ChapterTitle),
CONCAT(ISNULL(cg.GoalSummary, N''), N' ', ISNULL(cg.EmotionalGoal, N''), N' ', ISNULL(cg.ReaderTakeaway, N''), N' ', ISNULL(cg.MustInclude, N''), N' ', ISNULL(cg.RisksOrConcerns, N'')),
CAST(NULL AS int)
FROM dbo.ChapterGoals cg
INNER JOIN dbo.Chapters c ON c.ChapterID = cg.ChapterID AND c.IsArchived = 0
INNER JOIN dbo.Books b ON b.BookID = c.BookID AND b.ProjectID = @ProjectID AND b.IsArchived = 0
WHERE cg.GoalSummary LIKE @Term OR cg.EmotionalGoal LIKE @Term OR cg.ReaderTakeaway LIKE @Term OR cg.MustInclude LIKE @Term OR cg.RisksOrConcerns LIKE @Term
UNION ALL
SELECT N'Attachment', sa.SceneAttachmentID, sa.Title,
CONCAT(ISNULL(sa.ExternalUrl, N''), N' ', ISNULL(sa.FilePath, N''), N' ', ISNULL(sa.Notes, N'')), sa.SceneID
FROM dbo.SceneAttachments sa
INNER JOIN dbo.Scenes s ON s.SceneID = sa.SceneID AND s.IsArchived = 0
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID AND c.IsArchived = 0
INNER JOIN dbo.Books b ON b.BookID = c.BookID AND b.ProjectID = @ProjectID AND b.IsArchived = 0
WHERE sa.Title LIKE @Term OR sa.ExternalUrl LIKE @Term OR sa.FilePath LIKE @Term OR sa.Notes LIKE @Term
UNION ALL
SELECT N'Workflow', sw.SceneWorkflowID, CONCAT(N'Workflow: ', s.SceneTitle),
sw.BlockedReason, s.SceneID
FROM dbo.SceneWorkflow sw
INNER JOIN dbo.Scenes s ON s.SceneID = sw.SceneID AND s.IsArchived = 0
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID AND c.IsArchived = 0
INNER JOIN dbo.Books b ON b.BookID = c.BookID AND b.ProjectID = @ProjectID AND b.IsArchived = 0
WHERE sw.BlockedReason LIKE @Term OR sw.DraftStatus LIKE @Term
ORDER BY ResultType, Title;
END;
GO

View File

@ -0,0 +1,39 @@
SET ANSI_NULLS ON;
GO
SET QUOTED_IDENTIFIER ON;
GO
CREATE OR ALTER PROCEDURE dbo.CharacterTimeline_GetByProject
@ProjectID int,
@BookID int = NULL
AS
BEGIN
SET NOCOUNT ON;
SELECT TOP (12) CharacterID, ProjectID, CharacterName, ShortName, Sex, BirthDate, AgeAtSeriesStart, AgeReferenceSceneID,
Height, EyeColour, DefaultDescription, CreatedDate, UpdatedDate, IsArchived
FROM dbo.Characters
WHERE ProjectID = @ProjectID AND IsArchived = 0
ORDER BY CharacterName;
SELECT sc.SceneCharacterID, sc.SceneID, sc.CharacterID, c.ProjectID, c.CharacterName, c.ShortName, c.BirthDate, c.AgeAtSeriesStart,
sc.RoleInSceneTypeID, role.TypeName AS RoleInSceneTypeName, sc.PresenceTypeID, presence.TypeName AS PresenceTypeName,
sc.LocationID, location.LocationName, location.LocationPath, sc.EntryLocationID, entry.LocationName AS EntryLocationName,
sc.ExitLocationID, exitLocation.LocationName AS ExitLocationName, sc.AppearanceNotes, sc.OutfitDescription,
sc.PhysicalCondition, sc.EmotionalState, sc.KnowledgeNotes, s.SceneNumber, s.SceneTitle, ch.ChapterNumber, b.BookTitle,
sc.CreatedDate, sc.UpdatedDate
FROM dbo.SceneCharacters sc
INNER JOIN dbo.Characters c ON c.CharacterID = sc.CharacterID
INNER JOIN dbo.Scenes s ON s.SceneID = sc.SceneID
INNER JOIN dbo.Chapters ch ON ch.ChapterID = s.ChapterID
INNER JOIN dbo.Books b ON b.BookID = ch.BookID
LEFT JOIN dbo.CharacterRoleInSceneTypes role ON role.CharacterRoleInSceneTypeID = sc.RoleInSceneTypeID
LEFT JOIN dbo.PresenceTypes presence ON presence.PresenceTypeID = sc.PresenceTypeID
LEFT JOIN dbo.LocationPaths location ON location.LocationID = sc.LocationID
LEFT JOIN dbo.LocationPaths entry ON entry.LocationID = sc.EntryLocationID
LEFT JOIN dbo.LocationPaths exitLocation ON exitLocation.LocationID = sc.ExitLocationID
WHERE c.ProjectID = @ProjectID AND c.IsArchived = 0 AND s.IsArchived = 0 AND ch.IsArchived = 0 AND b.IsArchived = 0
AND (@BookID IS NULL OR b.BookID = @BookID)
ORDER BY b.SortOrder, ch.SortOrder, s.SortOrder, c.CharacterName;
END;
GO

View File

@ -170,6 +170,16 @@ public sealed class SceneEditViewModel
public IReadOnlyList<SceneDependency> DependenciesNeedingThisScene { get; set; } = [];
public SceneDependencyCreateViewModel NewDependency { get; set; } = new();
public SceneMoveRequestViewModel MoveRequest { get; set; } = new();
public SceneWorkflow Workflow { get; set; } = new();
public IReadOnlyList<SceneNote> Notes { get; set; } = [];
public IReadOnlyList<SceneChecklistItem> ChecklistItems { get; set; } = [];
public IReadOnlyList<SceneAttachment> Attachments { get; set; } = [];
public SceneNoteEditViewModel NewNote { get; set; } = new();
public SceneChecklistItemEditViewModel NewChecklistItem { get; set; } = new();
public SceneAttachmentEditViewModel NewAttachment { get; set; } = new();
public IReadOnlyList<SelectListItem> SceneNoteTypeOptions { get; set; } = [];
public IReadOnlyList<SelectListItem> SceneAttachmentTypeOptions { get; set; } = [];
public IReadOnlyList<SelectListItem> DraftStatusOptions { get; set; } = [];
public int? ReturnProjectID { get; set; }
public int? ReturnBookID { get; set; }
@ -185,6 +195,93 @@ public sealed class SceneEditViewModel
public IReadOnlyList<ScenePurposeType> ScenePurposeTypes { get; set; } = [];
}
public sealed class SceneWorkflowEditViewModel
{
public int SceneID { get; set; }
public int? ReturnProjectID { get; set; }
public int? ReturnBookID { get; set; }
public string DraftStatus { get; set; } = "Planned";
public int? EstimatedWordCount { get; set; }
public int? ActualWordCount { get; set; }
public DateTime? DraftedDate { get; set; }
public DateTime? LastWorkedOn { get; set; }
public bool ReadyForDraft { get; set; }
public bool ReadyForRevision { get; set; }
public bool ReadyForPolish { get; set; }
public bool IsBlocked { get; set; }
public string? BlockedReason { get; set; }
public int? Priority { get; set; }
}
public sealed class SceneNoteEditViewModel
{
public int SceneNoteID { get; set; }
public int SceneID { get; set; }
public int? ReturnProjectID { get; set; }
public int? ReturnBookID { get; set; }
public int SceneNoteTypeID { get; set; }
public string? NoteTitle { get; set; }
public string NoteText { get; set; } = string.Empty;
public int? SortOrder { get; set; }
public bool IsPinned { get; set; }
public bool IsResolved { get; set; }
}
public sealed class SceneChecklistItemEditViewModel
{
public int SceneChecklistItemID { get; set; }
public int SceneID { get; set; }
public int? ReturnProjectID { get; set; }
public int? ReturnBookID { get; set; }
public string ItemText { get; set; } = string.Empty;
public bool IsCompleted { get; set; }
public int? SortOrder { get; set; }
}
public sealed class SceneAttachmentEditViewModel
{
public int SceneAttachmentID { get; set; }
public int SceneID { get; set; }
public int? ReturnProjectID { get; set; }
public int? ReturnBookID { get; set; }
public int SceneAttachmentTypeID { get; set; }
public string Title { get; set; } = string.Empty;
public string? FilePath { get; set; }
public string? ExternalUrl { get; set; }
public string? Notes { get; set; }
}
public sealed class ChapterGoalEditViewModel
{
public int ChapterGoalID { get; set; }
public int ChapterID { get; set; }
public string? GoalSummary { get; set; }
public string? EmotionalGoal { get; set; }
public string? ReaderTakeaway { get; set; }
public string? MustInclude { get; set; }
public string? RisksOrConcerns { get; set; }
}
public sealed class ChapterWorkflowViewModel
{
public Project Project { get; set; } = new();
public Book Book { get; set; } = new();
public Chapter Chapter { get; set; } = new();
public ChapterGoal Goal { get; set; } = new();
public IReadOnlyList<ChapterWorkflowScene> Scenes { get; set; } = [];
}
public sealed class WriterDashboardViewModel
{
public int? ProjectID { get; set; }
public IReadOnlyList<SelectListItem> ProjectOptions { get; set; } = [];
public IReadOnlyList<WriterDashboardScene> ContinueWriting { get; set; } = [];
public IReadOnlyList<WriterDashboardScene> NeedsAttention { get; set; } = [];
public IReadOnlyList<WriterDashboardScene> RevisionQueue { get; set; } = [];
public IReadOnlyList<WriterDashboardScene> PolishingQueue { get; set; } = [];
public IReadOnlyList<StoryBibleAttentionItem> StoryHealthReminders { get; set; } = [];
}
public sealed class TimelineViewModel
{
public Project Project { get; set; } = new();

View File

@ -17,6 +17,7 @@
</div>
<nav class="context-nav" aria-label="Chapter navigation">
<a asp-controller="Timeline" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID" asp-route-bookId="@Model.Book.BookID">Timeline</a>
<a asp-controller="Writer" asp-action="Chapter" asp-route-id="@Model.Chapter.ChapterID">Chapter workflow</a>
<a asp-controller="Analytics" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID" asp-route-bookId="@Model.Book.BookID" asp-route-startChapterNumber="@Model.Chapter.ChapterNumber" asp-route-endChapterNumber="@Model.Chapter.ChapterNumber">Story health</a>
<a asp-controller="Warnings" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID" asp-route-bookId="@Model.Book.BookID">Warnings</a>
<a asp-controller="Characters" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID">Characters</a>
@ -40,6 +41,7 @@
<div class="button-row">
<a class="btn btn-outline-secondary" asp-action="Edit" asp-route-id="@Model.Chapter.ChapterID">Edit chapter</a>
<a class="btn btn-outline-primary" asp-controller="Timeline" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID" asp-route-bookId="@Model.Book.BookID">Timeline</a>
<a class="btn btn-outline-primary" asp-controller="Writer" asp-action="Chapter" asp-route-id="@Model.Chapter.ChapterID">Chapter workflow</a>
<a class="btn btn-primary" asp-controller="Scenes" asp-action="Create" asp-route-chapterId="@Model.Chapter.ChapterID">Add scene</a>
</div>
</div>

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="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>
<a asp-controller="Warnings" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID">Warnings</a>
@ -37,6 +38,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="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>
<a class="btn btn-outline-primary" asp-controller="PlotThreads" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID">Plot threads</a>

View File

@ -63,6 +63,10 @@
<a href="#inspector-timing">Time</a>
<a href="#inspector-purpose">Purpose</a>
<a href="#inspector-metrics">Metrics</a>
<a href="#inspector-writer">Writer</a>
<a href="#inspector-notes">Notes</a>
<a href="#inspector-checklist">Checklist</a>
<a href="#inspector-attachments">Attachments</a>
<a href="#inspector-warnings">Warnings</a>
<a href="#inspector-dependencies">Dependencies</a>
<a href="#inspector-move">Move</a>
@ -220,6 +224,223 @@
</div>
</form>
@if (TempData["WriterWorkspaceError"] is string writerWorkspaceError)
{
<div class="alert alert-warning">@writerWorkspaceError</div>
}
<section class="inspector-section writer-workspace-section" data-section-title="Writer Workspace" data-section-id="inspector-writer">
<div class="inspector-section-heading">
<h3>Writer Workspace</h3>
<a class="btn btn-outline-secondary btn-sm" asp-controller="Writer" asp-action="Chapter" asp-route-id="@Model.ChapterID">Chapter workflow</a>
</div>
<form asp-controller="Scenes" asp-action="SaveWorkflow" method="post" class="writer-workspace-form">
<input type="hidden" name="SceneID" value="@Model.SceneID" />
<input type="hidden" name="ReturnProjectID" value="@Model.ReturnProjectID" />
<input type="hidden" name="ReturnBookID" value="@Model.ReturnBookID" />
<div class="row g-2">
<div class="col-sm-6">
<label class="form-label" for="DraftStatus">Draft status</label>
<select class="form-select form-select-sm" id="DraftStatus" name="DraftStatus" asp-items="Model.DraftStatusOptions"></select>
</div>
<div class="col-sm-6">
<label class="form-label" for="Priority">Priority</label>
<input class="form-control form-control-sm" id="Priority" name="Priority" type="number" min="1" max="10" value="@Model.Workflow.Priority" />
</div>
<div class="col-sm-6">
<label class="form-label" for="EstimatedWordCount">Estimated words</label>
<input class="form-control form-control-sm" id="EstimatedWordCount" name="EstimatedWordCount" type="number" min="0" value="@Model.Workflow.EstimatedWordCount" />
</div>
<div class="col-sm-6">
<label class="form-label" for="ActualWordCount">Actual words</label>
<input class="form-control form-control-sm" id="ActualWordCount" name="ActualWordCount" type="number" min="0" value="@Model.Workflow.ActualWordCount" />
</div>
<div class="col-sm-6">
<label class="form-label" for="DraftedDate">Drafted date</label>
<input class="form-control form-control-sm" id="DraftedDate" name="DraftedDate" type="date" value="@Model.Workflow.DraftedDate?.ToString("yyyy-MM-dd")" />
</div>
<div class="col-sm-6">
<label class="form-label" for="LastWorkedOn">Last worked on</label>
<input class="form-control form-control-sm" id="LastWorkedOn" name="LastWorkedOn" type="date" value="@Model.Workflow.LastWorkedOn?.ToString("yyyy-MM-dd")" />
</div>
<div class="col-12 writer-checkbox-grid">
<label><input type="checkbox" name="ReadyForDraft" value="true" checked="@Model.Workflow.ReadyForDraft" /> Ready for draft</label>
<label><input type="checkbox" name="ReadyForRevision" value="true" checked="@Model.Workflow.ReadyForRevision" /> Ready for revision</label>
<label><input type="checkbox" name="ReadyForPolish" value="true" checked="@Model.Workflow.ReadyForPolish" /> Ready for polish</label>
<label><input type="checkbox" name="IsBlocked" value="true" checked="@Model.Workflow.IsBlocked" /> Blocked</label>
</div>
<div class="col-12">
<label class="form-label" for="BlockedReason">Blocked reason</label>
<input class="form-control form-control-sm" id="BlockedReason" name="BlockedReason" value="@Model.Workflow.BlockedReason" />
</div>
</div>
<div class="button-row compact-buttons mt-2">
<button class="btn btn-primary btn-sm" type="submit">Save workflow</button>
</div>
</form>
</section>
<section class="inspector-section writer-notes-section" data-section-title="Scene Notes" data-section-id="inspector-notes">
<h3>Scene Notes</h3>
@if (!Model.Notes.Any())
{
<p class="muted">No working notes yet.</p>
}
else
{
<div class="writer-note-list">
@foreach (var note in Model.Notes)
{
<details class="writer-mini-card" @(note.IsPinned ? "open" : "")>
<summary>
<span class="status-pill">@note.TypeName</span>
<strong>@(string.IsNullOrWhiteSpace(note.NoteTitle) ? note.NoteText : note.NoteTitle)</strong>
@if (note.IsPinned) { <span class="badge text-bg-light">Pinned</span> }
@if (note.IsResolved) { <span class="badge text-bg-success">Resolved</span> }
</summary>
<form asp-controller="Scenes" asp-action="SaveNote" method="post" class="writer-edit-form">
<input type="hidden" name="SceneNoteID" value="@note.SceneNoteID" />
<input type="hidden" name="SceneID" value="@Model.SceneID" />
<input type="hidden" name="ReturnProjectID" value="@Model.ReturnProjectID" />
<input type="hidden" name="ReturnBookID" value="@Model.ReturnBookID" />
<select class="form-select form-select-sm" name="SceneNoteTypeID">
@foreach (var option in Model.SceneNoteTypeOptions)
{
<option value="@option.Value" selected="@(option.Value == note.SceneNoteTypeID.ToString())">@option.Text</option>
}
</select>
<input class="form-control form-control-sm" name="NoteTitle" value="@note.NoteTitle" placeholder="Title" />
<textarea class="form-control form-control-sm" name="NoteText" rows="3">@note.NoteText</textarea>
<div class="writer-checkbox-grid">
<label><input type="checkbox" name="IsPinned" value="true" checked="@note.IsPinned" /> Pinned</label>
<label><input type="checkbox" name="IsResolved" value="true" checked="@note.IsResolved" /> Resolved</label>
</div>
<div class="button-row compact-buttons">
<button class="btn btn-primary btn-sm" type="submit">Save note</button>
</div>
</form>
<form asp-controller="Scenes" asp-action="DeleteNote" method="post" class="mt-1">
<input type="hidden" name="id" value="@note.SceneNoteID" />
<input type="hidden" name="sceneId" value="@Model.SceneID" />
<input type="hidden" name="projectId" value="@Model.ReturnProjectID" />
<input type="hidden" name="bookId" value="@Model.ReturnBookID" />
<button class="btn btn-outline-danger btn-sm" type="submit">Remove note</button>
</form>
</details>
}
</div>
}
<form asp-controller="Scenes" asp-action="SaveNote" method="post" class="writer-add-form">
<input type="hidden" name="SceneID" value="@Model.SceneID" />
<input type="hidden" name="ReturnProjectID" value="@Model.ReturnProjectID" />
<input type="hidden" name="ReturnBookID" value="@Model.ReturnBookID" />
<select class="form-select form-select-sm" name="SceneNoteTypeID" asp-items="Model.SceneNoteTypeOptions"></select>
<input class="form-control form-control-sm" name="NoteTitle" placeholder="Optional title" />
<textarea class="form-control form-control-sm" name="NoteText" rows="3" placeholder="Add a drafting, revision, research, or TODO note"></textarea>
<div class="writer-checkbox-grid">
<label><input type="checkbox" name="IsPinned" value="true" /> Pin note</label>
</div>
<button class="btn btn-outline-primary btn-sm" type="submit">Add note</button>
</form>
</section>
<section class="inspector-section writer-checklist-section" data-section-title="Scene Checklist" data-section-id="inspector-checklist">
<h3>Scene Checklist</h3>
<div class="writer-checklist">
@foreach (var item in Model.ChecklistItems)
{
<form asp-controller="Scenes" asp-action="SaveChecklistItem" method="post" class="writer-checklist-row">
<input type="hidden" name="SceneChecklistItemID" value="@item.SceneChecklistItemID" />
<input type="hidden" name="SceneID" value="@Model.SceneID" />
<input type="hidden" name="ReturnProjectID" value="@Model.ReturnProjectID" />
<input type="hidden" name="ReturnBookID" value="@Model.ReturnBookID" />
<input type="checkbox" name="IsCompleted" value="true" checked="@item.IsCompleted" />
<input class="form-control form-control-sm" name="ItemText" value="@item.ItemText" />
<button class="btn btn-outline-primary btn-sm" type="submit">Save</button>
</form>
<form asp-controller="Scenes" asp-action="DeleteChecklistItem" method="post" class="writer-remove-inline">
<input type="hidden" name="id" value="@item.SceneChecklistItemID" />
<input type="hidden" name="sceneId" value="@Model.SceneID" />
<input type="hidden" name="projectId" value="@Model.ReturnProjectID" />
<input type="hidden" name="bookId" value="@Model.ReturnBookID" />
<button class="btn btn-outline-danger btn-sm" type="submit">Remove</button>
</form>
}
</div>
<form asp-controller="Scenes" asp-action="SaveChecklistItem" method="post" class="writer-add-form writer-add-inline">
<input type="hidden" name="SceneID" value="@Model.SceneID" />
<input type="hidden" name="ReturnProjectID" value="@Model.ReturnProjectID" />
<input type="hidden" name="ReturnBookID" value="@Model.ReturnBookID" />
<input class="form-control form-control-sm" name="ItemText" placeholder="Add checklist item" />
<button class="btn btn-outline-primary btn-sm" type="submit">Add item</button>
</form>
</section>
<section class="inspector-section writer-attachments-section" data-section-title="Attachments / Research" data-section-id="inspector-attachments">
<h3>Attachments / Research</h3>
@if (!Model.Attachments.Any())
{
<p class="muted">No scene references yet.</p>
}
else
{
<div class="writer-note-list">
@foreach (var attachment in Model.Attachments)
{
<details class="writer-mini-card">
<summary>
<span class="status-pill">@attachment.TypeName</span>
<strong>@attachment.Title</strong>
</summary>
<form asp-controller="Scenes" asp-action="SaveAttachment" method="post" class="writer-edit-form">
<input type="hidden" name="SceneAttachmentID" value="@attachment.SceneAttachmentID" />
<input type="hidden" name="SceneID" value="@Model.SceneID" />
<input type="hidden" name="ReturnProjectID" value="@Model.ReturnProjectID" />
<input type="hidden" name="ReturnBookID" value="@Model.ReturnBookID" />
<select class="form-select form-select-sm" name="SceneAttachmentTypeID">
@foreach (var option in Model.SceneAttachmentTypeOptions)
{
<option value="@option.Value" selected="@(option.Value == attachment.SceneAttachmentTypeID.ToString())">@option.Text</option>
}
</select>
<input class="form-control form-control-sm" name="Title" value="@attachment.Title" />
<input class="form-control form-control-sm" name="ExternalUrl" value="@attachment.ExternalUrl" placeholder="External URL" />
<input class="form-control form-control-sm" name="FilePath" value="@attachment.FilePath" placeholder="File path" />
<textarea class="form-control form-control-sm" name="Notes" rows="2">@attachment.Notes</textarea>
<button class="btn btn-primary btn-sm" type="submit">Save attachment</button>
</form>
<form asp-controller="Scenes" asp-action="DeleteAttachment" method="post" class="mt-1">
<input type="hidden" name="id" value="@attachment.SceneAttachmentID" />
<input type="hidden" name="sceneId" value="@Model.SceneID" />
<input type="hidden" name="projectId" value="@Model.ReturnProjectID" />
<input type="hidden" name="bookId" value="@Model.ReturnBookID" />
<button class="btn btn-outline-danger btn-sm" type="submit">Remove attachment</button>
</form>
@if (!string.IsNullOrWhiteSpace(attachment.ExternalUrl))
{
<a href="@attachment.ExternalUrl" target="_blank" rel="noopener">Open link</a>
}
@if (!string.IsNullOrWhiteSpace(attachment.FilePath))
{
<p class="muted">@attachment.FilePath</p>
}
</details>
}
</div>
}
<form asp-controller="Scenes" asp-action="SaveAttachment" method="post" class="writer-add-form">
<input type="hidden" name="SceneID" value="@Model.SceneID" />
<input type="hidden" name="ReturnProjectID" value="@Model.ReturnProjectID" />
<input type="hidden" name="ReturnBookID" value="@Model.ReturnBookID" />
<select class="form-select form-select-sm" name="SceneAttachmentTypeID" asp-items="Model.SceneAttachmentTypeOptions"></select>
<input class="form-control form-control-sm" name="Title" placeholder="Reference title" />
<input class="form-control form-control-sm" name="ExternalUrl" placeholder="External URL" />
<input class="form-control form-control-sm" name="FilePath" placeholder="File path" />
<textarea class="form-control form-control-sm" name="Notes" rows="2" placeholder="Why this reference matters"></textarea>
<button class="btn btn-outline-primary btn-sm" type="submit">Add attachment</button>
</form>
</section>
<section class="inspector-section warning-inspector-section" data-section-title="Continuity Warnings" data-section-id="inspector-warnings">
<div class="inspector-section-heading">
<h3>Continuity Warnings</h3>

View File

@ -23,6 +23,9 @@
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Projects" asp-action="Index">Projects</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Writer" asp-action="Index">Writer Workspace</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">About</a>
</li>

View File

@ -0,0 +1,100 @@
@model ChapterWorkflowViewModel
@{
ViewData["Title"] = $"Chapter Workflow - {Model.Chapter.ChapterTitle}";
var blockedCount = Model.Scenes.Count(x => x.IsBlocked);
var readyDraftCount = Model.Scenes.Count(x => x.ReadyForDraft);
var readyRevisionCount = Model.Scenes.Count(x => x.ReadyForRevision);
var readyPolishCount = Model.Scenes.Count(x => x.ReadyForPolish);
}
<nav class="breadcrumb-trail" aria-label="Breadcrumb">
<a asp-controller="Projects" asp-action="Index">Projects</a>
<a asp-controller="Projects" asp-action="Details" asp-route-id="@Model.Project.ProjectID">@Model.Project.ProjectName</a>
<a asp-controller="Books" asp-action="Details" asp-route-id="@Model.Book.BookID">@Model.Book.BookTitle</a>
<a asp-controller="Chapters" asp-action="Details" asp-route-id="@Model.Chapter.ChapterID">Chapter @Model.Chapter.ChapterNumber</a>
<span>Workflow</span>
</nav>
<div class="page-heading">
<div>
<p class="eyebrow">Chapter workflow</p>
<h1>Chapter @Model.Chapter.ChapterNumber: @Model.Chapter.ChapterTitle</h1>
<p class="lead-text">Chapter intent, readiness, blockers, notes, and checklist progress.</p>
</div>
<div class="button-row">
<a class="btn btn-outline-primary" asp-controller="Timeline" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID" asp-route-bookId="@Model.Book.BookID">Open timeline</a>
<a class="btn btn-outline-secondary" asp-controller="Warnings" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID" asp-route-bookId="@Model.Book.BookID">Warnings</a>
</div>
</div>
<section class="story-bible-section">
<div class="story-bible-grid story-bible-grid-compact">
<article class="story-bible-card"><h3>Ready to draft</h3><strong class="writer-kpi">@readyDraftCount</strong></article>
<article class="story-bible-card"><h3>Ready to revise</h3><strong class="writer-kpi">@readyRevisionCount</strong></article>
<article class="story-bible-card"><h3>Ready to polish</h3><strong class="writer-kpi">@readyPolishCount</strong></article>
<article class="story-bible-card"><h3>Blocked</h3><strong class="writer-kpi">@blockedCount</strong></article>
</div>
</section>
<section class="story-bible-section">
<div class="story-bible-section-heading">
<div>
<p class="eyebrow">Intent</p>
<h2>Chapter Goals</h2>
</div>
</div>
<form asp-action="SaveChapterGoal" method="post" class="story-bible-card chapter-goal-form">
<input type="hidden" name="ChapterGoalID" value="@Model.Goal.ChapterGoalID" />
<input type="hidden" name="ChapterID" value="@Model.Chapter.ChapterID" />
<label class="form-label">Goal summary</label>
<textarea class="form-control" name="GoalSummary" rows="3">@Model.Goal.GoalSummary</textarea>
<label class="form-label">Emotional goal</label>
<input class="form-control" name="EmotionalGoal" value="@Model.Goal.EmotionalGoal" />
<label class="form-label">Reader takeaway</label>
<textarea class="form-control" name="ReaderTakeaway" rows="2">@Model.Goal.ReaderTakeaway</textarea>
<label class="form-label">Must include</label>
<textarea class="form-control" name="MustInclude" rows="3">@Model.Goal.MustInclude</textarea>
<label class="form-label">Risks or concerns</label>
<textarea class="form-control" name="RisksOrConcerns" rows="3">@Model.Goal.RisksOrConcerns</textarea>
<button class="btn btn-primary mt-2" type="submit">Save chapter goals</button>
</form>
</section>
<section class="story-bible-section">
<div class="story-bible-section-heading">
<div>
<p class="eyebrow">Scenes</p>
<h2>Workflow Summary</h2>
</div>
</div>
<div class="writer-queue-list">
@foreach (var scene in Model.Scenes)
{
<article class="writer-queue-card">
<div class="writer-queue-title">
<h3>Scene @scene.SceneNumber: @scene.SceneTitle</h3>
<span class="status-pill">@scene.DraftStatus</span>
</div>
<div class="story-bible-meta">
@if (scene.Priority.HasValue) { <span>Priority @scene.Priority</span> }
<span>@scene.WarningCount warnings</span>
<span>@scene.RemainingChecklistCount checklist remaining</span>
<span>@scene.UnresolvedNoteCount open notes</span>
@if (scene.ReadyForDraft) { <span>Ready for draft</span> }
@if (scene.ReadyForRevision) { <span>Ready for revision</span> }
@if (scene.ReadyForPolish) { <span>Ready for polish</span> }
@if (scene.IsBlocked) { <span>Blocked</span> }
</div>
@if (!string.IsNullOrWhiteSpace(scene.BlockedReason))
{
<p class="muted">@scene.BlockedReason</p>
}
<div class="story-bible-actions">
<a asp-controller="Scenes" asp-action="Edit" asp-route-id="@scene.SceneID">Open scene</a>
<a asp-controller="Timeline" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID" asp-route-bookId="@Model.Book.BookID" asp-route-selectedSceneId="@scene.SceneID">Timeline</a>
<a asp-controller="Warnings" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID" asp-route-bookId="@Model.Book.BookID" asp-route-sceneId="@scene.SceneID">Warnings</a>
</div>
</article>
}
</div>
</section>

View File

@ -0,0 +1,91 @@
@model WriterDashboardViewModel
@{
ViewData["Title"] = "Writer Workspace";
}
<div class="page-heading writer-dashboard-heading">
<div>
<p class="eyebrow">Today view</p>
<h1>Writer Workspace</h1>
<p class="lead-text">A practical control room for drafting, revision, polish, and story readiness.</p>
</div>
<form asp-action="Index" method="get" class="story-bible-search">
<select class="form-select" name="projectId" asp-items="Model.ProjectOptions">
<option value="">All projects</option>
</select>
<button class="btn btn-primary" type="submit">Focus</button>
</form>
</div>
<div class="writer-dashboard-grid">
<section class="writer-dashboard-panel">
<div class="story-bible-section-heading">
<div>
<p class="eyebrow">Write Next</p>
<h2>Continue Writing</h2>
</div>
<span class="soft-count">@Model.ContinueWriting.Count scenes</span>
</div>
<partial name="_WriterSceneQueue" model="Model.ContinueWriting" />
</section>
<section class="writer-dashboard-panel">
<div class="story-bible-section-heading">
<div>
<p class="eyebrow">Practical blockers</p>
<h2>Needs Attention</h2>
</div>
<span class="soft-count">@Model.NeedsAttention.Count scenes</span>
</div>
<partial name="_WriterSceneQueue" model="Model.NeedsAttention" />
</section>
<section class="writer-dashboard-panel">
<div class="story-bible-section-heading">
<div>
<p class="eyebrow">Revision</p>
<h2>Revision Queue</h2>
</div>
<span class="soft-count">@Model.RevisionQueue.Count scenes</span>
</div>
<partial name="_WriterSceneQueue" model="Model.RevisionQueue" />
</section>
<section class="writer-dashboard-panel">
<div class="story-bible-section-heading">
<div>
<p class="eyebrow">Final pass</p>
<h2>Polishing Queue</h2>
</div>
<span class="soft-count">@Model.PolishingQueue.Count scenes</span>
</div>
<partial name="_WriterSceneQueue" model="Model.PolishingQueue" />
</section>
</div>
<section class="story-bible-section">
<div class="story-bible-section-heading">
<div>
<p class="eyebrow">Story health</p>
<h2>Reminders</h2>
</div>
<span class="soft-count">@Model.StoryHealthReminders.Count items</span>
</div>
<div class="attention-list">
@foreach (var reminder in Model.StoryHealthReminders)
{
<article>
<span class="badge text-bg-light">@reminder.Category</span>
<strong>@reminder.Title</strong>
@if (!string.IsNullOrWhiteSpace(reminder.Detail))
{
<p>@reminder.Detail</p>
}
@if (reminder.SceneID.HasValue)
{
<a asp-controller="Scenes" asp-action="Edit" asp-route-id="@reminder.SceneID">Open scene</a>
}
</article>
}
</div>
</section>

View File

@ -0,0 +1,51 @@
@model IReadOnlyList<WriterDashboardScene>
@if (!Model.Any())
{
<p class="muted">Nothing in this queue.</p>
}
else
{
<div class="writer-queue-list">
@foreach (var scene in Model)
{
<article class="writer-queue-card">
<div class="writer-queue-title">
<div>
<span class="eyebrow">@scene.BookTitle / Chapter @scene.ChapterNumber</span>
<h3>Scene @scene.SceneNumber: @scene.SceneTitle</h3>
</div>
<span class="status-pill">@scene.DraftStatus</span>
</div>
@if (!string.IsNullOrWhiteSpace(scene.Summary))
{
<p>@scene.Summary</p>
}
<div class="story-bible-meta">
@if (scene.Priority.HasValue) { <span>Priority @scene.Priority</span> }
<span>@scene.WarningCount warnings</span>
<span>@scene.RemainingChecklistCount checklist remaining</span>
<span>@scene.UnresolvedNoteCount open notes</span>
@if (scene.IsBlocked) { <span>Blocked</span> }
</div>
@if (!string.IsNullOrWhiteSpace(scene.ImportantThreads))
{
<p class="muted"><strong>Important:</strong> @scene.ImportantThreads</p>
}
@if (!string.IsNullOrWhiteSpace(scene.PinnedNotes))
{
<p class="muted"><strong>Notes:</strong> @scene.PinnedNotes</p>
}
@if (!string.IsNullOrWhiteSpace(scene.BlockedReason))
{
<p class="muted"><strong>Blocked:</strong> @scene.BlockedReason</p>
}
<div class="story-bible-actions">
<a asp-controller="Scenes" asp-action="Edit" asp-route-id="@scene.SceneID">Open scene</a>
<a asp-controller="Timeline" asp-action="Index" asp-route-projectId="@scene.ProjectID" asp-route-bookId="@scene.BookID" asp-route-selectedSceneId="@scene.SceneID">Timeline</a>
<a asp-controller="Warnings" asp-action="Index" asp-route-projectId="@scene.ProjectID" asp-route-bookId="@scene.BookID" asp-route-sceneId="@scene.SceneID">Warnings</a>
</div>
</article>
}
</div>
}

View File

@ -2594,6 +2594,121 @@ body.dragging-location [data-drag-type="location"] {
color: var(--plotline-muted);
}
.writer-workspace-form,
.writer-add-form,
.writer-edit-form,
.chapter-goal-form {
display: grid;
gap: 10px;
}
.writer-checkbox-grid {
display: flex;
flex-wrap: wrap;
gap: 8px 14px;
}
.writer-checkbox-grid label {
display: inline-flex;
align-items: center;
gap: 6px;
color: var(--plotline-muted);
font-size: 0.86rem;
font-weight: 650;
}
.writer-note-list,
.writer-queue-list {
display: grid;
gap: 10px;
}
.writer-mini-card {
border: 1px solid var(--plotline-line);
border-radius: 12px;
padding: 10px;
background: rgba(255, 255, 255, 0.72);
}
.writer-mini-card summary {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
}
.writer-mini-card summary strong {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.writer-checklist {
display: grid;
gap: 6px;
}
.writer-checklist-row,
.writer-add-inline {
display: grid;
grid-template-columns: auto minmax(0, 1fr) auto;
align-items: center;
gap: 8px;
}
.writer-remove-inline {
display: flex;
justify-content: end;
margin: -2px 0 4px;
}
.writer-dashboard-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 16px;
margin-bottom: 26px;
}
.writer-dashboard-panel {
border: 1px solid var(--plotline-line);
border-radius: 14px;
padding: 14px;
background: rgba(255, 255, 255, 0.7);
box-shadow: 0 14px 34px rgba(36, 32, 26, 0.05);
}
.writer-queue-card {
border: 1px solid var(--plotline-line);
border-radius: 12px;
padding: 12px;
background: rgba(255, 255, 255, 0.9);
}
.writer-queue-card h3 {
margin: 0;
font-size: 1rem;
}
.writer-queue-card p {
margin: 8px 0 0;
color: var(--plotline-muted);
}
.writer-queue-title {
display: flex;
align-items: start;
justify-content: space-between;
gap: 12px;
}
.writer-kpi {
display: block;
color: var(--plotline-accent);
font-size: 2.2rem;
line-height: 1;
}
@media (max-width: 760px) {
.plotline-shell {
padding-left: 14px;
@ -2625,6 +2740,15 @@ body.dragging-location [data-drag-type="location"] {
grid-template-columns: 1fr;
}
.writer-dashboard-grid {
grid-template-columns: 1fr;
}
.writer-checklist-row,
.writer-add-inline {
grid-template-columns: 1fr;
}
.attention-list p {
grid-column: auto;
}