Phase 1Q
This commit is contained in:
parent
8f56934dd8
commit
624ec9a394
@ -1,5 +0,0 @@
|
||||
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
|
||||
@ -1,5 +0,0 @@
|
||||
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=
|
||||
@ -1,5 +0,0 @@
|
||||
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=
|
||||
@ -1,5 +0,0 @@
|
||||
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=
|
||||
@ -1,5 +0,0 @@
|
||||
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
|
||||
@ -1,5 +0,0 @@
|
||||
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==
|
||||
@ -1,5 +0,0 @@
|
||||
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==
|
||||
@ -1,5 +0,0 @@
|
||||
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=
|
||||
@ -1,5 +0,0 @@
|
||||
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=
|
||||
@ -1,5 +0,0 @@
|
||||
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
|
||||
@ -1,5 +0,0 @@
|
||||
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=
|
||||
@ -1,5 +0,0 @@
|
||||
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
|
||||
@ -1,5 +0,0 @@
|
||||
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=
|
||||
@ -1,5 +0,0 @@
|
||||
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=
|
||||
@ -1,5 +0,0 @@
|
||||
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==
|
||||
@ -1,5 +0,0 @@
|
||||
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
|
||||
@ -0,0 +1,5 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACoxUTW/bOBD9KwLPokJR1AcF7MFwWqyRpjESd3exN4ocxkRk0qAop0GR/76gJNdO0cPehBFn5r03b+YHsuIAqEWr4/HTCWwYUIqCmUKU0AqTCpN8l9O2pC3hGWclY1X5L0qRuYM31KKaSZlLDriACjBTpMRdoSVWCnhOckVoyWJN8Tyg9gcSJlNwMhIyN/wFfjDOohb9baxyr7G3MJns3agy73rY2CEIKyOYr5v1Hf6y2u4etvOrAYaYnBmFWpQzUpayo1gB0ZhVHcNcCYIFr/JOS9Yw4HOasQG8FX02qJdLf+WChSBbmtEiI5hy9J4iJYKIkDsxwO7tGFFMEt3GeDqFp8/2BzqBRy1Nz2LO5W5kb24OQzeaXt0E4Z8haC8O8Or8C5xEj1J09O4IPhiYtHl4Si6QcpKRjFaUEJSieyH3xkKyuU0e+khYyIYwUAXmoBVmPBeYs6LAqihFTStd11SjFN2Bt9BfVb030rvB6ZAskicf+qydDcaObhySjQ3w7EWY8z6LfgCUou3Y9WbYP4JQbzv3OMZ/duwjl/XoPdiQbEXYJ3+KYY9aVFY1LRlhUpRFDlRqTokmpOEFVYyCrCsFNaWyY4zrQhel7ITs6kZ3oIquzlGKHsZwHMNZ/+9XIHbeHA6gLgh2k8bbXgTt/GGjwAajTRzN+cVkqL6fWCVLzXVvwIYPIqMWkRyoKhrAmuYVZo0SWJQFx1VVVBpyyWsa0a2d1eZ5/KkT0VqxQpdNU3W8ywlQXTeM1LojSupCclV2UNOa1A00XBLSlSxvOi6JgrxkKrLzTo0y/GqFYhrQ42jjdiabSPvVWPy9YhdFtt4FJ10fB2Z6eHobAhx+N7W1OxzdYELkr5fRfhvg3tn4d9n/RbMleeXCJQZxD5Jp9SqiteRC4kqJGrOKl7ihBceS5VCIuma1bFCKbp18AZ9Egwljp5mcPfXwlKy83JsAMow+dv5nIjWP8/N5ZS56ZF8/7dbOw+p4TJfoH6co0lzsbIAPd+XbEJ/v3RBZBD9e+egJen3GdeUmBaf5UpmJplINqCrHlOUlZpznuOFljbu66XhXNVDKeGFmtyaPoIwHGaZ6u7nbIuzvbLnywWghwxB354uTk5sWey5PfgV5NbWf2XPzq8ktTloq5SQ69mm+m5OB/tfZfH9/f/8PAAD//wMAbXToGicGAAA=
|
||||
@ -0,0 +1,5 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACpSUXW/bNhSG/4rAa1EhKYoSdZc5K2akbdzGa4fdUeSRQ0QhDZJKFwT+7wNlB7Fb7GJ3AnXe8/Geh3xFTj0B6tH1fv/7M7gUUYmSXY4YYQITgQndUtY3rCeyajlnom3/RiWyt/CCetRyramWgGsQgLkhDR7qUWNjQFJCDWENzznVLqL+FSlbGXi2Giofv0GI1jvUo+/WGf8j11a20pOfTRX8BGsXk3I6N/N5vbrFH68327vNMSpCzOLKGtQjyknT6IFhA2TEXAwcS6MIVlLQYdS84yCPMusSBKemKprH9/rGJwdJ96xidUUwk+hQIqOSyi0PKsL2ZZ+7WCy6yeflcrx89q/oGQLqWflm5jHdlZ7s1VMcZjuZq8nvdtbtVt6NdjcHlXLhEu2D30NIFhZzfrNOhZePfrfL+dAHNUVAJdpCeLJOTccfNzCqeUr3fg6LM9E8/lcM6pGak0clWpycpqVscRpmNVlw6RftWeG7+2IzqTT68HSxpPuj9+v/Yf0npR+sg2J9g3pEKDBTd4BHRgXmnVFYNbXEQtRiBKplyygq0cq7ZN3s51isXYLdybWL/q6DfrAJdJpDnukvkWHbBG9mnYr3BVNSkaomJIvmtJ9T8RWMDaAT5CG2YT7l+0nCBFtEH+wEv9izmkMAl4qNSg/FHyo+oB41omUNJ1yrpqbA9CgZGQnpZM0MZ6BbYaBlTA+cy7Ee60YPSg9tNw5g6qHNY0OGrFi4ZgM0wJjEhnLAHDTBnewMVhIYNYyQrjMX5hZ3U9Yp3REOpsYSRoO5pApLXtfY1I1qmRjblo1nPm2PQFCSy59D+GcEc2Lp8xHtt9kNPB/v8dKnMKYDIyhmnDaYS0lxJ5sWD203yEF00OgMwcq76N+M/AZh8NGm/Ih8mS1kEi8C3vdyC8HBdLabT1YHH/2YihOUxcW2vs4uv2HFAugP6/A/CxY3Xj9CKDJWyrqzVR4Oh8O/AAAA//8DAH+Nh0gKBQAA
|
||||
@ -0,0 +1,5 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACpRUXW/cKBT9KxbPxuHL2PgtmrTaUbabUTvqrvYNw6VBcWAEOG0UzX9f4ZlVm6fVviCEOfeee87BbyjoZ0ATuj2dPrxAKBm1qPjtiBEmMZGY0CNlU88mSjpJlGJC/o1a5O/hFU1oEMZQowBzkICFJT2euTPYWlCUUEtYL2pN/S2j6Q1p31l48Qa6mL9Cyj4GNKE/fbDxe+2tfWeWuNouxQX2IRcdTCXzx353j3+/PRwfDpdbGXIFd96iCVFB+t7MDFsgDgs5C6ysJlgrSWdnxChAXWA+FEhBL122Tz/721gCFDOxjvGOYKbQuUVWF10pzzrD8fVUWWwS3dXzdjvettMbeoGEJtb+K+al3I1Z/M1znle/2JvLWnR+ytvqtCkxvaIWnVI8QSoeNn1uc4bneXk96vz08XKnbvOHH2DWAnYX11DqxHxALdoEWhZdfAzNleNu8RDK9rEkH7I3/1WLoxY9fGkOiy4upud3fny5yLz/Hyp/0ubRB2j2d2hChAKzfATsGJVYjFZj3XOFpeTSATVqYBS1aBdD8WGNa272ocC3tI2EJvRRLxku/G6TefQFTFlTnfMvWXN1SNGupjQ/vaSkIx0npILWclpL8xmsT2AK1CGOab3W+/oewiTbQLs1JQilOejy2Pym8yOaUC8H1gsijO45BWacYsQRMirOrGBgBmlhYMzMQijHHe/NrM08jG4Gy+ehTgg1Os2WVuEoFc4qPA6DwcIYgzWAxkSMPYAgehTzOx2bh6XitBmJAMuxAmexUFRjJTjHlvd6YNINA3O/SHLNAyW1vYWXy7vbGEhrR7CSYiZoj4VSFI+qH/A8jLOa5Qi9qU7eQwqw/CLUJ29SzNGV5pqQ5p10n9dQ/x3NlpbvPuAfm0d30TxBaqrH2of6VK62ns/n8z8AAAD//wMAC2rMKYIEAAA=
|
||||
@ -0,0 +1,5 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACoxU227cNhD9FYGvFRXeL3pzbQddpI6N7KIt+sbL0FYtUwuKchIY/vdCWjtXFOiLIAw5cw7PnJknlN0DoB6dHY+Xj5DrjFpUhy3ECFOYKEzogbJesp6STlEtuJF/oxYN7+Az6pEWIdBgAXNQgEUkEnueAo4RLCU0EibFWtPdzqh/Qm7oIjwOAbpp/gPKPEwZ9ejPIcfp44rthi6M0xK7Mo2wy3N1Oaxk3u/O3+Hfz24O1zenWzPMa3I3RNQjKoiUwTMcgSQslBfYRkews4r6FIQRYE9pQ65Qshu7Od5/xY9TzVBDzzrGO4KZRc8tiq66lbJ3Mxw+H1cWm0QXa7zdwttv/4QeoaCeta9insq9CePw5mH2yzDGN9sXtehYpiOUOsCmxvW++UqCko50TDFCUIuuXLgbMjS7i+Z6XJ/ogiECIscWUsTCUoet4BxHLp1mKmnNEmrROygZxm+qXg2hTPOUavMicvMdzv7s/DI7P8KK8daNM6AW7eZ9dTm6ccpw+QnCUk+1DmVZj8+nXIe8TMvc7HKF2+Jezl/zz5dSINfmxtW75jc336EeSaWZFEQEJzkFFpJlJBFiLGdRMAhaRdCMBS+ETTxxGbwLXpvkIXKvKWrRr6uG+yUEmOevbDaTjOPGoXnp0/k4QK7fyYh6RCiwyA3gxKjCwkSHneQWK8VVAhqsZl9gDq7cQkU9SsZ7T0LwTFgaHLOOx0R4VMkyprUTiUstmFKSaBei1oyKyDUExxMnhqEW3ZQpLqH+2Gq+NeCmTP9AqKtUqEeKSGmC854pQY2zBgRVwigL2orkiODBOyqN854SJ0mwhgVHlXXCR5nsK/3LfDtkuBjm4+g+f4NrOtXxX4JzhiYnfrj+trgH+DiV+/cnG3fvLw+oRR+WvG6EZrda5OOQ8Se1TvTVMtbhcFfARYhXU4SffQTruDSnCQ1KcM8DDjxRLEyS2EovsQYPkLxMTAXUoosp3ENpVoe5Ia9T9aXY9b45K+FuqBDqUlZ+f208rvfNzehqmsrDd5vkm4f9KEDHOCVrZyI8nrbRxlHFaCAqipmgEgtrKTZWauy18dYrAzKsil0v9bjU5gPEoUCo24tfvLhhnt9BuP9ZjFcXvFiUktVr+9MW26T9X0vsuUUP4OalwMO2rfsntMsZyoZ8sZxGcZevhnEcZghTjjPqjaGdoVq/EPyva5awTnOtn5+fn/8FAAD//wMAPZDLtBkGAAA=
|
||||
@ -0,0 +1,5 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACpRTS2/cIBD+KxbnxeFlML5Fm1ZdpW1WyaqtesMwdFEcvMI4D0X73yu8W6XprReERnwz32N4RdE8AOrQ5eHw4RFintAK5bCUGGESE4kJ3VHWNayjpJaUt4qSn2iFwjW8oA4pYS21GjAHCVg40uCee4udA00JdYQ1ovQ0vybUvSITagePwUI9Tt8gTWGMqEPfQ3TjU5ltQm2HcXZ1GgfYxCmbaAuZr5v1Nf58ud3dbE+vJpgKuA4OdYgK0jS2Z9gB8VjIXmDtDMFGS9p7K1oB+gQLMUOKZqgnd/823405QrYdqxmvCWYaHVfImWwK5d5MsHs5FBaLRVelvlrKy7V7RY+QUMdWf8w8tbuwQ7h4mPo5DO7idGYz3ReVhzQeIOUAiyeLzGEwOYyxOk9aDwFiRit0c1dtB5P9mB7eGXV30r/5D/m7Mv3DM9g5g1uPc8wnLFqhL8buQ4Rqc4U6RCgwx1vAnlGJResMNg3XWEouPVCrFaNohdZjzCHO4zxVm5jhV1oEoA59NMMEJ+qXye5DBpvnVFT9kGUXtml0s83Vm/+U1KTmpDC5mfNhztUtuJDAZij6dmk+9/sHwiRbQOs5JYi52pq8rz6ZaY861EjFGkGENQ2nwKzXjHhCWs2ZEwyskg4UY7YXQnvueWN7Y3vV+h4c71VRCCXu6rxhTgLXCje2sVgw6nAvPcctgAKtlPLg3vlY3QwFZ2xLBDiONXiHhaYGa8E5drwxikmvFPN/WXJOn5Iy3sHj6a8sDKRzLThJMRO0wUJrilvdKNyrtte9bKGxJeRrSBGGv4z6Emwap9Hn6rw81TvrbudY/nu1LNJTiPh5yehqtPeQqpKxCbGs9znW4/F4/A0AAP//AwATWzwkNgQAAA==
|
||||
@ -0,0 +1,5 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACpSUTW/cNhCG/4rA81LhlyhRN8Nu0YWbepEYbdHLghoOvaxlaktRTgNj/3tA7RqOm1NvEjHvfDx8hy8k2ickPbk6Hn96xphnsiE5rEeCCU2Zpozfc9E3oue85lJpppq/yIaEW/xKetIqAA4GqUSNVDnW0EF6oM6h4Yw7JhpVctqHmfQvxIba4XMArKf5d0xzmCLpyR8huulLqW1DDeO0uDpNI27jnG2E0sxv2+tb+uvV7v5ud46acS7iOjjSE65Y08AgqEPmqdKDosZZRq3RfPCgOoXmLAsxY4p2rGf3+FbfTTlihl7UQtaMCkNOG+JstqXlwc54//VYulgR3ZTzzXq8fvYv5BkT6cXmFeY53QcYw4e0RLIhxzQdMeWAK4N1rHG0OUyxumS+HgPGfA79GyHv17lEB61xg0GlWnBosZEOmBOKSezcwD1XCiSaDphRvGHgWSes96LprO8GJBty97najTb7KT29A/35zG/7P/B9tHAIEavtDekJ4yic7JB6wTVVnbPUNtJQraX2yMG0gpMNuZ5iDnGZlrnaxowPaZ2a9ORnO86X/q4SHEJGyEsqKP7UxTCjXSIc9sc0+TDiPsx7h94uYyY98RftLk1ugVy9XSRnNaslYyXxko9Lrj6hCwkhYxn0Pi2Xmv+RCC1W0fWSEsZc7Ww+VL/Y+UB60uhWNIopsI3kKMAbwTxjnZHCKYHQaoetEDAoZbz0soHBwtB2fkAnh7ZQwOKbar1SJ7QTzgEdpBBUeW3pIJDTdjDgQDrpNX/Hurobi85CxxQ6SQ16R5XhlholJXWysa3Qvm2FLxY/Hvf5bKlXJxWjuu9oXRzHGf8Rc8J/FpzPsC6898tZ7vD5vLnrGNq5Dp3mVCjeUGUMp51pWjq03WAG3WEDxTK3mCKO39H+GCBN8+RzdbFi9Y7/pyWW16dabfklRPrvaoabCR4xVcVMNsSybBf/nDbkCe28JHxaH6/+hRwtPNqHMorHhBFwD9MS87qfs3t8/eNvq/ZDJDudTqdvAAAA//8DAGGM7QYbBQAA
|
||||
@ -0,0 +1,5 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACpRTy27bMBD8FYFnU+FLlKhb4LSokaYxmqAteqPIZUxEIQ2KygOB/72g7CJJb70RC87s7MzuKwr6AVCPzvf7T48Q8oRWKPulxAiTmEhM6C1lfcN6RmqiJGuY/I1WyF/CC+pRK4yhRgHmIAELSxo8cGewtaAooZawRhROfTeh/hVpX1t49AbqOP2ANPkYUI9++mDjU+mtfW3GONs6xRE2Yco6mCLm22Z9ib+eb2+vt8dfE0wFXHuLekQFaRozMGyBOCzkILCymmCtJB2cEZ0AdYT5kCEFPdaTvX/rb2MOkE3PasZrgplChxWyOusiedAT3L7si4rFootSXy3l5dm/okdIqGerv2Ye6c7M6M9MfHjQwZ45H/y0Qyu0T3EPKXtY7FgmHEedfQzVqcl69BAyWqHrm2o76uxievjg0c1x9M1/TH6lzc4HqDYXqEeEArO8A+wYlVh0VmPdcIWl5NIBNaplFK3QOobswxznqdqEDHdpUYl69FmPExz1nSez8xlMnlOR/kuWrOHZ53W0pYAL0TZFO5tcvflNSU1qTkghmfN+ztV3sD6ByVCGuk3zif8fCJNsAa3nlCDkaqvzrvqipx3qUSNb1ggijG44BWacYsQR0inOrGBgWmmhZcwMQijHHW/MoM3Qdm4Ay4e2CIUSb7VslLKdNco4bAgoLGBgWFE9YDVwrQ20ClT3wdfqeiw4bToiwHKswFksFNVYCc6x5Y1umXRty9w7S06RU1LaW3g83saiQFrbgZUUM0EbLJSiuFNNi4e2G9QgO2hMSfYSUoDxnVFX3qQ4RZer08ZUH6z7Pody39WyPU8+4Ocls4to7iFVJXPtQ1nnU8yHw+HwBwAA//8DAHsvl/wmBAAA
|
||||
@ -0,0 +1,5 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACpSUTW/cNhCG/4rAsyjzm6Juht2ihptmERtt0duIM4qFaKUFRW0aGP7vBbVbJO6tN2LAGb585p15ZTMciXXs9nT66UxzXlnN8riHlFCOC8eFfJaqs6rTbSO0ts6rv1jNxkf6xjrmTYwyBuKaHHGDwvJeD5EjUpBColDWlJrweWXdK4OxQTqPkZpl/Z3SOi4z69gf44zL1/I2jE2clg2btEz0MK8Z5ljE/PZw98h/vT08fzxcbq20luRmRNYxHaV2ZiAevO65Ca3kwUrJvXZKSuxtq8IlbZwzpRmmZsUv39/HJc+UY6capRvBVWBvNUPIUCT3sNLzt1NRsSO6L/F6D+/H7pWdKbFO1f/CvJS7idN4s279RGeaTpBWSjdxOR5hRlazU1pOlPJIO5YzpX5Zx1yIxuihbaXEFryxHlttlfaoBWErvMWAAZxxg4rWDti3wZvBoJIGHRnTe1azHdw0QR6Xubpqv5tGmjOr2cen6jBBHpZ0fIf+6UL04X8ALbLLbWG1856M1hqC65Ug9C46p/oIwg+9IhlJ2GB8kF5IHZQIxUitRxAhspp9gPgyzlQ93LOOCUkKdUt8UNJx0yJwsDpw57QbSMbglWQ1u1vmPM7bsq3Vw5zpc9r/yzr2M0wrXX56m+LLmCnmLRUIf7pixkNacIu5+m4AKRrRaCFK0pZPW64+EY6JYqaC4zlt13r/SVFO7Ul3W0o05+oA+aX6BdYX1rEAbZQQIAC4GAeyggaBApzwSA4doVHo0QSDzgQRnYFeGgQr+6FEWM2o+K3aLa4sWOvbyI0NkpvBAu/bVnFHIAZDxmqw7zhWH6eSB7EVhlDzQANyEyTwYLTmqC145Qbv1fADkqtZpCiAkc6XYd0VOMSW0EmujLTchCB5G6znvW/70LuWbCyaHynNNP0A6sMY07IuQ66uXqveofu0zWXhVLvvvo4z/3vv0f0Sv1CqSo9hnMt8Xdv6VrMjwbolOu77qntlhzJc1fO+tpRupFO2Zk8ZUt5O17B2jQhSvr29vf0DAAD//wMAY0OS2PQEAAA=
|
||||
@ -0,0 +1,5 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACpSUy27jNhSGX0XgWlR4EyVqlzod1MhkYkzcadEdRR45RGTSoKhMg8DvPqDsIM4MuuhOOOJ/Lt/5yVfk9R5Qh64Ph9+fwacJlSi5JcQIk5hITOiWsq5mHW8rUrdEKf4PKpG7hRfUoUYYQ40CzEECFpbUuOeDwdaCooRawmqRc+rdhLpXpF1l4dkZqML0DeLkgkcd+st5G77n2tpVZgyzrWIYYe2npL3JzXxZr27x5+vN9n5zOjXBlMWVs6hD3FAuxQBYNbzHQrUUq5pS3HDJKLV93TJ1kjmfIHo9VpN9eq9vQ/KQTMcqxiuCmULHElmddG651xNsXw65iwXRTY6XS3j57F7RM0TUsfIN5indlRnd1X7qZzfaqzHsds7vVsEPbjdHnXLhEh1iOEBMDhY4vzmv48vnsNvlfOiTHidAJdpC3Duvx9OPGxj0PKaHMMeFzGSf/usM6pCeU0AlWkiO41K2OA+zGh349Iv2ovD9Q7EZdRpC3H9Y0sOJ/fp/oL/T5tF5KNY3qEOEArO8BTwwKrForca65gpLyeUA1KiGUVSiVfDJ+TnMU7H2CXZnah/6u47m0SUwaY55pr9lNtsmBjubVLwvmJKKVJyQLJrTYU7FV7AugkmQh9jG+ZzvJwmTbBF9ciP8gmc1xwg+FRudHos/9PSIOqR0a6hWWmktjRmgJjAQS7QkjQVpJVjBbGOFElYKRYwUuqfC6pr2Q46gEkE2WbH4uqeGGaE5HhijWNTGYNVTg2shDTEDk0S3H+AW92PWadMSAZZjBYPFQlGNleAcW17rhsmhadhwwelsCEoy9UsT/jmBPXvpy8nab7NbeD7d46VPaW0LVlLMBK2xUIriVtUN7pu2V71soTZ5slXwU3gD+Q1iHyaX8iNy57zb6/HnI++buYXoYbzYzp0zMUxhSMXZlsWHfX2dfX7FisWi353H/y7GuAnmCWKRjaWdv1jm8Xg8/gAAAP//AwD+kh3KDAUAAA==
|
||||
@ -0,0 +1,5 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACpSUTW/cNhCG/4rAsyjzS/zQzbBb1HVTL+JFWvQ2Ike2EC21oKhNA2P/e0DtBol7640Y8J0ZPvMO30iEA5KO3B6Pv5ww5oXUJI9bSDChKdOU8T0XXSs6aRrnrGil/YfUZHzEr6QjRnnPvUMqUSNVgbW0l4OnIaDjjAcmWlVywstCujcCYxPwNHps5uUTpmWcI+nIX2MM85dSG8bGT/MamjRP+BCXDNGXZv58uHukf9zu9k+7y60FlyJuxkA6Ij2XWg1InZE9Vc5y6lrOqZFacB761gp3kY0xY4owNUv4/KN+mHPE7DvRCNkwKhw51yRAhtJyDwvuvx5LFxui+xKvt/B27N7ICRPpRP0d5iXdjZ/GmzwfJzzhdIS0YLrx8+EAMZCaHNN8xJRH3LBsL50myOMcq2uxu2nEmElNnp6r3QR5mNPhHavnC4KH/0HghKkvt1krtTGopJTgdC8YBqO91qL3wMzQC+QeWeuUcdwwLp1gTrbaWBOAOU9q8jLNPUy/Lxs/J1hvAzNCC1TohNLaeYbS4tAaZAG07rX1LkhrhLLBSnAA3PVovNO+GOQD+NcxYvVwTzrCOIogLdJBcE2VDUChlY5qLfWA3DsjOKnJ3RzzGNd5XaqHmPElbfxIR36FacELudvkX8eMPq+pQP1bl2K7NIfV5+qHAzhrWCMZK6I1H9dcfcQwJvQZC959Wq/5/iMRWmyiuzUljLnaQX6tfoPltVAB6zm48lTt/YAtw4EFBpqZgDpoDEoEE5RTQSvHvFbQcxWg5f1QIqQmWAxXbR4XPUrmvKQwMEsVG4A6FEgHr5TTrLUMwzuO1dNUdOAtUxgkdTgEqhwH6pSUNMgWjNCDMWL4CcnVfJwVwAFPl23dOtAhWAyaU6F4S5VznFrXGtob27teW2x96fkRU8TpJ1AfRp/mZR5ydfVu9Q7dxzWWH6fafPxljPTfbUb3s/+MqSozhjGWBbuO9VyTA8KyJjxsH1b3RnZlu6r99m8J2XAt2po8Z0h5PV7DUjfMcX4+n8/fAAAA//8DAMyRdbf1BAAA
|
||||
@ -0,0 +1,5 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACoxUS2/bOBD+KwLPpkJSFCUK2IPhtFgjTWMk7u5ibxQ5jInIokFRToMi/31BWa6dooe9CaN5fY/hD9SrPaAGLQ+HT0fo44AWKLopxAgTmAhM6JaypmRNUeecE84Z/RctkLuDN9SgimtNtQRcgADMDSlxW1iNjQFJCTWElTz1VM8Dan4g5XIDR6ch98NfEAbne9Sgv11v/GuarVyuOz+aPPgO1v0QVa/TMl/Xqzv8ZbnZPmxOWQMMqTh3BjWo0LQQ3AKWVdFiLmuKZUkprgrBKDVtWTN5KnN9hNCrLh/My2W+8bGHqBuWsyInmEn0vkBGRZVWbtUA27dD2mKi6DbFF1N4+mx+oCME1LDFmcxTuxvduZv90I6uMzdRhWeINqg9vPrwAkfVoQU6BH+AEB1M3Dw8ZZeVKMlJzgQjBC3QvdI710O2vs0eugRY6ZpwMAWWYA3mkioseVFgU5SqYsJWFbNoge4g9NBddb13OvjB25jNlGcf5qx8H10/+nHI1n2E56Diqe6z6gZAC7QZ284Nu0dQ5m3rH8f0rx+7hGU1hgB9zDYq7rI/1bBDDZKq1lRJJZUSWlsoCVhiiBKkMiCMAMOZqQyX3AguiRZctZQbVdLWpghaoIcxHsZ45v/71RLb4PZ7MJcNthPHm05F68N+baCPzrokzTljMlTXTaiyueeqc9DHDySjBhEKzBQ1YMuowLw2CquykFiIQligWlaMngiz7nn8yROx1vDClnUtWtlSAsxWNSeVbYnRttDSlC1UrCJVDbXUhLQlp3UrNTFAS24SuuDNqOOvVigmgR7HPl1ntk6wX12Pvwt+YWQTfPTad0kw18HT2xBh/zvVVn5/8IOLCb+dpf02wL3v09/5/mfO5uKlj5cYpDvIptOrK0JqKzQuNK0wb0HguqootowxWVVlqSuGFujW6xcIWTKYcv2kydlTD0/ZMuidi6DjGNLkfyZQJzk/n0/mwkf+9dN25QMsD4fFHP3jmEg6NTsb4MO78m1I6Ts/xCvIM7Qn6Ox5sSs7GTieniqXgsKYGoygmHFaYi4lxbUsK9xWdStbUUOpL3bNHsG4ADpO/bZhTNNmZn/ny2WIziodh3Q8X7ye7DT7c075dckr2X5Wn4ZfSTdbae5ESbLs0+nhnBz0v97N9/f39/8AAAD//wMAsR8nGCgGAAA=
|
||||
@ -0,0 +1,5 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACpRTy27bMBD8FYFnU+FLlKhb4LSokbYxEqMteqO4q5qIQhkUlQcC/3tB2UWa3nohiJVmd2Z2+EqCfUDSksvD4cMjhjSRFUl+KQkmNGWaMr7joq1Eq2SpjGK1Fj/JivhrfCEtqZVz3BmkEjVSBayinewdBUDDGQcmKpV72l8TaV+J9SXgo3dYjtM3jJMfA2nJdx9gfMqzrS/dMM5QxnHATZiSDS6T+bpZX9PPl9vdzfb014RTBpceSEuk41KrHqmpZUeVaTg1Fee0llpwDl3VCHOC+ZAwBjuUE9y/zYcxBUyuFaWQJaPCkOOKgE02U+7shLuXQ2axWHSV66ulvFzbV/KIkbRi9cfMU7sLN/iLh6mb/QAXpzPZ6T6rPMTxgDF5XDxZZA6DTX4MxXnSevAYElmRm7tiO9jUj/HhnVF3J/2b/5C/y9M/PKObE8J6nEMiLeEyf/pi3d4HLDZXpCWMowDZIO0F11Q1YKmtpKFaS90jd6YWnKzIegzJh3mcp2ITEv6KiwDSko92mPBE/TK6vU/o0hyzqh86Z2EbR5hdKt7856xkpWQsg+Z0mFNxi+AjuoRZ3y7O537/QIQWC2g9x4ghFVub9sUnO+1JS4xtHLfGGmu1cz1WDHsGzGpWA2rQCEpADcoo0Mowp5XtuAJb8a7PFbIimNddLAmrUWqplaTATUMVakttA45ygB6saWrVN+98LG6GjLOuYQpBUoM9UGW4pUZJSUFWtha6r2vR/2XJefucZYMBH09vZWGgARoEzalQvKLKGE4bU9W0q5vOdLrBymXO1xgDDn8Z9cW7OE5jn4pzeIp31t3OIb/3YgnSkw/0ednR1ejuMRZ5x9aHHO/zWo/H4/E3AAAA//8DAFkKilI2BAAA
|
||||
@ -0,0 +1,5 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACpRTy27cOBD8FYFnUeZLlKibMd5FBk7Wg8RIFnuj2K0MEZkcUJSTwJh/X1AzQeLcciOKrO7qquYLCfYJyUBuT6e/njHkhdQk+w0STGjKNGX8kYuhFYOSjTKaCan+IzXx9/idDKRTznFnkErUSBWwlo5ychQADWccmGhVqWk/L2R4IdY3gM/eYROXj5gWHwMZyCcfIH4tva1v3BxXaFKccR+WbIMrYv7Z7+7p29vD48Ph8mrBpZAbD2Qg0nGp1YTUdHKkyvScmpZz2kktOIex7YW50HzImIKdmwW+/OwPMQfMbhCNkA2jwpBzTcBmWySPdsHH76eiYrPoruD1Bm/H4YU8YyKDqH+YeSl342Z/4+LTkw1wM/nglyOpySnFE6bscbNjm3CebfYxVNcmu9ljyKQmDx+qw2zzFNPTK48+XEbf/8Hk76w7+oDV/o4MhHEUIHukk+Caqh4sta00VGupJ+TOdIKTmuxiyD6scV2qfcj4OW0qyUD+tvOCF323yR19RpfXVKT/q0vW+M3nXYQCMFKTQ4qwulz9tJuzhjWSlcuHNZ/WXL1H8AldxjLTY1qv5X+jCC020m5NCUOuDjYfqzd2OZKBGNs7bo011mrnJmwZTgyY1awD1KARlIAOlFGglWFOKztyBbbl41SQorukW20LpRlDC2qkUjJNFQOkFoWjrOUtMxa0A/HK1uphLjzreqYQJDU4AVWGW2qUlBRkazuhp64T0y+WXBPnrPgN+Hz5GhcFAD2C5lQo3lJlDKe9aTs6dv1oRt1j64rme0wB51+MeuddikuccnVdmOqVde/XUL53tS3PVx/oty2yu+i+YKpK5NaHss3XlM/n8/l/AAAA//8DAEtVZ1slBAAA
|
||||
@ -0,0 +1,5 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACpRUy27cOBD8FaHPosyXKFE3Y5xgB96sB8kgG+RGka2YsEwNSMqJYfjfF9TMwvFpsReCaLGK1dVFvUAwjwgDXJ9OH54w5AQ1ZL+VOOWKUEUoOzI+tHyQopGaCSXkd6jB3+IzDNBJa5nVSAQqJNLRloxissQ51IwyR3krC6f5kWB4AeMbh0/eYrOkrxiTXwIM8LcPbvlZ7ja+sfOyuiYuM+5DyibYIuav/e6W/Hl9ON4dzqcSpgJuvIMBhGVCyQmJ7sRIpO4Z0S1jpBOKM+bGtuf6DPMhYwxmbpJ7eLvfLTlgtgNvuGgo4Rpea3AmmyJ5NAmPz6eiYrPoptTrrbxthxd4wggDr/8180x3ZWd/9ZjG1c/u6rxmkx7Stk7G5iU+Qw2nuJwwZo+bP9cp4eM4Px9Nevh4PlO26cMvtGtGt1vWkGEAJhTUsBk0zyb7JVQXjbvZY8jbxxx9SN7+F5eAGu6+VIfZ5GmJj+/m8eVs8/5/uPzJ2HsfsNrfwACUIXeiRzJxpojsnSGmFZooJdSEzOqOM6hht4Tsw7qsqdqHjD/i1hIM8NHMCc/6rqO99xltXmPp85squTrExa02V2+zZLShjaC0gNZ8WnP1GZ2PaDOWJo5xvfB9fQ/him+g3RojhlwdTL6v/jDpHgbQprfMaKONUdZO2FKcqKNG0c6hcgqd5K5zUkunpKZWSTMy6UzLxqlUoAYs0am2tFo2GdTKEjnKjkhLOemtMASF0pJJw0dWPHnzsbqbC87Ynkp0gmicHJGaGaKlEMSJ1nRcTV3Hp98sueSB0ULm8On87jYFyrkenWKES9YSqTUjvW47Mnb9qEfVY2uL5luMAeffjPrkbVzSMuXqkpDqnXWf11D+HdWWlp8+kF/bjG4W+4CxKjM2PpSnchnr6+vr6z8AAAD//wMAN4ao/4IEAAA=
|
||||
@ -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+kJDmsIcGEpkxTxndcdLXolKyU0aqR5m9SknCO30lHGuUcdwapRI1UeVZTK3tHvUfDGfdM1GrJCTcT6Z4IhMrjQ3BYjdMfmKYwRtKRP0P04+OCDaFywzj7Ko0DbuKUIbqFzOXm5Jz+fny9u7o+3JpwWh5XwZOOSMelVj1S00hLlWk5NTXntJFacO5t3QpzeBZixhRhqCZ/94bvxxwxu05UQlaMCkOeS+Ihw0LZwoS77/uFxSrR6RIv1/D62z2RB0ykE+WrmId0R24IR/eTncPgj9YvKck+jXtMOeCqxtW2eCPBWcUqoQVjpCQX4G5DxGJzWlwNS4ngWqbQS2qw91QZDtQoKamXNTRC900jelKSc0wRh3dZL4JL4zT2uXgRufiAsz0+OYtgB1wwvsAwISnJZtpmiB6GMeLZN3RzPuTapXk5PhljDnEe56nYxIw3CV7OX9+fzClhzMU15NviN5huSUcMtI6DAQOgneuxZtgzz0CzxqP2Gr0SvvHKKK+VYU4rsFx5qLntlwgpya+LhtvZOZymNzarSYZh5VC89OlkCBjzBxlJRxhH4WWLtBdcU9V6oFBLQ7WWukfuTCP4K8wO0g1m0pG+tdYy56xQhjsQBqTvmfS6N0I0Dahe1o0SWtesAeebRnDlZYMOZC9ZK0hJrtPoZ5c/t1quDbhO4z/o8iIV6Yhmdd06sFZoxVswLSquVasNNkb1wJR0FnjdgrWcQc2caYUDrg0o6+v+h0pn8SZEPA3TfoDv73DbSlfyFwfQ8h7Up+tfEtzj45juLg82ri7PdqQkX+e4bIRis1jkMUT6TS8TfTEPOexuE4JHfzF6/NlHuIxLsU6ol1w72ViKogeqGmDUtojUaqcatNIZsahxOro7TMXiMAhxmaofya62xXFytyGjy3Na+P218rjaFtcD5H5M9x82ybvCPgtQCcnZ0hmPD4dttHLU3rfoNadC8ZoqYzhtTd1Q27TWWN1i7RbFrua8n3PxFX1I6PJa8YsXV8yTW3R3P4vx6oIXi3K2eG172GKrtP9riT2X5B5hmhPer9u6eyKbGDGtyKfzYRQ38SIMQ5jQjdFPpKuVairGhHhh+N/3jKqYEvr5+fn5XwAAAP//AwCgOTEOGwYAAA==
|
||||
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
41
PlotLine/Controllers/ExportsController.cs
Normal file
41
PlotLine/Controllers/ExportsController.cs
Normal file
@ -0,0 +1,41 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using PlotLine.Services;
|
||||
|
||||
namespace PlotLine.Controllers;
|
||||
|
||||
public sealed class ExportsController(IExportService exports) : Controller
|
||||
{
|
||||
public async Task<IActionResult> StoryBible(int projectId, string format = "html") =>
|
||||
ExportResult(await exports.ExportStoryBibleAsync(projectId, format), format);
|
||||
|
||||
public async Task<IActionResult> NarrativeTimeline(int projectId, string format = "html") =>
|
||||
ExportResult(await exports.ExportNarrativeTimelineAsync(projectId, format), format);
|
||||
|
||||
public async Task<IActionResult> Character(int id, string format = "html") =>
|
||||
ExportResult(await exports.ExportCharacterReferenceAsync(id, format), format);
|
||||
|
||||
public async Task<IActionResult> SceneBrief(int id, string format = "html") =>
|
||||
ExportResult(await exports.ExportSceneBriefAsync(id, format), format);
|
||||
|
||||
public async Task<IActionResult> ChapterBrief(int id, string format = "html") =>
|
||||
ExportResult(await exports.ExportChapterBriefAsync(id, format), format);
|
||||
|
||||
public async Task<IActionResult> WritingSession(int sceneId, string format = "html") =>
|
||||
ExportResult(await exports.ExportWriterSessionBriefAsync(sceneId, format), format);
|
||||
|
||||
public async Task<IActionResult> ResearchPack(int projectId, string format = "html") =>
|
||||
ExportResult(await exports.ExportResearchPackAsync(projectId, format), format);
|
||||
|
||||
public async Task<IActionResult> Search(int projectId, string q, string format = "html") =>
|
||||
ExportResult(await exports.ExportSearchResultsAsync(projectId, q, format), format);
|
||||
|
||||
private IActionResult ExportResult((string FileName, string ContentType, string Content) export, string format)
|
||||
{
|
||||
if (string.Equals(format, "html", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return Content(export.Content, export.ContentType);
|
||||
}
|
||||
|
||||
return File(System.Text.Encoding.UTF8.GetBytes(export.Content), export.ContentType, export.FileName);
|
||||
}
|
||||
}
|
||||
@ -10,8 +10,11 @@ public sealed class ScenesController(
|
||||
IAssetService assets,
|
||||
ICharacterService characters,
|
||||
ILocationService locations,
|
||||
IWriterWorkspaceService writerWorkspace) : Controller
|
||||
IWriterWorkspaceService writerWorkspace,
|
||||
IWebHostEnvironment environment) : Controller
|
||||
{
|
||||
private const long MaxAttachmentBytes = 25 * 1024 * 1024;
|
||||
|
||||
public async Task<IActionResult> Create(int chapterId)
|
||||
{
|
||||
var model = await scenes.GetCreateAsync(chapterId);
|
||||
@ -340,15 +343,33 @@ public sealed class ScenesController(
|
||||
return RedirectForScene(model.SceneID, model.ReturnProjectID, model.ReturnBookID);
|
||||
}
|
||||
|
||||
if (model.UploadedFile is not null && model.UploadedFile.Length > MaxAttachmentBytes)
|
||||
{
|
||||
TempData["WriterWorkspaceError"] = "Attachment files must be 25 MB or smaller.";
|
||||
return RedirectForScene(model.SceneID, model.ReturnProjectID, model.ReturnBookID);
|
||||
}
|
||||
|
||||
if (model.UploadedFile is not null && model.UploadedFile.Length > 0)
|
||||
{
|
||||
model.FilePath = await SaveUploadedAttachmentAsync(model.SceneID, model.UploadedFile);
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(model.FilePath) && string.IsNullOrWhiteSpace(model.ExternalUrl))
|
||||
{
|
||||
TempData["WriterWorkspaceError"] = "Upload a file or enter an external link.";
|
||||
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)
|
||||
public async Task<IActionResult> DeleteAttachment(int id, int sceneId, int? projectId, int? bookId, string? filePath)
|
||||
{
|
||||
await writerWorkspace.DeleteAttachmentAsync(id);
|
||||
DeleteUploadedAttachment(filePath);
|
||||
return RedirectForScene(sceneId, projectId, bookId);
|
||||
}
|
||||
|
||||
@ -361,4 +382,50 @@ public sealed class ScenesController(
|
||||
|
||||
return RedirectToAction(nameof(Edit), new { id = sceneId });
|
||||
}
|
||||
|
||||
private async Task<string> SaveUploadedAttachmentAsync(int sceneId, IFormFile uploadedFile)
|
||||
{
|
||||
var uploadRoot = Path.Combine(environment.WebRootPath, "uploads", "scenes", sceneId.ToString());
|
||||
Directory.CreateDirectory(uploadRoot);
|
||||
|
||||
var originalName = Path.GetFileName(uploadedFile.FileName);
|
||||
var safeName = MakeSafeFileName(originalName);
|
||||
var storedName = $"{Guid.NewGuid():N}_{safeName}";
|
||||
var absolutePath = Path.Combine(uploadRoot, storedName);
|
||||
|
||||
await using var stream = System.IO.File.Create(absolutePath);
|
||||
await uploadedFile.CopyToAsync(stream);
|
||||
|
||||
return $"/uploads/scenes/{sceneId}/{storedName}";
|
||||
}
|
||||
|
||||
private static string MakeSafeFileName(string fileName)
|
||||
{
|
||||
var name = string.IsNullOrWhiteSpace(fileName) ? "attachment" : fileName;
|
||||
foreach (var invalidChar in Path.GetInvalidFileNameChars())
|
||||
{
|
||||
name = name.Replace(invalidChar, '-');
|
||||
}
|
||||
|
||||
name = string.Join("-", name.Split([' ', '\t', '\r', '\n'], StringSplitOptions.RemoveEmptyEntries));
|
||||
return name.Length > 120 ? name[^120..] : name;
|
||||
}
|
||||
|
||||
private void DeleteUploadedAttachment(string? filePath)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(filePath) || !filePath.StartsWith("/uploads/scenes/", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var uploadsRoot = Path.GetFullPath(Path.Combine(environment.WebRootPath, "uploads"));
|
||||
var relativePath = filePath.TrimStart('/').Replace('/', Path.DirectorySeparatorChar);
|
||||
var absolutePath = Path.GetFullPath(Path.Combine(environment.WebRootPath, relativePath));
|
||||
if (!absolutePath.StartsWith(uploadsRoot, StringComparison.OrdinalIgnoreCase) || !System.IO.File.Exists(absolutePath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
System.IO.File.Delete(absolutePath);
|
||||
}
|
||||
}
|
||||
|
||||
@ -212,6 +212,18 @@ public interface IWriterWorkspaceRepository
|
||||
Task<IReadOnlyList<StoryBibleAttentionItem>> GetWriterDashboardRemindersAsync(int? projectId);
|
||||
}
|
||||
|
||||
public interface IExportRepository
|
||||
{
|
||||
Task<ExportPacket> GetStoryBibleAsync(int projectId);
|
||||
Task<ExportPacket> GetNarrativeTimelineAsync(int projectId);
|
||||
Task<ExportPacket> GetCharacterReferenceAsync(int characterId);
|
||||
Task<ExportPacket> GetSceneBriefAsync(int sceneId);
|
||||
Task<ExportPacket> GetChapterBriefAsync(int chapterId);
|
||||
Task<ExportPacket> GetWriterSessionBriefAsync(int sceneId);
|
||||
Task<ExportPacket> GetResearchPackAsync(int projectId);
|
||||
Task<ExportPacket> GetSearchResultsAsync(int projectId, string query);
|
||||
}
|
||||
|
||||
public sealed class TimelineData
|
||||
{
|
||||
public Project? Project { get; init; }
|
||||
@ -731,6 +743,101 @@ public sealed class WriterWorkspaceRepository(ISqlConnectionFactory connectionFa
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ExportRepository(ISqlConnectionFactory connectionFactory) : IExportRepository
|
||||
{
|
||||
public async Task<ExportPacket> GetStoryBibleAsync(int projectId)
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
using var result = await connection.QueryMultipleAsync("dbo.Export_StoryBible", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure);
|
||||
return new ExportPacket
|
||||
{
|
||||
Title = "Story Bible",
|
||||
Characters = (await result.ReadAsync<StoryBibleCharacterSummary>()).ToList(),
|
||||
PlotThreads = (await result.ReadAsync<StoryBibleThreadSummary>()).ToList(),
|
||||
Assets = (await result.ReadAsync<StoryBibleAssetSummary>()).ToList(),
|
||||
Relationships = (await result.ReadAsync<StoryBibleRelationshipSummary>()).ToList(),
|
||||
Locations = (await result.ReadAsync<StoryBibleLocationSummary>()).ToList(),
|
||||
Timeline = (await result.ReadAsync<StoryBibleTimelineSummary>()).ToList(),
|
||||
OpenQuestions = (await result.ReadAsync<StoryBibleAttentionItem>()).ToList(),
|
||||
RevisionItems = (await result.ReadAsync<StoryBibleRevisionItem>()).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<ExportPacket> GetNarrativeTimelineAsync(int projectId)
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
var rows = await connection.QueryAsync<StoryBibleTimelineSummary>("dbo.Export_NarrativeTimeline", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure);
|
||||
return new ExportPacket { Title = "Narrative Timeline Summary", Timeline = rows.ToList() };
|
||||
}
|
||||
|
||||
public async Task<ExportPacket> GetCharacterReferenceAsync(int characterId)
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
using var result = await connection.QueryMultipleAsync("dbo.Export_CharacterReference", new { CharacterID = characterId }, commandType: CommandType.StoredProcedure);
|
||||
return new ExportPacket
|
||||
{
|
||||
Title = "Character Reference Pack",
|
||||
Character = await result.ReadSingleOrDefaultAsync<Character>(),
|
||||
CharacterRelationships = (await result.ReadAsync<CharacterRelationship>()).ToList(),
|
||||
CharacterAppearances = (await result.ReadAsync<SceneCharacter>()).ToList(),
|
||||
CharacterKnowledge = (await result.ReadAsync<CharacterKnowledgeItem>()).ToList(),
|
||||
CharacterCustody = (await result.ReadAsync<AssetCustodyEvent>()).ToList(),
|
||||
Items = (await result.ReadAsync<ExportBriefItem>()).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<ExportPacket> GetSceneBriefAsync(int sceneId)
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
using var result = await connection.QueryMultipleAsync("dbo.Export_SceneBrief", new { SceneID = sceneId }, commandType: CommandType.StoredProcedure);
|
||||
return new ExportPacket
|
||||
{
|
||||
Title = "Scene Brief",
|
||||
SceneHeader = await result.ReadSingleOrDefaultAsync<ExportSceneBriefHeader>(),
|
||||
Items = (await result.ReadAsync<ExportBriefItem>()).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<ExportPacket> GetChapterBriefAsync(int chapterId)
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
using var result = await connection.QueryMultipleAsync("dbo.Export_ChapterBrief", new { ChapterID = chapterId }, commandType: CommandType.StoredProcedure);
|
||||
return new ExportPacket
|
||||
{
|
||||
Title = "Chapter Brief",
|
||||
ChapterHeader = await result.ReadSingleOrDefaultAsync<ExportChapterBriefHeader>(),
|
||||
Timeline = (await result.ReadAsync<StoryBibleTimelineSummary>()).ToList(),
|
||||
Items = (await result.ReadAsync<ExportBriefItem>()).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<ExportPacket> GetWriterSessionBriefAsync(int sceneId)
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
using var result = await connection.QueryMultipleAsync("dbo.Export_WriterSessionBrief", new { SceneID = sceneId }, commandType: CommandType.StoredProcedure);
|
||||
return new ExportPacket
|
||||
{
|
||||
Title = "Writing Session Brief",
|
||||
SceneHeader = await result.ReadSingleOrDefaultAsync<ExportSceneBriefHeader>(),
|
||||
Items = (await result.ReadAsync<ExportBriefItem>()).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<ExportPacket> GetResearchPackAsync(int projectId)
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
var rows = await connection.QueryAsync<ExportResearchItem>("dbo.Export_ResearchPack", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure);
|
||||
return new ExportPacket { Title = "Research Pack", ResearchItems = rows.ToList() };
|
||||
}
|
||||
|
||||
public async Task<ExportPacket> GetSearchResultsAsync(int projectId, string query)
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
var rows = await connection.QueryAsync<StoryBibleSearchResult>("dbo.Export_SearchResults", new { ProjectID = projectId, Query = query }, commandType: CommandType.StoredProcedure);
|
||||
return new ExportPacket { Title = $"Search Results: {query}", SearchResults = rows.ToList() };
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class SceneDependencyRepository(ISqlConnectionFactory connectionFactory) : ISceneDependencyRepository
|
||||
{
|
||||
public async Task<SceneDependencyLookupData> GetLookupsAsync()
|
||||
|
||||
@ -1276,6 +1276,95 @@ public sealed class ChapterWorkflowScene
|
||||
public int UnresolvedNoteCount { get; set; }
|
||||
}
|
||||
|
||||
public sealed class ExportSceneBriefHeader
|
||||
{
|
||||
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? ScenePurposeNotes { get; set; }
|
||||
public string? SceneOutcomeNotes { get; set; }
|
||||
public string RevisionStatusName { get; set; } = string.Empty;
|
||||
public string? PovCharacterName { get; set; }
|
||||
public string? LocationPath { get; set; }
|
||||
public string TimeLabel { get; set; } = string.Empty;
|
||||
public string DraftStatus { get; set; } = "Planned";
|
||||
public bool IsBlocked { get; set; }
|
||||
public string? BlockedReason { get; set; }
|
||||
public int? Priority { get; set; }
|
||||
}
|
||||
|
||||
public sealed class ExportBriefItem
|
||||
{
|
||||
public string ItemType { get; set; } = string.Empty;
|
||||
public string Title { get; set; } = string.Empty;
|
||||
public string? Detail { get; set; }
|
||||
public string? Status { get; set; }
|
||||
public int? SortOrder { get; set; }
|
||||
}
|
||||
|
||||
public sealed class ExportChapterBriefHeader
|
||||
{
|
||||
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 string? Summary { get; set; }
|
||||
public string RevisionStatusName { get; set; } = string.Empty;
|
||||
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 ExportResearchItem
|
||||
{
|
||||
public string BookTitle { get; set; } = string.Empty;
|
||||
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 Topic { get; set; } = string.Empty;
|
||||
public string Title { get; set; } = string.Empty;
|
||||
public string? Detail { get; set; }
|
||||
public string? Reference { get; set; }
|
||||
}
|
||||
|
||||
public sealed class ExportPacket
|
||||
{
|
||||
public string Title { get; set; } = string.Empty;
|
||||
public IReadOnlyList<StoryBibleCharacterSummary> Characters { get; init; } = [];
|
||||
public IReadOnlyList<StoryBibleThreadSummary> PlotThreads { get; init; } = [];
|
||||
public IReadOnlyList<StoryBibleAssetSummary> Assets { get; init; } = [];
|
||||
public IReadOnlyList<StoryBibleRelationshipSummary> Relationships { get; init; } = [];
|
||||
public IReadOnlyList<StoryBibleLocationSummary> Locations { get; init; } = [];
|
||||
public IReadOnlyList<StoryBibleTimelineSummary> Timeline { get; init; } = [];
|
||||
public IReadOnlyList<StoryBibleAttentionItem> OpenQuestions { get; init; } = [];
|
||||
public IReadOnlyList<StoryBibleRevisionItem> RevisionItems { get; init; } = [];
|
||||
public ExportSceneBriefHeader? SceneHeader { get; init; }
|
||||
public ExportChapterBriefHeader? ChapterHeader { get; init; }
|
||||
public Character? Character { get; init; }
|
||||
public IReadOnlyList<SceneCharacter> CharacterAppearances { get; init; } = [];
|
||||
public IReadOnlyList<CharacterRelationship> CharacterRelationships { get; init; } = [];
|
||||
public IReadOnlyList<CharacterKnowledgeItem> CharacterKnowledge { get; init; } = [];
|
||||
public IReadOnlyList<AssetCustodyEvent> CharacterCustody { get; init; } = [];
|
||||
public IReadOnlyList<ExportBriefItem> Items { get; init; } = [];
|
||||
public IReadOnlyList<ExportResearchItem> ResearchItems { get; init; } = [];
|
||||
public IReadOnlyList<StoryBibleSearchResult> SearchResults { get; init; } = [];
|
||||
}
|
||||
|
||||
public sealed class AnalyticsBusyScene
|
||||
{
|
||||
public int SceneID { get; set; }
|
||||
|
||||
@ -34,6 +34,7 @@ public class Program
|
||||
builder.Services.AddScoped<IAnalyticsRepository, AnalyticsRepository>();
|
||||
builder.Services.AddScoped<IStoryBibleRepository, StoryBibleRepository>();
|
||||
builder.Services.AddScoped<IWriterWorkspaceRepository, WriterWorkspaceRepository>();
|
||||
builder.Services.AddScoped<IExportRepository, ExportRepository>();
|
||||
builder.Services.AddScoped<IProjectService, ProjectService>();
|
||||
builder.Services.AddScoped<IBookService, BookService>();
|
||||
builder.Services.AddScoped<IChapterService, ChapterService>();
|
||||
@ -47,6 +48,7 @@ public class Program
|
||||
builder.Services.AddScoped<IAnalyticsService, AnalyticsService>();
|
||||
builder.Services.AddScoped<IStoryBibleService, StoryBibleService>();
|
||||
builder.Services.AddScoped<IWriterWorkspaceService, WriterWorkspaceService>();
|
||||
builder.Services.AddScoped<IExportService, ExportService>();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
@ -57,6 +59,7 @@ public class Program
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
app.UseStaticFiles();
|
||||
app.UseRouting();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using PlotLine.Data;
|
||||
using PlotLine.Models;
|
||||
@ -164,6 +166,18 @@ public interface IWriterWorkspaceService
|
||||
Task DeleteAttachmentAsync(int sceneAttachmentId);
|
||||
}
|
||||
|
||||
public interface IExportService
|
||||
{
|
||||
Task<(string FileName, string ContentType, string Content)> ExportStoryBibleAsync(int projectId, string format);
|
||||
Task<(string FileName, string ContentType, string Content)> ExportNarrativeTimelineAsync(int projectId, string format);
|
||||
Task<(string FileName, string ContentType, string Content)> ExportCharacterReferenceAsync(int characterId, string format);
|
||||
Task<(string FileName, string ContentType, string Content)> ExportSceneBriefAsync(int sceneId, string format);
|
||||
Task<(string FileName, string ContentType, string Content)> ExportChapterBriefAsync(int chapterId, string format);
|
||||
Task<(string FileName, string ContentType, string Content)> ExportWriterSessionBriefAsync(int sceneId, string format);
|
||||
Task<(string FileName, string ContentType, string Content)> ExportResearchPackAsync(int projectId, string format);
|
||||
Task<(string FileName, string ContentType, string Content)> ExportSearchResultsAsync(int projectId, string query, string format);
|
||||
}
|
||||
|
||||
public sealed class ProjectService(IProjectRepository projects, IBookRepository books) : IProjectService
|
||||
{
|
||||
public Task<IReadOnlyList<Project>> ListAsync() => projects.ListAsync();
|
||||
@ -1894,6 +1908,274 @@ public sealed class WriterWorkspaceService(
|
||||
public Task DeleteAttachmentAsync(int sceneAttachmentId) => writerWorkspace.DeleteAttachmentAsync(sceneAttachmentId);
|
||||
}
|
||||
|
||||
public sealed class ExportService(IExportRepository exports) : IExportService
|
||||
{
|
||||
public async Task<(string FileName, string ContentType, string Content)> ExportStoryBibleAsync(int projectId, string format)
|
||||
=> Render(await exports.GetStoryBibleAsync(projectId), "story-bible", format);
|
||||
|
||||
public async Task<(string FileName, string ContentType, string Content)> ExportNarrativeTimelineAsync(int projectId, string format)
|
||||
=> Render(await exports.GetNarrativeTimelineAsync(projectId), "narrative-timeline", format);
|
||||
|
||||
public async Task<(string FileName, string ContentType, string Content)> ExportCharacterReferenceAsync(int characterId, string format)
|
||||
=> Render(await exports.GetCharacterReferenceAsync(characterId), "character-reference", format);
|
||||
|
||||
public async Task<(string FileName, string ContentType, string Content)> ExportSceneBriefAsync(int sceneId, string format)
|
||||
=> Render(await exports.GetSceneBriefAsync(sceneId), "scene-brief", format);
|
||||
|
||||
public async Task<(string FileName, string ContentType, string Content)> ExportChapterBriefAsync(int chapterId, string format)
|
||||
=> Render(await exports.GetChapterBriefAsync(chapterId), "chapter-brief", format);
|
||||
|
||||
public async Task<(string FileName, string ContentType, string Content)> ExportWriterSessionBriefAsync(int sceneId, string format)
|
||||
=> Render(await exports.GetWriterSessionBriefAsync(sceneId), "writing-session-brief", format);
|
||||
|
||||
public async Task<(string FileName, string ContentType, string Content)> ExportResearchPackAsync(int projectId, string format)
|
||||
=> Render(await exports.GetResearchPackAsync(projectId), "research-pack", format);
|
||||
|
||||
public async Task<(string FileName, string ContentType, string Content)> ExportSearchResultsAsync(int projectId, string query, string format)
|
||||
=> Render(await exports.GetSearchResultsAsync(projectId, query), "search-results", format);
|
||||
|
||||
private static (string FileName, string ContentType, string Content) Render(ExportPacket packet, string slug, string format)
|
||||
{
|
||||
var normalised = string.IsNullOrWhiteSpace(format) ? "html" : format.Trim().ToLowerInvariant();
|
||||
return normalised switch
|
||||
{
|
||||
"markdown" or "md" => ($"{slug}.md", "text/markdown; charset=utf-8", RenderMarkdown(packet)),
|
||||
"text" or "txt" => ($"{slug}.txt", "text/plain; charset=utf-8", RenderText(packet)),
|
||||
_ => ($"{slug}.html", "text/html; charset=utf-8", RenderHtml(packet))
|
||||
};
|
||||
}
|
||||
|
||||
private static string RenderMarkdown(ExportPacket packet)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
builder.AppendLine($"# {packet.Title}").AppendLine();
|
||||
AppendPacket(builder, packet, markdown: true);
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private static string RenderText(ExportPacket packet)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
builder.AppendLine(packet.Title).AppendLine(new string('=', packet.Title.Length)).AppendLine();
|
||||
AppendPacket(builder, packet, markdown: false);
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private static string RenderHtml(ExportPacket packet)
|
||||
{
|
||||
var body = RenderMarkdown(packet)
|
||||
.Split('\n')
|
||||
.Select(line =>
|
||||
{
|
||||
var text = WebUtility.HtmlEncode(line.TrimEnd());
|
||||
if (text.StartsWith("### ")) return $"<h3>{text[4..]}</h3>";
|
||||
if (text.StartsWith("## ")) return $"<h2>{text[3..]}</h2>";
|
||||
if (text.StartsWith("# ")) return $"<h1>{text[2..]}</h1>";
|
||||
if (text.StartsWith("- ")) return $"<li>{text[2..]}</li>";
|
||||
return string.IsNullOrWhiteSpace(text) ? "" : $"<p>{text}</p>";
|
||||
});
|
||||
return $$"""
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>{{WebUtility.HtmlEncode(packet.Title)}}</title>
|
||||
<style>
|
||||
body { font-family: Georgia, 'Times New Roman', serif; line-height: 1.55; max-width: 920px; margin: 32px auto; padding: 0 24px; color: #24201a; }
|
||||
h1, h2, h3 { font-family: Arial, sans-serif; line-height: 1.2; }
|
||||
h1 { border-bottom: 2px solid #d8d0c2; padding-bottom: 12px; }
|
||||
h2 { margin-top: 32px; border-bottom: 1px solid #e6dfd2; padding-bottom: 6px; }
|
||||
li { margin: 5px 0; }
|
||||
@media print { body { margin: 0; max-width: none; } a { color: inherit; } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{{string.Join(Environment.NewLine, body)}}
|
||||
</body>
|
||||
</html>
|
||||
""";
|
||||
}
|
||||
|
||||
private static void AppendPacket(StringBuilder builder, ExportPacket packet, bool markdown)
|
||||
{
|
||||
if (packet.SceneHeader is not null) AppendSceneHeader(builder, packet.SceneHeader, markdown);
|
||||
if (packet.ChapterHeader is not null) AppendChapterHeader(builder, packet.ChapterHeader, markdown);
|
||||
if (packet.Character is not null) AppendCharacter(builder, packet);
|
||||
AppendTimeline(builder, packet.Timeline, markdown);
|
||||
AppendCharacters(builder, packet.Characters);
|
||||
AppendThreads(builder, packet.PlotThreads);
|
||||
AppendAssets(builder, packet.Assets);
|
||||
AppendRelationships(builder, packet.Relationships);
|
||||
AppendLocations(builder, packet.Locations);
|
||||
AppendItems(builder, "Brief Details", packet.Items, markdown);
|
||||
AppendResearch(builder, packet.ResearchItems, markdown);
|
||||
AppendSearch(builder, packet.SearchResults, markdown);
|
||||
AppendAttention(builder, packet.OpenQuestions);
|
||||
AppendRevision(builder, packet.RevisionItems);
|
||||
}
|
||||
|
||||
private static void Heading(StringBuilder builder, string text, bool markdown, int level = 2)
|
||||
=> builder.AppendLine(markdown ? $"{new string('#', level)} {text}" : text).AppendLine(markdown ? "" : new string('-', text.Length));
|
||||
|
||||
private static void AppendSceneHeader(StringBuilder builder, ExportSceneBriefHeader scene, bool markdown)
|
||||
{
|
||||
Heading(builder, $"{scene.BookTitle} / Chapter {scene.ChapterNumber} / Scene {scene.SceneNumber}: {scene.SceneTitle}", markdown);
|
||||
builder.AppendLine($"- POV: {scene.PovCharacterName ?? "Not set"}");
|
||||
builder.AppendLine($"- Location: {scene.LocationPath ?? "Not set"}");
|
||||
builder.AppendLine($"- Time: {scene.TimeLabel}");
|
||||
builder.AppendLine($"- Revision: {scene.RevisionStatusName}");
|
||||
builder.AppendLine($"- Draft status: {scene.DraftStatus}");
|
||||
if (scene.Priority.HasValue) builder.AppendLine($"- Priority: {scene.Priority}");
|
||||
if (scene.IsBlocked) builder.AppendLine($"- Blocked: {scene.BlockedReason}");
|
||||
AppendBlock(builder, "Summary", scene.Summary, markdown);
|
||||
AppendBlock(builder, "Purpose", scene.ScenePurposeNotes, markdown);
|
||||
AppendBlock(builder, "Outcome", scene.SceneOutcomeNotes, markdown);
|
||||
}
|
||||
|
||||
private static void AppendChapterHeader(StringBuilder builder, ExportChapterBriefHeader chapter, bool markdown)
|
||||
{
|
||||
Heading(builder, $"{chapter.BookTitle} / Chapter {chapter.ChapterNumber}: {chapter.ChapterTitle}", markdown);
|
||||
builder.AppendLine($"- Revision: {chapter.RevisionStatusName}");
|
||||
AppendBlock(builder, "Summary", chapter.Summary, markdown);
|
||||
AppendBlock(builder, "Goal", chapter.GoalSummary, markdown);
|
||||
AppendBlock(builder, "Emotion", chapter.EmotionalGoal, markdown);
|
||||
AppendBlock(builder, "Reader Takeaway", chapter.ReaderTakeaway, markdown);
|
||||
AppendBlock(builder, "Must Include", chapter.MustInclude, markdown);
|
||||
AppendBlock(builder, "Risks", chapter.RisksOrConcerns, markdown);
|
||||
}
|
||||
|
||||
private static void AppendBlock(StringBuilder builder, string title, string? text, bool markdown)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text)) return;
|
||||
builder.AppendLine(markdown ? $"### {title}" : title).AppendLine(text.Trim()).AppendLine();
|
||||
}
|
||||
|
||||
private static void AppendTimeline(StringBuilder builder, IReadOnlyList<StoryBibleTimelineSummary> rows, bool markdown)
|
||||
{
|
||||
if (!rows.Any()) return;
|
||||
Heading(builder, "Timeline Summary", markdown);
|
||||
foreach (var book in rows.GroupBy(x => new { x.BookID, x.BookTitle }))
|
||||
{
|
||||
builder.AppendLine(markdown ? $"### {book.Key.BookTitle}" : book.Key.BookTitle);
|
||||
foreach (var scene in book)
|
||||
{
|
||||
builder.AppendLine($"- Chapter {scene.ChapterNumber}, Scene {scene.SceneNumber}: {scene.SceneTitle}");
|
||||
builder.AppendLine($" POV: {scene.PovCharacterName ?? "Not set"}; Location: {scene.LocationPath ?? "Not set"}; Status: {scene.RevisionStatusName}");
|
||||
if (!string.IsNullOrWhiteSpace(scene.Summary)) builder.AppendLine($" {scene.Summary}");
|
||||
}
|
||||
builder.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
private static void AppendCharacters(StringBuilder builder, IReadOnlyList<StoryBibleCharacterSummary> rows)
|
||||
{
|
||||
if (!rows.Any()) return;
|
||||
builder.AppendLine("## Characters").AppendLine();
|
||||
foreach (var c in rows) builder.AppendLine($"- {c.CharacterName}: {c.SceneCount} scenes; first {c.FirstSceneLabel ?? "not placed"}; last {c.LastSceneLabel ?? "not placed"}");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void AppendThreads(StringBuilder builder, IReadOnlyList<StoryBibleThreadSummary> rows)
|
||||
{
|
||||
if (!rows.Any()) return;
|
||||
builder.AppendLine("## Plot Threads").AppendLine();
|
||||
foreach (var t in rows) builder.AppendLine($"- {t.ThreadTitle} ({t.ThreadStatusName}, importance {t.Importance}): {t.EventHistory ?? "No events"}");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void AppendAssets(StringBuilder builder, IReadOnlyList<StoryBibleAssetSummary> rows)
|
||||
{
|
||||
if (!rows.Any()) return;
|
||||
builder.AppendLine("## Story Assets").AppendLine();
|
||||
foreach (var a in rows) builder.AppendLine($"- {a.AssetName} ({a.KindName}, {a.CurrentStateName ?? "No state"}): {a.EventHistory ?? "No events"}");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void AppendRelationships(StringBuilder builder, IReadOnlyList<StoryBibleRelationshipSummary> rows)
|
||||
{
|
||||
if (!rows.Any()) return;
|
||||
builder.AppendLine("## Relationships").AppendLine();
|
||||
foreach (var r in rows) builder.AppendLine($"- {r.CharacterAName} / {r.CharacterBName}: {r.RelationshipTypeName}; {r.CurrentStateName ?? "No state"}");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void AppendLocations(StringBuilder builder, IReadOnlyList<StoryBibleLocationSummary> rows)
|
||||
{
|
||||
if (!rows.Any()) return;
|
||||
builder.AppendLine("## Locations").AppendLine();
|
||||
foreach (var l in rows) builder.AppendLine($"- {l.LocationPath}: {l.SceneCount} scenes, {l.CharacterCount} characters, {l.AssetCount} assets");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void AppendCharacter(StringBuilder builder, ExportPacket packet)
|
||||
{
|
||||
var c = packet.Character!;
|
||||
builder.AppendLine($"## {c.CharacterName}").AppendLine();
|
||||
builder.AppendLine($"- Short name: {c.ShortName}");
|
||||
builder.AppendLine($"- Sex: {c.Sex}");
|
||||
builder.AppendLine($"- Age at series start: {c.AgeAtSeriesStart}");
|
||||
builder.AppendLine($"- Height: {c.Height}");
|
||||
builder.AppendLine($"- Eye colour: {c.EyeColour}");
|
||||
if (!string.IsNullOrWhiteSpace(c.DefaultDescription)) builder.AppendLine().AppendLine(c.DefaultDescription).AppendLine();
|
||||
foreach (var app in packet.CharacterAppearances) builder.AppendLine($"- Appearance: {app.BookTitle} / Ch {app.ChapterNumber} / Scene {app.SceneNumber}: {app.SceneTitle} ({app.RoleInSceneTypeName})");
|
||||
foreach (var rel in packet.CharacterRelationships) builder.AppendLine($"- Relationship: {rel.CharacterAName} / {rel.CharacterBName}: {rel.RelationshipTypeName}");
|
||||
foreach (var item in packet.CharacterKnowledge) builder.AppendLine($"- Knowledge: {item.KnowledgeStateName} - {item.AssetName ?? item.ThreadTitle}: {item.Description}");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void AppendItems(StringBuilder builder, string title, IReadOnlyList<ExportBriefItem> rows, bool markdown)
|
||||
{
|
||||
if (!rows.Any()) return;
|
||||
Heading(builder, title, markdown);
|
||||
foreach (var group in rows.GroupBy(x => x.ItemType))
|
||||
{
|
||||
builder.AppendLine(markdown ? $"### {group.Key}" : group.Key);
|
||||
foreach (var item in group) builder.AppendLine($"- {item.Title}{(string.IsNullOrWhiteSpace(item.Status) ? "" : $" [{item.Status}]")}{(string.IsNullOrWhiteSpace(item.Detail) ? "" : $": {item.Detail}")}");
|
||||
builder.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
private static void AppendResearch(StringBuilder builder, IReadOnlyList<ExportResearchItem> rows, bool markdown)
|
||||
{
|
||||
if (!rows.Any()) return;
|
||||
Heading(builder, "Research Pack", markdown);
|
||||
foreach (var item in rows)
|
||||
{
|
||||
builder.AppendLine($"- {item.BookTitle} / Ch {item.ChapterNumber} / Scene {item.SceneNumber}: {item.Topic} - {item.Title}");
|
||||
if (!string.IsNullOrWhiteSpace(item.Detail)) builder.AppendLine($" {item.Detail}");
|
||||
if (!string.IsNullOrWhiteSpace(item.Reference)) builder.AppendLine($" {item.Reference}");
|
||||
}
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void AppendSearch(StringBuilder builder, IReadOnlyList<StoryBibleSearchResult> rows, bool markdown)
|
||||
{
|
||||
if (!rows.Any()) return;
|
||||
Heading(builder, "Search Results", markdown);
|
||||
foreach (var group in rows.GroupBy(x => x.ResultType))
|
||||
{
|
||||
builder.AppendLine(markdown ? $"### {group.Key}" : group.Key);
|
||||
foreach (var result in group) builder.AppendLine($"- {result.Title}{(string.IsNullOrWhiteSpace(result.Detail) ? "" : $": {result.Detail}")}");
|
||||
}
|
||||
}
|
||||
|
||||
private static void AppendAttention(StringBuilder builder, IReadOnlyList<StoryBibleAttentionItem> rows)
|
||||
{
|
||||
if (!rows.Any()) return;
|
||||
builder.AppendLine("## Open Questions and Warnings").AppendLine();
|
||||
foreach (var item in rows) builder.AppendLine($"- {item.Category}: {item.Title}{(string.IsNullOrWhiteSpace(item.Detail) ? "" : $" - {item.Detail}")}");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void AppendRevision(StringBuilder builder, IReadOnlyList<StoryBibleRevisionItem> rows)
|
||||
{
|
||||
if (!rows.Any()) return;
|
||||
builder.AppendLine("## Revision Status").AppendLine();
|
||||
foreach (var item in rows) builder.AppendLine($"- {item.GroupName}: {item.ItemCount} {item.Detail}");
|
||||
builder.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class AssetService(IProjectRepository projects, IAssetRepository assets, ILocationRepository locations) : IAssetService
|
||||
{
|
||||
public async Task<StoryAssetListViewModel?> GetAssetsAsync(int projectId)
|
||||
|
||||
278
PlotLine/Sql/017_Phase1Q_Exports.sql
Normal file
278
PlotLine/Sql/017_Phase1Q_Exports.sql
Normal file
@ -0,0 +1,278 @@
|
||||
SET ANSI_NULLS ON;
|
||||
GO
|
||||
SET QUOTED_IDENTIFIER ON;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.Export_StoryBible
|
||||
@ProjectID int
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
EXEC dbo.StoryBible_CharacterSummaries @ProjectID;
|
||||
EXEC dbo.StoryBible_ThreadSummaries @ProjectID;
|
||||
EXEC dbo.StoryBible_AssetSummaries @ProjectID;
|
||||
EXEC dbo.StoryBible_RelationshipSummaries @ProjectID;
|
||||
EXEC dbo.StoryBible_LocationSummaries @ProjectID;
|
||||
EXEC dbo.StoryBible_TimelineSummary @ProjectID;
|
||||
EXEC dbo.StoryBible_OpenQuestions @ProjectID;
|
||||
EXEC dbo.StoryBible_RevisionProgress @ProjectID;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.Export_NarrativeTimeline
|
||||
@ProjectID int
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
EXEC dbo.StoryBible_TimelineSummary @ProjectID;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.Export_SceneBrief
|
||||
@SceneID int
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT p.ProjectID, p.ProjectName, b.BookID, b.BookTitle, c.ChapterID, c.ChapterNumber, c.ChapterTitle,
|
||||
s.SceneID, s.SceneNumber, s.SceneTitle, s.Summary, s.ScenePurposeNotes, s.SceneOutcomeNotes,
|
||||
rs.StatusName AS RevisionStatusName,
|
||||
pov.CharacterName AS PovCharacterName,
|
||||
location.LocationPath,
|
||||
COALESCE(s.RelativeTimeText, CONVERT(nvarchar(30), s.StartDateTime, 113), N'Time not set') AS TimeLabel,
|
||||
COALESCE(sw.DraftStatus, N'Planned') AS DraftStatus,
|
||||
COALESCE(sw.IsBlocked, 0) AS IsBlocked,
|
||||
sw.BlockedReason,
|
||||
sw.Priority
|
||||
FROM dbo.Scenes s
|
||||
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
|
||||
INNER JOIN dbo.Books b ON b.BookID = c.BookID
|
||||
INNER JOIN dbo.Projects p ON p.ProjectID = b.ProjectID
|
||||
INNER JOIN dbo.RevisionStatuses rs ON rs.RevisionStatusID = s.RevisionStatusID
|
||||
LEFT JOIN dbo.Characters pov ON pov.CharacterID = COALESCE(s.POVCharacterID, c.POVCharacterID)
|
||||
LEFT JOIN dbo.LocationPaths location ON location.LocationID = s.PrimaryLocationID
|
||||
LEFT JOIN dbo.SceneWorkflow sw ON sw.SceneID = s.SceneID
|
||||
WHERE s.SceneID = @SceneID;
|
||||
|
||||
SELECT N'Purpose' AS ItemType, spt.PurposeName AS Title, CAST(NULL AS nvarchar(max)) AS Detail, CAST(NULL AS nvarchar(100)) AS Status, spt.SortOrder
|
||||
FROM dbo.ScenePurposes sp
|
||||
INNER JOIN dbo.ScenePurposeTypes spt ON spt.ScenePurposeTypeID = sp.ScenePurposeTypeID
|
||||
WHERE sp.SceneID = @SceneID
|
||||
UNION ALL
|
||||
SELECT N'Character', ch.CharacterName, CONCAT(ISNULL(role.TypeName, N'No role'), N' / ', ISNULL(presence.TypeName, N'No presence'), N'. ', ISNULL(sc.AppearanceNotes, N'')), ISNULL(location.LocationPath, N'No location'), 100
|
||||
FROM dbo.SceneCharacters sc
|
||||
INNER JOIN dbo.Characters ch ON ch.CharacterID = sc.CharacterID
|
||||
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
|
||||
WHERE sc.SceneID = @SceneID
|
||||
UNION ALL
|
||||
SELECT N'Plot Thread', pt.ThreadTitle, CONCAT(tet.TypeName, N': ', te.EventTitle, N'. ', ISNULL(te.EventDescription, N'')), CAST(pt.Importance AS nvarchar(20)), 200
|
||||
FROM dbo.ThreadEvents te
|
||||
INNER JOIN dbo.PlotThreads pt ON pt.PlotThreadID = te.PlotThreadID
|
||||
INNER JOIN dbo.ThreadEventTypes tet ON tet.ThreadEventTypeID = te.EventTypeID
|
||||
WHERE te.SceneID = @SceneID
|
||||
UNION ALL
|
||||
SELECT N'Asset', sa.AssetName, CONCAT(aet.TypeName, N': ', ae.EventTitle, N'. ', ISNULL(ae.EventDescription, N'')), ast.StateName, 300
|
||||
FROM dbo.AssetEvents ae
|
||||
INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = ae.StoryAssetID
|
||||
INNER JOIN dbo.AssetEventTypes aet ON aet.AssetEventTypeID = ae.AssetEventTypeID
|
||||
LEFT JOIN dbo.AssetStates ast ON ast.AssetStateID = ae.ToStateID
|
||||
WHERE ae.SceneID = @SceneID
|
||||
UNION ALL
|
||||
SELECT N'Relationship', CONCAT(ca.CharacterName, N' / ', cb.CharacterName), CONCAT(rs.StateName, N'. ', ISNULL(re.Description, N'')), CAST(re.Intensity AS nvarchar(20)), 400
|
||||
FROM dbo.RelationshipEvents re
|
||||
INNER JOIN dbo.CharacterRelationships cr ON cr.CharacterRelationshipID = re.CharacterRelationshipID
|
||||
INNER JOIN dbo.Characters ca ON ca.CharacterID = cr.CharacterAID
|
||||
INNER JOIN dbo.Characters cb ON cb.CharacterID = cr.CharacterBID
|
||||
INNER JOIN dbo.RelationshipStates rs ON rs.RelationshipStateID = re.RelationshipStateID
|
||||
WHERE re.SceneID = @SceneID
|
||||
UNION ALL
|
||||
SELECT N'Note', COALESCE(sn.NoteTitle, snt.TypeName), sn.NoteText, CASE WHEN sn.IsResolved = 1 THEN N'Resolved' ELSE N'Open' END, 500
|
||||
FROM dbo.SceneNotes sn
|
||||
INNER JOIN dbo.SceneNoteTypes snt ON snt.SceneNoteTypeID = sn.SceneNoteTypeID
|
||||
WHERE sn.SceneID = @SceneID
|
||||
UNION ALL
|
||||
SELECT N'Attachment', sa.Title, CONCAT(ISNULL(sa.Notes, N''), N' ', ISNULL(sa.ExternalUrl, N''), N' ', ISNULL(sa.FilePath, N'')), sat.TypeName, 600
|
||||
FROM dbo.SceneAttachments sa
|
||||
INNER JOIN dbo.SceneAttachmentTypes sat ON sat.SceneAttachmentTypeID = sa.SceneAttachmentTypeID
|
||||
WHERE sa.SceneID = @SceneID
|
||||
UNION ALL
|
||||
SELECT N'Checklist', sci.ItemText, CAST(NULL AS nvarchar(max)), CASE WHEN sci.IsCompleted = 1 THEN N'Done' ELSE N'Open' END, 700
|
||||
FROM dbo.SceneChecklistItems sci
|
||||
WHERE sci.SceneID = @SceneID
|
||||
UNION ALL
|
||||
SELECT N'Warning', wt.TypeName, cw.Message, ws.SeverityName, 800
|
||||
FROM dbo.ContinuityWarnings cw
|
||||
INNER JOIN dbo.WarningTypes wt ON wt.WarningTypeID = cw.WarningTypeID
|
||||
INNER JOIN dbo.WarningSeverities ws ON ws.WarningSeverityID = cw.WarningSeverityID
|
||||
WHERE cw.SceneID = @SceneID AND cw.IsDismissed = 0
|
||||
ORDER BY SortOrder, ItemType, Title;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.Export_ChapterBrief
|
||||
@ChapterID int
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT p.ProjectID, p.ProjectName, b.BookID, b.BookTitle, c.ChapterID, c.ChapterNumber, c.ChapterTitle,
|
||||
c.Summary, rs.StatusName AS RevisionStatusName, cg.GoalSummary, cg.EmotionalGoal, cg.ReaderTakeaway,
|
||||
cg.MustInclude, cg.RisksOrConcerns
|
||||
FROM dbo.Chapters c
|
||||
INNER JOIN dbo.Books b ON b.BookID = c.BookID
|
||||
INNER JOIN dbo.Projects p ON p.ProjectID = b.ProjectID
|
||||
INNER JOIN dbo.RevisionStatuses rs ON rs.RevisionStatusID = c.RevisionStatusID
|
||||
LEFT JOIN dbo.ChapterGoals cg ON cg.ChapterID = c.ChapterID
|
||||
WHERE c.ChapterID = @ChapterID;
|
||||
|
||||
SELECT b.BookID, b.BookTitle, c.ChapterID, c.ChapterNumber, c.ChapterTitle, s.SceneID, s.SceneNumber,
|
||||
s.SceneTitle, s.Summary, pov.CharacterName AS PovCharacterName, location.LocationPath, rs.StatusName AS RevisionStatusName
|
||||
FROM dbo.Scenes s
|
||||
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
|
||||
INNER JOIN dbo.Books b ON b.BookID = c.BookID
|
||||
INNER JOIN dbo.RevisionStatuses rs ON rs.RevisionStatusID = s.RevisionStatusID
|
||||
LEFT JOIN dbo.Characters pov ON pov.CharacterID = COALESCE(s.POVCharacterID, c.POVCharacterID)
|
||||
LEFT JOIN dbo.LocationPaths location ON location.LocationID = s.PrimaryLocationID
|
||||
WHERE s.ChapterID = @ChapterID AND s.IsArchived = 0
|
||||
ORDER BY s.SortOrder, s.SceneID;
|
||||
|
||||
SELECT N'Warning' AS ItemType, wt.TypeName AS Title, cw.Message AS Detail, ws.SeverityName AS Status, 10 AS SortOrder
|
||||
FROM dbo.ContinuityWarnings cw
|
||||
INNER JOIN dbo.WarningTypes wt ON wt.WarningTypeID = cw.WarningTypeID
|
||||
INNER JOIN dbo.WarningSeverities ws ON ws.WarningSeverityID = cw.WarningSeverityID
|
||||
WHERE cw.ChapterID = @ChapterID AND cw.IsDismissed = 0
|
||||
UNION ALL
|
||||
SELECT N'Open Thread', pt.ThreadTitle, pt.Summary, ts.StatusName, 20
|
||||
FROM dbo.ThreadEvents te
|
||||
INNER JOIN dbo.Scenes s ON s.SceneID = te.SceneID
|
||||
INNER JOIN dbo.PlotThreads pt ON pt.PlotThreadID = te.PlotThreadID
|
||||
INNER JOIN dbo.ThreadStatuses ts ON ts.ThreadStatusID = pt.ThreadStatusID
|
||||
WHERE s.ChapterID = @ChapterID AND pt.IsArchived = 0 AND ts.IsOpenStatus = 1
|
||||
UNION ALL
|
||||
SELECT N'Open Asset', sa.AssetName, sa.Description, ast.StateName, 30
|
||||
FROM dbo.AssetEvents ae
|
||||
INNER JOIN dbo.Scenes s ON s.SceneID = ae.SceneID
|
||||
INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = ae.StoryAssetID
|
||||
LEFT JOIN dbo.AssetStates ast ON ast.AssetStateID = sa.CurrentStateID
|
||||
WHERE s.ChapterID = @ChapterID AND sa.IsArchived = 0 AND sa.IsResolved = 0
|
||||
UNION ALL
|
||||
SELECT N'Open Note', COALESCE(sn.NoteTitle, snt.TypeName), sn.NoteText, snt.TypeName, 40
|
||||
FROM dbo.SceneNotes sn
|
||||
INNER JOIN dbo.SceneNoteTypes snt ON snt.SceneNoteTypeID = sn.SceneNoteTypeID
|
||||
INNER JOIN dbo.Scenes s ON s.SceneID = sn.SceneID
|
||||
WHERE s.ChapterID = @ChapterID AND sn.IsResolved = 0
|
||||
ORDER BY SortOrder, ItemType, Title;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.Export_WriterSessionBrief
|
||||
@SceneID int
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
EXEC dbo.Export_SceneBrief @SceneID;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.Export_CharacterReference
|
||||
@CharacterID int
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
DECLARE @ProjectID int = (SELECT ProjectID FROM dbo.Characters WHERE CharacterID = @CharacterID);
|
||||
|
||||
SELECT CharacterID, ProjectID, CharacterName, ShortName, Sex, BirthDate, AgeAtSeriesStart, AgeReferenceSceneID,
|
||||
Height, EyeColour, DefaultDescription, CreatedDate, UpdatedDate, IsArchived
|
||||
FROM dbo.Characters
|
||||
WHERE CharacterID = @CharacterID;
|
||||
|
||||
EXEC dbo.Relationship_ListByCharacter @CharacterID;
|
||||
|
||||
SELECT sc.SceneCharacterID, sc.SceneID, sc.CharacterID, ch.ProjectID, ch.CharacterName, ch.ShortName, ch.BirthDate, ch.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, c.ChapterNumber, b.BookTitle,
|
||||
sc.CreatedDate, sc.UpdatedDate
|
||||
FROM dbo.SceneCharacters sc
|
||||
INNER JOIN dbo.Characters ch ON ch.CharacterID = sc.CharacterID
|
||||
INNER JOIN dbo.Scenes s ON s.SceneID = sc.SceneID
|
||||
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
|
||||
INNER JOIN dbo.Books b ON b.BookID = c.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 sc.CharacterID = @CharacterID AND s.IsArchived = 0 AND c.IsArchived = 0 AND b.IsArchived = 0
|
||||
ORDER BY b.SortOrder, c.SortOrder, s.SortOrder;
|
||||
|
||||
EXEC dbo.CharacterKnowledge_ListByCharacter @CharacterID;
|
||||
|
||||
SELECT ace.AssetCustodyEventID, ace.StoryAssetID, sa.AssetName, ace.SceneID,
|
||||
ace.AssetCustodyEventTypeID, acet.TypeName AS CustodyEventTypeName, ace.Description,
|
||||
STRING_AGG(CONCAT(COALESCE(custodian.CharacterName, aec.CharacterNameText), N' (', cr.RoleName, N')'), N', ') AS CustodianSummary,
|
||||
ace.CreatedDate, ace.UpdatedDate, b.BookTitle, ch.ChapterNumber, s.SceneNumber, s.SceneTitle
|
||||
FROM dbo.AssetCustodyEvents ace
|
||||
INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = ace.StoryAssetID
|
||||
INNER JOIN dbo.AssetCustodyEventTypes acet ON acet.AssetCustodyEventTypeID = ace.AssetCustodyEventTypeID
|
||||
INNER JOIN dbo.Scenes s ON s.SceneID = ace.SceneID
|
||||
INNER JOIN dbo.Chapters ch ON ch.ChapterID = s.ChapterID
|
||||
INNER JOIN dbo.Books b ON b.BookID = ch.BookID
|
||||
INNER JOIN dbo.AssetCustodyEventCharacters aec ON aec.AssetCustodyEventID = ace.AssetCustodyEventID
|
||||
LEFT JOIN dbo.Characters custodian ON custodian.CharacterID = aec.CharacterID
|
||||
LEFT JOIN dbo.CustodyRoles cr ON cr.CustodyRoleID = aec.CustodyRoleID
|
||||
WHERE aec.CharacterID = @CharacterID
|
||||
GROUP BY ace.AssetCustodyEventID, ace.StoryAssetID, sa.AssetName, ace.SceneID, ace.AssetCustodyEventTypeID, acet.TypeName, ace.Description, ace.CreatedDate, ace.UpdatedDate, b.BookTitle, ch.ChapterNumber, s.SceneNumber, s.SceneTitle, b.SortOrder, ch.SortOrder, s.SortOrder
|
||||
ORDER BY b.SortOrder, ch.SortOrder, s.SortOrder;
|
||||
|
||||
SELECT N'Note' AS ItemType, COALESCE(sn.NoteTitle, snt.TypeName) AS Title, sn.NoteText AS Detail,
|
||||
CASE WHEN sn.IsResolved = 1 THEN N'Resolved' ELSE N'Open' END AS Status, 10 AS SortOrder
|
||||
FROM dbo.SceneNotes sn
|
||||
INNER JOIN dbo.SceneNoteTypes snt ON snt.SceneNoteTypeID = sn.SceneNoteTypeID
|
||||
INNER JOIN dbo.SceneCharacters sc ON sc.SceneID = sn.SceneID
|
||||
WHERE sc.CharacterID = @CharacterID AND sn.IsResolved = 0
|
||||
ORDER BY SortOrder, Title;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.Export_ResearchPack
|
||||
@ProjectID int
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT b.BookTitle, c.ChapterNumber, c.ChapterTitle, s.SceneID, s.SceneNumber, s.SceneTitle,
|
||||
snt.TypeName AS Topic, COALESCE(sn.NoteTitle, snt.TypeName) AS Title, sn.NoteText AS Detail,
|
||||
CAST(NULL AS nvarchar(1000)) AS Reference
|
||||
FROM dbo.SceneNotes sn
|
||||
INNER JOIN dbo.SceneNoteTypes snt ON snt.SceneNoteTypeID = sn.SceneNoteTypeID
|
||||
INNER JOIN dbo.Scenes s ON s.SceneID = sn.SceneID
|
||||
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
|
||||
INNER JOIN dbo.Books b ON b.BookID = c.BookID
|
||||
WHERE b.ProjectID = @ProjectID AND snt.TypeName IN (N'Research Note', N'Continuity Note', N'Timeline Note') AND s.IsArchived = 0 AND c.IsArchived = 0 AND b.IsArchived = 0
|
||||
UNION ALL
|
||||
SELECT b.BookTitle, c.ChapterNumber, c.ChapterTitle, s.SceneID, s.SceneNumber, s.SceneTitle,
|
||||
sat.TypeName, sa.Title, sa.Notes, COALESCE(sa.ExternalUrl, sa.FilePath)
|
||||
FROM dbo.SceneAttachments sa
|
||||
INNER JOIN dbo.SceneAttachmentTypes sat ON sat.SceneAttachmentTypeID = sa.SceneAttachmentTypeID
|
||||
INNER JOIN dbo.Scenes s ON s.SceneID = sa.SceneID
|
||||
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
|
||||
INNER JOIN dbo.Books b ON b.BookID = c.BookID
|
||||
WHERE b.ProjectID = @ProjectID AND s.IsArchived = 0 AND c.IsArchived = 0 AND b.IsArchived = 0
|
||||
ORDER BY BookTitle, ChapterNumber, SceneNumber, Topic, Title;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.Export_SearchResults
|
||||
@ProjectID int,
|
||||
@Query nvarchar(200)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
EXEC dbo.StoryBible_Search @ProjectID, @Query;
|
||||
END;
|
||||
GO
|
||||
@ -1,4 +1,5 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using PlotLine.Models;
|
||||
|
||||
@ -249,6 +250,7 @@ public sealed class SceneAttachmentEditViewModel
|
||||
public string? FilePath { get; set; }
|
||||
public string? ExternalUrl { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
public IFormFile? UploadedFile { get; set; }
|
||||
}
|
||||
|
||||
public sealed class ChapterGoalEditViewModel
|
||||
|
||||
@ -42,6 +42,7 @@
|
||||
<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-outline-primary" asp-controller="Exports" asp-action="ChapterBrief" asp-route-id="@Model.Chapter.ChapterID">Chapter brief</a>
|
||||
<a class="btn btn-primary" asp-controller="Scenes" asp-action="Create" asp-route-chapterId="@Model.Chapter.ChapterID">Add scene</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -33,6 +33,8 @@
|
||||
<div class="button-row">
|
||||
<a class="btn btn-outline-secondary" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID">Characters</a>
|
||||
<a class="btn btn-outline-secondary" asp-controller="Timeline" asp-action="Index" asp-route-ProjectID="@Model.Project.ProjectID" asp-route-SelectedSceneID="@firstAppearance?.SceneID" asp-route-FocusType="character" asp-route-FocusID="@Model.Character.CharacterID">Back to timeline</a>
|
||||
<a class="btn btn-outline-primary" asp-controller="Exports" asp-action="Character" asp-route-id="@Model.Character.CharacterID">Export character</a>
|
||||
<a class="btn btn-outline-secondary" asp-controller="Exports" asp-action="Character" asp-route-id="@Model.Character.CharacterID" asp-route-format="markdown">Markdown</a>
|
||||
<a class="btn btn-outline-primary" asp-action="Edit" asp-route-id="@Model.Character.CharacterID">Edit character</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -41,7 +41,15 @@
|
||||
<p class="eyebrow">@Model.Book?.BookTitle / Chapter @Model.Chapter?.ChapterNumber</p>
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
</div>
|
||||
<a class="btn btn-outline-secondary" asp-controller="Chapters" asp-action="Details" asp-route-id="@Model.ChapterID">Back to chapter</a>
|
||||
<div class="button-row">
|
||||
@if (Model.SceneID > 0)
|
||||
{
|
||||
<a class="btn btn-outline-primary" asp-controller="Exports" asp-action="SceneBrief" asp-route-id="@Model.SceneID">Scene brief</a>
|
||||
<a class="btn btn-outline-secondary" asp-controller="Exports" asp-action="SceneBrief" asp-route-id="@Model.SceneID" asp-route-format="markdown">Markdown</a>
|
||||
<a class="btn btn-outline-primary" asp-controller="Exports" asp-action="WritingSession" asp-route-sceneId="@Model.SceneID">Prepare Writing Session</a>
|
||||
}
|
||||
<a class="btn btn-outline-secondary" asp-controller="Chapters" asp-action="Details" asp-route-id="@Model.ChapterID">Back to chapter</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="edit-panel scene-editor">
|
||||
|
||||
@ -384,19 +384,50 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="writer-note-list">
|
||||
<div class="attachment-card-list">
|
||||
@foreach (var attachment in Model.Attachments)
|
||||
{
|
||||
<details class="writer-mini-card">
|
||||
<summary>
|
||||
var hasFile = !string.IsNullOrWhiteSpace(attachment.FilePath);
|
||||
var fileHref = hasFile && attachment.FilePath!.StartsWith("http", StringComparison.OrdinalIgnoreCase)
|
||||
? attachment.FilePath
|
||||
: hasFile && attachment.FilePath!.StartsWith("/")
|
||||
? attachment.FilePath
|
||||
: hasFile && !System.IO.Path.IsPathRooted(attachment.FilePath!)
|
||||
? "/" + attachment.FilePath!.TrimStart('/', '\\')
|
||||
: null;
|
||||
<details class="writer-mini-card attachment-card">
|
||||
<summary class="attachment-summary">
|
||||
<span class="attachment-kind">@(hasFile ? "File" : "Link")</span>
|
||||
<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">
|
||||
<div class="attachment-card-body">
|
||||
@if (!string.IsNullOrWhiteSpace(attachment.Notes))
|
||||
{
|
||||
<p>@attachment.Notes</p>
|
||||
}
|
||||
@if (hasFile)
|
||||
{
|
||||
<p class="muted">@System.IO.Path.GetFileName(attachment.FilePath)</p>
|
||||
}
|
||||
<div class="button-row compact-buttons">
|
||||
@if (!string.IsNullOrWhiteSpace(fileHref))
|
||||
{
|
||||
<a class="btn btn-outline-secondary btn-sm" href="@fileHref" target="_blank" rel="noopener">Open</a>
|
||||
<a class="btn btn-outline-secondary btn-sm" href="@fileHref" download>Download</a>
|
||||
}
|
||||
@if (!string.IsNullOrWhiteSpace(attachment.ExternalUrl))
|
||||
{
|
||||
<a class="btn btn-outline-secondary btn-sm" href="@attachment.ExternalUrl" target="_blank" rel="noopener">Open link</a>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<form asp-controller="Scenes" asp-action="SaveAttachment" method="post" enctype="multipart/form-data" 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" />
|
||||
<input type="hidden" name="FilePath" value="@attachment.FilePath" />
|
||||
<select class="form-select form-select-sm" name="SceneAttachmentTypeID">
|
||||
@foreach (var option in Model.SceneAttachmentTypeOptions)
|
||||
{
|
||||
@ -404,8 +435,9 @@
|
||||
}
|
||||
</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" />
|
||||
<label class="form-label mb-0">Replace file</label>
|
||||
<input class="form-control form-control-sm" name="UploadedFile" type="file" />
|
||||
<input class="form-control form-control-sm" name="ExternalUrl" value="@attachment.ExternalUrl" placeholder="Or external URL" />
|
||||
<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>
|
||||
@ -414,31 +446,31 @@
|
||||
<input type="hidden" name="sceneId" value="@Model.SceneID" />
|
||||
<input type="hidden" name="projectId" value="@Model.ReturnProjectID" />
|
||||
<input type="hidden" name="bookId" value="@Model.ReturnBookID" />
|
||||
<input type="hidden" name="filePath" value="@attachment.FilePath" />
|
||||
<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">
|
||||
<details class="writer-mini-card attachment-add-card">
|
||||
<summary><strong>+ Add Attachment</strong></summary>
|
||||
<form asp-controller="Scenes" asp-action="SaveAttachment" method="post" enctype="multipart/form-data" 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" />
|
||||
<label class="form-label mb-0">Attachment type</label>
|
||||
<select class="form-select form-select-sm" name="SceneAttachmentTypeID" asp-items="Model.SceneAttachmentTypeOptions"></select>
|
||||
<label class="form-label mb-0">Title</label>
|
||||
<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" />
|
||||
<label class="form-label mb-0">Choose file</label>
|
||||
<input class="form-control form-control-sm" name="UploadedFile" type="file" />
|
||||
<label class="form-label mb-0">Or external URL</label>
|
||||
<input class="form-control form-control-sm" name="ExternalUrl" placeholder="https://..." />
|
||||
<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>
|
||||
</details>
|
||||
</section>
|
||||
|
||||
<section class="inspector-section warning-inspector-section" data-section-title="Continuity Warnings" data-section-id="inspector-warnings">
|
||||
|
||||
@ -42,6 +42,14 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="button-row export-action-row">
|
||||
<a class="btn btn-outline-primary btn-sm" asp-controller="Exports" asp-action="StoryBible" asp-route-projectId="@Model.Project.ProjectID">Printable Story Bible</a>
|
||||
<a class="btn btn-outline-secondary btn-sm" asp-controller="Exports" asp-action="StoryBible" asp-route-projectId="@Model.Project.ProjectID" asp-route-format="markdown">Markdown</a>
|
||||
<a class="btn btn-outline-secondary btn-sm" asp-controller="Exports" asp-action="StoryBible" asp-route-projectId="@Model.Project.ProjectID" asp-route-format="text">Text</a>
|
||||
<a class="btn btn-outline-primary btn-sm" asp-controller="Exports" asp-action="NarrativeTimeline" asp-route-projectId="@Model.Project.ProjectID">Timeline export</a>
|
||||
<a class="btn btn-outline-primary btn-sm" asp-controller="Exports" asp-action="ResearchPack" asp-route-projectId="@Model.Project.ProjectID">Research pack</a>
|
||||
</div>
|
||||
|
||||
<nav class="story-bible-nav" aria-label="Story Bible sections">
|
||||
<a href="#characters">Characters</a>
|
||||
<a href="#threads">Plot Threads</a>
|
||||
@ -62,6 +70,7 @@
|
||||
<h2>Results for "@Model.Query"</h2>
|
||||
</div>
|
||||
<a class="btn btn-outline-secondary btn-sm" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID">Clear search</a>
|
||||
<a class="btn btn-outline-primary btn-sm" asp-controller="Exports" asp-action="Search" asp-route-projectId="@Model.Project.ProjectID" asp-route-q="@Model.Query" asp-route-format="markdown">Export results</a>
|
||||
</div>
|
||||
@if (!Model.SearchResults.Any())
|
||||
{
|
||||
|
||||
@ -23,6 +23,8 @@
|
||||
</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-primary" asp-controller="Exports" asp-action="ChapterBrief" asp-route-id="@Model.Chapter.ChapterID">Export brief</a>
|
||||
<a class="btn btn-outline-secondary" asp-controller="Exports" asp-action="ChapterBrief" asp-route-id="@Model.Chapter.ChapterID" asp-route-format="markdown">Markdown</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>
|
||||
|
||||
@ -42,6 +42,7 @@ else
|
||||
}
|
||||
<div class="story-bible-actions">
|
||||
<a asp-controller="Scenes" asp-action="Edit" asp-route-id="@scene.SceneID">Open scene</a>
|
||||
<a asp-controller="Exports" asp-action="WritingSession" asp-route-sceneId="@scene.SceneID">Session brief</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>
|
||||
|
||||
@ -2644,6 +2644,39 @@ body.dragging-location [data-drag-type="location"] {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.attachment-card-list {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.attachment-summary {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.attachment-kind {
|
||||
border-radius: 999px;
|
||||
padding: 3px 8px;
|
||||
background: rgba(47, 111, 99, 0.1);
|
||||
color: var(--plotline-accent);
|
||||
font-size: 0.76rem;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.attachment-card-body {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.attachment-card-body p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.attachment-add-card {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.writer-checklist {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
@ -2709,6 +2742,26 @@ body.dragging-location [data-drag-type="location"] {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.export-action-row {
|
||||
margin: -8px 0 18px;
|
||||
}
|
||||
|
||||
@media print {
|
||||
header,
|
||||
.breadcrumb-trail,
|
||||
.context-header,
|
||||
.button-row,
|
||||
.story-bible-nav,
|
||||
.inspector-quick-nav {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.plotline-shell {
|
||||
max-width: none;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 760px) {
|
||||
.plotline-shell {
|
||||
padding-left: 14px;
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 816 KiB |
Loading…
x
Reference in New Issue
Block a user