Phase 3A Help System

This commit is contained in:
Nick Beckley 2026-06-04 20:00:13 +01:00
parent dd2917693a
commit 7ab5ccf374
35 changed files with 474 additions and 30 deletions

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSUXW/jKBSG/4rFtXEBY2x81013tFGn02ianV3tHYZDiupAhHFnqyr/fYSTqumM9mLvrGPe8/GcF16RV3tAPbo+HH5/Bp8mVKLklhAjTGAiMOFb2vVU9k1T1awhksl/UIncLbygHrVca6ol4BoEYG5Ig4faamwMSEqoIazhOafaTah/RcpVBp6dhipM3yBOLnjUo7+cN+F7rq1cpccwmyqGEdZ+Ssrr3MyX9eoWf77ebO83p1MTTFlcOYN6JJVVmmuDhw46zC0o3NXSYgOm1YzUumnqk8z5BNGrsZrM03t9E5KHpHtWsboimEl0LJFRSeWWBzXB9uWQu1gQ3eR4uYSXz/4VPUNEPSvfYJ7SXenRXe2nYXajuRrDbuf8bhW8dbs5qpQLl+gQwwFicrDA+c15FV8+h90u50Of1DgBKtEW4t55NZ5+3IBV85gewhwXMpN5+q8zqEdqTgGVaCE5jkvZ4jzManTg0y/ai8L3D8VmVMmGuP+wpIcT+/X/QH+n9KPzUKxvUI8IBWbqDrBlVGDeGYVVU0ssRC0sUC1bRlGJVsEn5+cwT8XaJ9idqX3o7zrqR5dApznmmf4W2WybGMysU/G+YEoqUtWEZNGcDnMqvoJxEXSCPMQ2zud8P0mYYIvokxvhFzyrOUbwqdio9Fj8oabHBUenqZJKKiW0ttAQsMQQJUhrQBgBhjPTGi65EVwSLbgaKDeqoYPNEVQiyCYrFl+LRrJaGoLtQAnmDenwoEWNOz5YKjtqO9V+gFvcj1mndEc4mBpLsAZzSRWWvK6xqRvVMmHbltkLTmdDUJKpX5rwzwnM2UtfTtZ+m93A8+ken/o0pgMjKGacNphLSXEnmxYPbTfIQXTQ6DzZKvgpvIH8BnEIk0v5Eblz3u3V+POR983cQvQwXmznzukYpmBTcbZl8WFfX2efX7Fiseh35/G/izFugn6CWGRjKecvlnk8Ho8/AAAA//8DAEJm1vgMBQAA

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSUW2/cNhCF/4rAZ1Hm/aI3w27RhZt6ES/aoG8UZxQL0UoLito0MPa/F9RukLpveRMGOsPDb87wjUzhiKQl96fTL2ec8kJqkoetJJgwlBnK1IG7lvtW60Zo7a11f5OaDE/4jbTEqhh59EglGqQKmKad7CMFQM8ZBya0Kj3D54W0byQMDeB5iNjMy5+YlmGeSEv+GiaYv5azw9DEcV6hSfOIu2nJYYrFzB+7hyf6+/3+8Ly//rXgUsTNAKQlPvQhqgi0c+io6jFQJ31PAcFGwWTUWl5lw5QxTWFsFvjy43yY84Q5tqIRsmFUeHKpCYQciuUuLHj4diouNkSPpV5v5e2zfSNnTKQV9XeY13Z3cRzulrUb8YzjKaQF012cj8cwAanJKc0nTHnADcsZUzcvQy5EY7TBOc7BBau0BSe1kBYkQ3DMavDgg1GmF1HrHjrnreoVCK7AoFKdJTXZwI1jyMM8VTfvD+OAUyY1eX6p9mPI/ZyO79C/XInufgJosU1aIpmWxlpUUsrgTScYgjXRGNHFwGzfCeQRmfbKem4Zl14wL7WxzkJgPpKafAjxdZiw2j2SljCOAqRD2gtuqHIQaNDSU2Ok6ZFHbwUnNXmYpzxM67wu1W7K+Dlt9yUt+TWMC15vep/i65Ax5jUVCJ9MCeM+zbDGXP0IAGcNayRjRbTm05qrjwhDwpix4Dik9dbvfxJhxCZ6WFPCKVf7kF+r38LyujF0kQcffAgmxh41w54BC4ZZQAMGQQmwoLwCozyLRoWOKwiad32pkJpgyVu1RdwEpYzskArBLVUGDHVBO6qwQyV7Jhj07zhWz2PRheiYQpDUYw9UeR6oV1JSkDpYYXprRdF9R3ILC2cFMOD5uqxXBwAOwXAqFNdUec+p89rSzrrOd8ahjsXzE6YJx/+A+jDENC9zn6tb1qp36D6uU3lwqi13X4eJ/rPN6HGOXzBVZcZhmMp+3cZ6qckRw7ImPG7vVftG9mW5qsP2bBnTMO5lTV5ySHk93crWNd4xcblcLv8CAAD//wMAL93JHfQEAAA=

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRUwW7cIBT8FYuzcQBjbHyLNo26Stus2lVb9YZ5zwmKg1eA00bR/nuFd6s0p6oXhJ6Zx7yZwS/Em0ckPbk8HN49oU+RlCS5tSSYUJQpyuSed71gPZOVZqxjrfpBSuJu8Jn0pJXWcquR1qiQSmANHerRUgDUnHFgopG5p7mLpH8hxlWAT85iNcevGKKbPenJN+dh/pnvNq6y07xAFeYJtz4m420m82m7uaEfLnf7293pVMSYwZUD0hNtRmOlBTp02FE5oqFdrUcKCK0VrLZNU59gzicM3kxVhIfX+2FOHpPtRSXqilGhybEkYJLJlAcTcf98yCxWia5yvVzL67Z/IU8YSC/KP2Ke2l3YyV08xmFxE1yc1mTiQ1zX0dg0h2dSkkOYDxiSw1WfyxjxcZie9yY+XJ/O5G189wvtkhA28+IT6QmvO1KSVaBpMsnNvjhz3EwOfVo/puB8dPZfvbI0t1+K3WTSOIfHN358Ocm8/Q+VPxp77zwW2yvSE8ZRQN0hHQVXVHZgqGlqTZWq1Yjc6lZwUpLN7JPzy7zEYusT3oV1JNKTazNFPPG7DPbeJbRpCXnO7yrnahdmWGwqXr3krGJVzVgGLemwpOIzggtoE+Yh9mE59/v6FiKUWEGbJQT0qdiZdF+8N/F+nbyz3GijjVHWjtgwHBkwo1gLqEAhSAEtSC1BSc2skmbgEkzDhzFXSEkwR6dY0yrVoKQdWqp1K6jsFKdDMwramLqRo5YDDtncVx2L2ynjjO2YRKipxhGo1NxQLeuaQt2YVqixbcX4lyTnPHCWBQZ8Or27lYEC6BAUp0LyhkqtOe1009Kh7QY9qA4bmznfYPA4/SXUR2fDHOcxFeeEFG+k+7z4/O8o1rT8dJ7+Wj26mu0DhiJ7bJzPT+Vs6/F4PP4GAAD//wMAZSWeyoIEAAA=

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACoxU227cNhD9FYGvFRXeRJF6c20HXaSOjeyiLfo2Ioc2a5laUJSTwPC/F9LauaJAXwRhSM45c+bMPJEED0h6cnY8Xj5iKjOpSYlbSDChKdOUqQM3vWA9U41lWigj/yY1ie/wM+lJp5zjziKVqJEqz1o6yOCo92g5456JVq054XYm/ROB2Hh8jA6baf4D8xynRHryZ0x++rhiQ2zcOC2+ydOIuzQXSG4l8353/o7+fnZzuL453ZpxXh830ZOeWAjglPN0MGioCgjUSBuoR985waRrW3l6FlPBnGBsZn//Fd9PJWFxvWiEbBgVljzXxEOBlfIAMx4+H1cWm0QXa7zewttv/0QeMZNe1K9intK9cWN88zAPSxz9m+1LanLM0xFzibipcb2vvpLgrGGN0IIxUpMrcHcxYbW7qK7HtURwhin0kloMnirLgVolJfWyhU7o0HUikJq8w5xw/CbrVXR5mqdQqheRq+9w9mfnlwmGEVeMtzDOSGqym/cFkodxSnj5Cd1STrkOeVmPz6dUYlqmZa52qeBthpfz1/fnS86YSnUD5a76Dea7rUXGcbBgAbRzAVuGgXkGmnUetdfolfCdV1Z5rSxzWsHAlYeWD2GNkJr8umq4X5zDef7KZjPJOG4cqpc+nY8RU/lORtITxlF4aZAGwTVVxgOFVlqqtdQBubOd4K8wB8i3WEhPghmGgTk3CGW5A2FB+sCk18EK0XWggmw7JbRuWQfOd53gyssOHcggmRGkJjd58osrP7Zabg24ydM/6MoqFemJZm1rHAyD0IobsAYV18poi51VAZiSbgDeGhgGzqBlzhrhgGsLavBt+KLSZbqNCS/ifBzh8ze4ptGN/MUBGB5A/XD9bYYH/Djl+/cnGzfvLw+kJh+WtG6Earda5GNM9JNeJ/pqGUs83GUEj/5q8vizj3Adl2qbUOOw7bQJlAfDqWKar0bmVA+MgYUOguxITS4md4+5Wh0GMa1T9SXZ9b46y+4uFnRlySu/vzYe1/vqZoQSpvzw3Sb5prAfBWiE5GztjMfH0zbaOGrvDXrNqVC8pcpaTo1tOzp0ZrCDNti6VbHrpRyXUn1AHzO6slX84sUN8/wO3f3PYry64MWinK1e25+22Cbt/1pizzV5QJiXjA/btu6fyC4lzBvyxXIaxV26iuMYZ3RT8jPpbWtFwxjrXhj+572Oi4YJpZ+fn5//BQAA//8DAJsBfCEbBgAA

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSTwW7cOAyGX8XQ2XIkWZYt34LJLjpIuxm0QbvYGy3SHaGOPJDltEUw776QZ4o2vfUiCLRJ/vx+6oUFeCLWs9vT6a9nCmlhJUt+CymhDBeGC/0ou16JXujKilao1v7HSubv6TvrWaudk84Sr8kQ1ygaPtSj44hkpZAoVKNzTfi8sP6Fga+Qnr2jal4+Ulz8HFjPPvmA89fcG3zlpnnFKs4T7cOSILgs5p/97p6/vT08Phwufy205OTKI+uZhRGcdsiHjjquRwLe1XbkSNg6JWrXNPUlzYdEMcBULfjlZ3+cU6DkelWpuhJcWXYuGUKCLHmAhR6/n7KKDdFdjpdbeLv2L+yZIutV+QPmpdyNm/yNm5+eIODN6INfjqxkpzifKCZPG45twmmC5OdQXJvsJk8hsZI9fCgOE6Rxjk+vGH24jL7/g8nfgTv6QMX+jvVMSFJYd8RHJQ3XHQKHprbcmNqMJJ1tlWQl280h+bDO61LsQ6LPcVPJevY3TAtd9N1Gd/SJXFpjlv6vyV7TN592M+aAYCU7xBlXl4qfuKWoRFWL/PFhTac1Fe8JfSSXKM/0GNdr+d9SlFFb0m6NkUIqDpCOxRtYjhuIzkmwYAGMcyM1gkaBAoxokQwaQq2wRW01Gm2FMxoGqREaOYw5knVnd4vLQllXDyCQNwQt12CAD2I72g7G1kLXuFdYi4cp54HrhCasuaURubYSuNV1zbFuoFVmbFs1/oLk6rgUmTfS8+VpbAoMYkdoJFdaNlxbK3lnm5YPbTfYwXTUuKz5nmKg6RdQ77yL8zKPqbguTPEK3fs15OddbMvz1Qf+bbPsbnZfKBbZcvAhb/PV5fP5fP4fAAD//wMAIOQppCUEAAA=

View File

@ -1,5 +0,0 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSTy27bMBBFf0Xg2lT4EiVqFzgtaqRtjMRoi+4ozqgmolAGReWBwP9eUHaRtLtuBGHAy7n3zPCVBPuApCWXh8OHRwxpIiuS/FISTGjKNGVqx5tWsJap0jApeMN+khXx1/hCWlIr57gzSCVqpApYRTvZOwqAhjMOTFQq32l/TaR9JdaXgI/eYTlO3zBOfgykJd99gPEp97a+dMM4QxnHATdhSja4bObrZn1NP19udzfb06kJpywuPZCWGNtbpxzQrsGGqh4tbaTpKSDUTjDpqkqeZD4kjMEO5QT3b/1hTAGTa0UpZMmoMOS4ImCTzZY7O+Hu5ZBdLIiucn21lJff9pU8YiStWP2Bebruwg3+4mHqZj/Axemb7HSfUx7ieMCYPC5MlpjDYJMfQ3HutB48hkRW5Oau2A429WN8+AvU3Sn/5j/i73L3D8/o5oSwHueQSEu44mRFvli39wGLzRVpCeMoQDZIe8E1VQ1YaitpqNZS98idqUXWrMeQfJjHeSo2IeGvuAQgLflohwlP1i+j2/uELs0xp/qh8y5s4wizS8Ubf85KVkrGsmhOhzkVtwg+okuY8+3ifL7vH4nQYhGt5xgxpGJr0774ZKf9AqVx3BprrNXO9Vgx7Bkwq1kNqEEjKAE1KKNAK8OcVrbjCmzFuz5XyIpgHnexbFgHoI2sKtqJilFVy54aXnVUKF2j1KbuMCveOBY3Q9ZZ1zCFIKnBHqgy3FKjpKQgK1sL3de16N8hOU+fswwY8PH0VhYHGqBB0JwKxSuqjOG0MVVNu7rpTKcbrFx2cI0x4PAO1Bfv4jiNfSrOy1P8he52Dvm9F8siPflAn5cZXY3uHmORZ2x9yOt9HuvxeDz+BgAA//8DAO0UJnI2BAAA

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRTy27cOBD8FYFnUeZLlKibMd5FBk7Wg8RIFntrka0MEZkcUJSTwJh/X1AzgZPcciOKrO7qquYLCfCEZCC3p9NfzxjyQmqS/QYJJjRlmjL1yPuhbYdWNr3oW87Uf6Qm/h6/k4F0ylpuDVKJGqlyrKWjnCx1Dg1n3DHRqlITPi9keCHgG4fP3mITl4+YFh8DGcgnH1z8WnqDb+wcV9ekOOM+LBmCLWL+2e/u6dvbw+PD4fJqwaWQG+/IQEB0I5ecUQYgqVKAFEbRU4fYoYTWgp0uNB8ypgBzs7gvr/1dzAGzHUQjZMOoMORcEwcZiuQRFnz8fioqNovuCl5v8HYcXsgzJjKI+oeZl3I3dvY3Nj49QXA3kw9+OZKanFI8YcoeNzu2CecZso+hujbZzR5DJjV5+FAdZshTTE+/ePThMvr+DyZ/B/boA1b7OzIQxlE42SOdBNdU9Q4otNJQraWekFvTCU5qsosh+7DGdan2IePntKkkA/kb5gUv+m6TPfqMNq+pSP9Xl6zxm8+76ArASE0OKbrV5urVbs4a1khWLh/WfFpz9R6dT2gzlpke03ot/xtFaLGRdmtKGHJ1gHys3sByJAMx0FsOBgyAtnbCluHEHAPNOofaaXRKuM4po5xWhlmtYOTKQcvHqSBFd0m32hZK9Kzve3R0wnakCiyn4yglNa3WnFkt7VgYr7ZWD/MWh+2ZQiepwclRZThQo6SkTrbQCT11nShx/LDkmjhnxW+Hz5evsSnQzvXoNKdC8ZYqYzjtTdvRsetHM+oeW1sU3GMKOP9k1DtvU1zilKvrwlS/WPd+DeV7V9vyfPWBftsiu4v2C6aqRA4+lG2+pnw+n8//AwAA//8DADLvW0klBAAA

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRTX2/cKBD/KhbPxgGMsfFbtOnpVrk2q3bVO93bmBk3KA5eYZy2iva7V3i3anNv94LQiJn5/eOVBXgm1rPb0+ndC4W0sJIlv5WUUIYLw4U+yq5vmr6pq07VStjmX1Yyf0/fWc9a7Zx0lnhNhrhG0fChHh1HJCuFRKEanWfCl4X1rwx8hfTiHVXz8pni4ufAeva3Dzh/zbvBV26aV6ziPNE+LAmCy2A+7Hf3/K/bw/HhcHm10JKbK4+sZ6DaQdZScAFQc62BOAyq40jUUg2NAzde2nxIFANM1YJPv/bjnAIl16tK1ZXgyrJzyRASZMgDLHT8fsooNonucr3cytu1f2UvFFmvyp9iXsbduMnfPC/D6ie8uZwJlqfM8hTnE8XkadNkozlNkPwciuum3eQpJFayh0/FYYI0zvH5jVCfLvz3/4P+MW9/943cmgh38xoS65nUgpXsPbhHH6jY37GeCUkK6474qKThukPg0NSWG1ObkaSzrZKsZLs5JB/WeV2KfUj0JW4EWM/+gGmhC/Tb6B59IpfWmFn9Y3IWDnHG1aXil/5SVKKqRUbysKbTmoqPhD6SS5T5HeN6nfefFmXU1rRbY6SQigOkx+JPWB5Zzyx0ToIFC2CcG6kRNAoUYESLZNAQaoUtaqvRaCuc0TBIjdDIYcwVVjLKdhdbwtQwdtqYlrfYGa5HsNwqqTi6zqpBdSMOwxsdi4dps8Z1QhPW3NKIXFsJ3Oq65lg30Coztq3K1vyU5Oq+FFlgpJfLX9kQGMSO0EiutGy4tlbyzjYtH9pusIPpqHEZ8z3FQNNvQr33Ls7LPKbiGp7ijXQf15D/e7EF6asP/Nvm0d3snigW2WPwIcf7auv5fD7/AAAA//8DACoRxvI2BAAA

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSUT2/cNhDFv4rAsyjzP0XdDLtFDTfNIjbaorcRZxQL0UoLito0MPzdC2q3SNxbb8JAb/j4mzd8ZTMciXXs9nT66UxzXlnN8riXlFCOC8eFeZZtZ12nTKOCt176v1jNxkf6xjrmTYwyBuKaHHGDwvJeD5EjUpBColDWlJ7weWXdK4OxQTqPkZpl/Z3SOi4z69gf44zL13I2jE2clg2btEz0MK8Z5ljM/PZw98h/vT08fzxc/lppLeJmRNYxq0WQ3hNHNxhuBut4r6Xk0CuMSsoeKV5k45wpzTA1K375fj4ueaYcO9Uo3QiuAnurGUKGYrmHlZ6/nYqLHdF9qdd7ef/sXtmZEutU/S/MS7ubOI0369ZPdKbpBGmldBOX4xFmZDU7peVEKY+0YzlT6pd1zIVojB7aVkpswRvrsdVWaY9aELbCWwwYwBk3qGjtgH0bvBkMKmnQkTG9ZzXbwU0T5HGZq6v3u2mkObOafXyqDhPkYUnHd+ifLkQf/gfQYpt1TAurnfdktNYQXK8EoXfROdVHEH7oFclIwgbjg/RC6qBE0Nb51iOIUDp9gPgyzlQ93LOOCUkKdUt8UNJx0yJwsDpw57QbSMbglWQ1u1vmPM7bsq3Vw5zpc9rvyzr2M0wrXW56m+LLmCnmLRUIf7oSxkNacIu5+h4AKRrRaCGKaMunLVefCMdEMVPB8Zy2a7//SJRTu+huS4nmXB0gv1S/wPrCOhagjRICBAAX40BW0CBQgBMeyaEjNAo9mmDQmSCiM9BLg2BlP5QKqxmVvFV7xL2JAC1GbgYgbvoh8hYAOVmrowUCsvIdx+rjVHQQW2EINQ80IDdBAg9Ga47agldu8F4NPyC5hkWK0gzpfFnW3YFDbAmd5MpIy00IkrfBet77tg+9a8nG4vmR0kzTD6A+jDEt6zLk6pq16h26T9tcHpxqz93XceZ/7zO6X+IXSlWZMYxz2a/rWN9qdiRYt0TH/b3qXtmhLFf1vD9byjbBtzV7ypDydrpWrWikFO7t7e3tHwAAAP//AwBVXoe28wQAAA==

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSUXW/jKBSG/4rFtXEBY2x81013tFGn02ianV3tHYbjFNWBCHBnqyr/fYSTqumM9mLvrGPe8/GcF16RU3tAPbo+HH5/BpciKlGyS4gRJjARmPAt7fpG9IxXNRNMEPoPKpG9hRfUo5ZrTbUEXIMAzA1p8FCPGhsDkhJqCGt4zql2EfWvSNnKwLPVUPn4DUK03qEe/WWd8d9zbWUrPfnZVMFPsHYxKadzM1/Wq1v8+Xqzvd+cTkWIWVxZg3rU1ETStgVsxMgxHxuBh5pSrAZmNKN0MKBPMusSBKemKpqn9/rGJwdJ96xidUUwk+hYIqOSyi0PKsL25ZC7WBDd5Hi5hJfP/hU9Q0A9K99gntJd6cle7eMw28lcTX63s2638m60uzmolAuX6BD8AUKysMD5zToVXj773S7nQ5/UFAGVaAthb52aTj9uYFTzlB78HBYy0Tz91xnUIzUnj0q0kJympWxxHmY1WXDpF+1F4fuHYjOpNPqw/7CkhxP79f9Af6f0o3VQrG9QjwgFZuoO8MiowLwzCqumlliIWoxAtWwZRSVaeZesm/0ci7VLsDtT+9DfddCPNoFOc8gz/S2y2TbBm1mn4n3BlFSkqgnJojkd5lR8BWMD6AR5iG2Yz/l+kjDBFtEnO8EveFZzCOBSsVHpsfhDxUfUI6k6TZVUUimh9QgNgZEYogRpDQgjwHBmWsMlN4JLogVXA+VGNXQYcwSVCLLJipOvB9UK2UksuGaYg+ywNC3FDdNEd60UnWQf4Bb3U9Yp3REOpsYSRoO5pApLXtfY1I1qmRjblo0XnM6GoCRTvzThnxHM2UtfTtZ+m93A8+keL30KYzowgmLGaYO5lBR3smnx0HaDHEQHjc6TrbyL/g3kNwiDjzblR+TOOrtX089H3jdzC8HBdLGdO6uDj35MxdmWxYd9fZ1dfsWKxaLfrcP/Lsa48foJQpGNpay7WObxeDz+AAAA//8DANXWaeQMBQAA

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpSUTW/cNhCG/4rAsyjzm6Juht2irpt6ES/SojeSM7KFaKUFRW0aGPvfA2o3SNxbb8JA78zwmXfmjUz+gKQjt8fjLyec8kJqkoctJJgwlBnK1J63nTadUI3QTlvO/yE1GR7xK+mIVTHy6JBKNEgVME2D7CMFQMcZBya0Kjn9y0K6N+KHBvA0RGzm5ROmZZgn0pG/hgnmL6W2H5o4zis0aR7xYVqyn2Jp5s+Hu0f6x+1u/7S7/LXgUsTNAKQjWjLHrUUKpldU9drQIDmnPgiIgvMAGC+yYcqYJj82C3z+UR/mPGGOnWiEbBgVjpxrAj770nLwC+6/HksXG6L7Eq+38PbZvZETJtKJ+jvMS7qbOA43eT6OeMLx6NOC6SbOh4OfgNTkmOYjpjzghmV76Tj6PMxTdS12Nw44ZVKTp+dqN/rcz+nwjtXzBcHD/yBwwhRIRyTT0liLSkrpnQmCIVgTjREhemb7IJBHZNop67hlXDrBnNTGthY8cyXTyzgHP/6+bPycYKEFZoURqNAJZYyLDGWLvbbIwBsTTBsdyNYK1UIrvfOeu4A2OhOLQT74+DpMWD3ck44wjgJki7QX3FDVgqdeS0eNkaZHHp0VnNTkbp7yMK3zulQPU8aXtPEjHfnVjwteyN2m+DpkjHlNBerfphTbpRnWmKsfDuCsYY1krIjWfFxz9RFhSBgzFrz7tF7z/UcijNhEd2tKOOVq5/Nr9ZtfXgsV30buXXmqibFHzbBnwLxhFtCAQVACLCinwCjHolE+cAVe89CXCKkJFsNVm8ed61sRQqCAmlEFGqlTXlEowEPrNLP4jmP1NBadjy1TCJI67IEqxz11SkoKUnsrTG+t6H9CcjUfZwUw4OmyrVsHBqBFMJwKxTVVznHaOm1psG1wwbSoY+n5EdOE40+gPgwxzcvc5+rq3eoduo/rVC5Otfn4yzDRf7cZ3c/xM6aqzNgPU1mw61jPNTmgX9aEh+1gdW9kV7ar2m93S+jG2bYmz9mnvB6vUc0azpk5n8/nbwAAAP//AwDcoTOW9AQAAA==

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACoxUTW/bOBD9KwLPpkJSFCUK2IPhtFgjTWMk7u5ibxQ5jInIokFRToMi/31BWa6dooe9CaP5eO/NG/5AvdoDatDycPh0hD4OaIGim0KMMIGJwIRvad2UomE8ryiXTBT/ogVyd/CGGlRxramWgAsQgLkhJW4Lq7ExICmhhrCSp57qeUDND6RcbuDoNOR++AvC4HyPGvS3641/TbOVy3XnR5MH38G6H6LqdQLzdb26w1+Wm+3D5pQ1wJCKc2dQg8qCSFpVgI2wHHNbCtwWlGLVMqMZpa0BfSpzfYTQqy4fzMtlvvGxh6gblrMiJ5hJ9L5ARkWVILdqgO3bIaGYJLpN8cUUnj6bH+gIATVscRbz1O5Gd+5mP7Sj68xNVOEZog1qD68+vMBRdWiBDsEfIEQHkzYPT9kFEiU5yZlghKAFuld653rI1rfZQ5cIK10TDqbAEqzBXFKFJS8KbIpSVUzYqmIWLdAdhB66q673Tgc/eBuzWfLsw5yV76PrRz8O2bqP8BxUPNV9Vt0AaIE2Y9u5YfcIyrxt/eOY/vVjl7isxhCgj9lGxV32pxp2qEFS1ZoqqaRSQmsLJQFLDFGCVAaEEWA4M5XhkhvBJdGCq5Zyo0ra2hRBC/QwxsMYz/p/vwKxDW6/B3NBsJ003nQqWh/2awN9dNal1ZwzJkN13cQqm3uuOgd9/CAyahChwExRA7aMCsxro7AqC4mFKIQFqmXF6Ekw657HnzoRaw0vbFnXopUtJcBsVXNS2ZYYbQstTdlCxSpS1VBLTUhbclq3UhMDtOQmsQvejDr+aoViWtDj2KfrzNaJ9qvr8XfBL4psgo9e+y4tzHXw9DZE2P9uayu/P/jBxcTfzqv9NsC979Pf+f5nzebipY+XGKQ7yKbTKzSHEmyNDeEM85ZUWLFS4oIayUvKKCsSwluvXyBkyWDK9dNOzp56eMqWQe9cBB3HkCb/M5E6rfPz+WQueuRfP21XPsDycFjM0T+OSaRTs7MBPrwr34aUvvNDvKI8U3uCzp6BXdnJwPH0VLkUFMbUYATFjNMScykprmVZ4baqW9mKGkp9sWv2CMYF0HHqtw1jmjYr+ztfLkN0Vuk4pOP54vVkp9mfc8qvIK/W9rP6NPxqdbOV5k6UJMs+nR7OyUH/6918f39//w8AAP//AwAnINo2KAYAAA==

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRTTW/UMBD9K9Gc16ntOE6cW7UFsSrQFawAcXM8E2o1dVaOU0DV/nfk7KICNy6WNfKbeR/jZwj2kaCD6+Px1ROFNMMGkl9LkkvNuGZcHUTb1bqrZNnyujWq/gob8Lf0EzpolHPCGWIVaWIKec36anAMkYzgArmsVe5pv83QPYP1JdKTd1RO8yeKs58CdPDZB5y+59nWl26cFizjNNIuzMkGl8m8321v2dvr/eFuf34105zBpUfooK64EU1DDPWgmBpqzfpKCGZ7iU4K0SO5M8yHRDHYsZzx4WU+TilQcp0sZVVyJg2cNoA22Uy5tzMdfh4zi9Wim1zfrOX12j3DE0Xo5Oa3med2V270V49zv/gRr85nsvNDVnmM05Fi8rR6ssocR5v8FIrLpO3oKSTYwN3HYj/aNEzx8S+jPp717/5D/iFPf/WD3JIIt9MSEnQgVI7nnXX3PlCxu4EOuCCJVUtskEIz1aJltq4M07rSAwlnGilgA9spJB+WaZmLXUj0La4CoIPXdpzpTP06unufyKUlZlVfdB62jxMuLhUv/gte8rLiPIOWdFxS8YHQR3KJsr5DXC79/oFILVfQdomRQir2Nt0Xb+x8Dx0Y2zphjTXWaucGqjkNHLnVvEHSqAmVxAaVUaiV4U4r2wuFthb9kCuwAcpxF+uGKVJtK6VgfVNrpqqemKm4YYPG3sp+4L3JFr/4WNyNGWddyxVhxQwNyJQRlhlVVQyr2jZSD00jhz8suaQveDYY6en8V1YGGrEl1IJJJWqmjBGsNXXD+qbtTa9bql3mfEsx0PiHUe+8i9M8Dam4LE/xl3UflpD/e7Eu0ncf2I81o5vJPVAscsbWh7zel1hPp9PpFwAAAP//AwCS1lp7NgQAAA==

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACoxUy27kNhD8FYHXiDLfEnVzbC8ycPzAziAJcmuRLZuxTA0oyt6F4X8PpLHXj0WAXAShSXZVV1f3E4lwj6Qlx/v92QPGPJGS5LCGBBOGMkOZ2vGm1aaVomqYZUbYv0lJwjl+Jy2plXPcWaQSDVLlmaad7B31Hi1n3DOh1ZITbibSPhEIlceH4LAapz8wTWGMpCV/hujHxwUbQuWGcfZVGgfcxClDdAuZy83JOf39+Hp3dX24NeG0PK6CJy3Rklle10i96RVVvTa0k5xT6IR3gvPOozs8CzFjijBUk797w/djjphdKyohK0aFJc8l8ZBhodzBhLvv+4XFKtHpEi/X8PrbPpEHTKQV5auYh3RHbghH91M3h8EfrV9Skn0a95hywFWNq23xRoKzilXCCMZISS7A3YaIxea0uBqWEsE1TKGX1GLvqbIcqFVSUi811ML0dS16UpJzTBGHd1kvgkvjNPa5eBG5+ICzPT45i9ANuGB8gWFCUpLNtM0QPQxjxLNv6OZ8yLVL83J8MsYc4jzOU7GJGW8SvJy/vj+ZU8KYi2vIt8VvMN2SllhoHAcLFsA416Nm2DPPwLDao/EGvRK+9soqb5RlzijouPKgedcvEVKSXxcNt7NzOE1vbFaTDMPKoXjp08kQMOYPMpKWMI7CywZpL7ihqvFAQUtLjZGmR+5sLfgrzA7SDWbSkr7puo451wlluQNhQfqeSW96K0Rdg+qlrpUwRrManK9rwZWXNTqQvWSNICW5TqOfXf7cark24DqN/6DLi1SkJYZp3TjoOmEUb8A2qLhRjbFYW9UDU9J1wHUDXccZaOZsIxxwY0F1Xvc/VDqLNyHiaZj2A3x/h9tUppK/OICG96A+Xf+S4B4fx3R3ebBxdXm2IyX5OsdlIxSbxSKPIdJvZpnoi3nIYXebEDz6i9Hjzz7CZVyKdUJN33gNoKgA0VDVdYKC7y3VHGpkXiimJSnJ6ejuMBWLwyDEZap+JLvaFsfJ3YaMLs9p4ffXyuNqW1wPkPsx3X/YJO8K+yxAJSRnS2c8Phy20YGj9w16w6lQXFNlLaeN1TXt6qaznWlQu0Wxqznv51x8RR8SurxW/OLFFfPkFt3dz2K8uuDFopwtXtsettgq7f9aYs8luUeY5oT367Zun8gmRkwr8ul8GMVNvAjDECZ0Y/QTaRtleWW0ti8M//OeVrrSkqnn5+fnfwEAAP//AwBmsMk7GwYAAA==

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRTy27cOBD8FYFnUeZLlKibMd5FBk7Wg8RIFnuj2K0MEZkcUJSTwJh/X1AzQeLcciOKrO7qquYLCfYJyUBuT6e/njHkhdQk+w0STGjKNGXqkfdDqwcpmp71PRfdf6Qm/h6/k4F0yjnuDFKJGqkC1tJRTo4CoOGMAxOtKjXt54UML8T6BvDZO2zi8hHT4mMgA/nkA8Svpbf1jZvjCk2KM+7Dkm1wRcw/+909fXt7eHw4XF4tuBRy44EMpJXM8K5DCnpSVE2tpqPknNpRgBOcj4DuQvMhYwp2bhb48rM/xBwwu0E0QjaMCkPONQGbbZE82gUfv5+Kis2iu4LXG7wdhxfyjIkMov5h5qXcjZv9jYtPTzbAzeSDX46kJqcUT5iyx82ObcJ5ttnHUF2b7GaPIZOaPHyoDrPNU0xPrzz6cBl9/weTv7Pu6ANW+zsyEMZRgOyRToJrqnqw1LbSUK2lnpA70wlOarKLIfuwxnWp9iHj57SpJAP5284LXvTdJnf0GV1eU5H+ry5Z4zefdxEKwEhNDinC6nL1027OGtZIVi4f1nxac/UewSd0GctMj2m9lv+NIrTYSLs1JQy5Oth8rN7Y5UgGYmzvuDXWWKudm7BlODFgVrMOUINGUAI6UEaBVoY5rezIFdiWj1NBiu6SbrUtFAM3QiuBQjtJqpQY6dhJSXFkTLhWc47jK1urh7nwrOuZQpDU4ARUGW6pUVJSkK3thJ66Tky/WHJNnLPiN+Dz5WtsCjRAj6A5FYq3VBnDaW/ajo5dP5pR99i6ovkeU8D5F6PeeZfiEqdcXRememXd+zWU711ty/PVB/pti+wuui+YqhK59aFs8zXl8/l8/h8AAP//AwB+LhBGJQQAAA==

View File

@ -0,0 +1,5 @@
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip
H4sIAAAAAAAACpRUTW/cNhT8KwLPoswvUaJuxjpBF27qRbJIi94ovqeYsEwtSMqJYex/L6jdIvGp6IUgnjiP82aGeiPBPiMZyO3p9OEFQ06kJtlvJcGEpkxTpo68H1o9SNH0TEoh+d+kJv4eX8lAOuUcdwapRI1UAWvpKCdHAdBwxoGJVpWe9lsiwxuxvgF88Q6bJX3FmPwSyED+9AGW7+Vu6xs3Lys0cZlxH1K2wRUyf+x39/T328Px4XA5lTAVcOOBDKSVzPCuQwp6UlRNraaj5JzaUYATnI+A7gLzIWMMdm4SPP28H5YcMLtBNEI2jApDzjUBm22hPNqEx9dTYbFJdFfq9VbetsMbecFIBlH/K+al3Y2b/c1zGlc/w81lzTY9pW2drMtLfCU1OcXlhDF73PS5TQmfx/n1aNPTx8uZsk0ffqBbM8JuWUMmA+GKk5psAs2zzX4J1ZXjbvYY8vYxRx+Sd//VS5KaPHypDrPN0xKf3/nx5SLz/n+o/Mm6Rx+w2t+RgTCOAmSPdBJcU9WDpbaVhmot9YTcmU6UQXZLyD6sy5qqfcj4LW4jkYF8tHPCC7/b6B59RpfXWOb8S5dcHeICq8vVTy85a1gjGSugNZ/WXH1G8BFdxjLEMa7Xfl/fQ4QWG2i3xoghVwebH6vfbHokAzG2d9waa6zVzk3YMpwYMKtZB6hBIygBHSijQCvDnFZ25Apsy8epVEhNsESn2tJqGHKcXEu1bjuqUI/UmqmjBtt2ZMh6dPBOx+phLjjreqYQJDU4AVWGW2qUlBRkazuhp64T0y+SXPPAWREY8OXy7jYGGqBH0JwKxVuqjOG0N21Hx64fzah7bF3hfI8x4PyLUJ+8i0taplxdE1K9k+7zGsq/o9rS8t0H+mPz6G5xTxir4rH1oTyVq63n8/n8DwAAAP//AwCYE5thggQAAA==

View File

@ -0,0 +1 @@

View File

@ -0,0 +1 @@

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,16 @@
---
title: Plot Lines
category: Timeline
tags: plot, threads, continuity
contextKey: timeline.plotLines
micro: Track unresolved story threads across scenes and books.
quickTitle: What are Plot Lines?
quickSummary: Plot Lines help you track unanswered questions, clues and story threads across chapters and books.
screenshot: plot-lines-overview.png
---
# Overview
Plot Lines help you follow story threads across scenes, chapters, and books.
Use them to keep track of promises, clues, unresolved questions, payoffs, and continuity threads that need attention later in the story.

View File

@ -0,0 +1 @@

Binary file not shown.

View File

@ -0,0 +1,23 @@
using Microsoft.AspNetCore.Mvc;
using PlotLine.Services;
using PlotLine.ViewModels;
namespace PlotLine.Controllers;
public sealed class HelpDiagnosticsController(IHelpService help) : Controller
{
public IActionResult Index()
{
return View(new HelpDiagnosticsViewModel
{
Articles = help.GetAllArticles(),
Coverage = help.GetCoverage()
});
}
public IActionResult Article(string path)
{
var article = help.GetByRelativePath(path);
return article is null ? NotFound() : View(new HelpArticleDiagnosticViewModel { Article = article });
}
}

View File

@ -0,0 +1 @@

View File

@ -0,0 +1 @@

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,16 @@
---
title: Plot Lines
category: Timeline
tags: plot, threads, continuity
contextKey: timeline.plotLines
micro: Track unresolved story threads across scenes and books.
quickTitle: What are Plot Lines?
quickSummary: Plot Lines help you track unanswered questions, clues and story threads across chapters and books.
screenshot: plot-lines-overview.png
---
# Overview
Plot Lines help you follow story threads across scenes, chapters, and books.
Use them to keep track of promises, clues, unresolved questions, payoffs, and continuity threads that need attention later in the story.

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,25 @@
namespace PlotLine.Models;
public sealed class HelpArticle
{
public string Title { get; set; } = string.Empty;
public string Category { get; set; } = string.Empty;
public IReadOnlyList<string> Tags { get; set; } = [];
public string ContextKey { get; set; } = string.Empty;
public string Micro { get; set; } = string.Empty;
public string QuickTitle { get; set; } = string.Empty;
public string QuickSummary { get; set; } = string.Empty;
public string Screenshot { get; set; } = string.Empty;
public string MarkdownContent { get; set; } = string.Empty;
public string RelativePath { get; set; } = string.Empty;
public IReadOnlyList<string> ValidationIssues { get; set; } = [];
public bool IsValid => ValidationIssues.Count == 0;
}
public sealed class HelpCoverageItem
{
public string ContextKey { get; set; } = string.Empty;
public string FilePath { get; set; } = string.Empty;
public bool IsValid { get; set; }
public IReadOnlyList<string> ValidationIssues { get; set; } = [];
}

View File

@ -11,4 +11,11 @@
<PackageReference Include="Microsoft.Data.SqlClient" Version="7.0.1" />
</ItemGroup>
<ItemGroup>
<Content Include="Help\**\*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>
</Project>

View File

@ -63,6 +63,7 @@ public class Program
builder.Services.AddScoped<IArchiveService, ArchiveService>();
builder.Services.AddScoped<IAcceptanceService, AcceptanceService>();
builder.Services.AddScoped<IImportService, ImportService>();
builder.Services.AddSingleton<IHelpService, HelpService>();
var app = builder.Build();

View File

@ -0,0 +1,179 @@
using Microsoft.AspNetCore.Hosting;
using PlotLine.Models;
namespace PlotLine.Services;
public interface IHelpService
{
IReadOnlyList<HelpArticle> GetAllArticles();
HelpArticle? GetByContextKey(string contextKey);
HelpArticle? GetByRelativePath(string relativePath);
IReadOnlyList<HelpCoverageItem> GetCoverage();
}
public sealed class HelpService(IWebHostEnvironment environment) : IHelpService
{
private readonly object cacheLock = new();
private IReadOnlyList<HelpArticle>? cachedArticles;
public IReadOnlyList<HelpArticle> GetAllArticles()
{
if (cachedArticles is not null)
{
return cachedArticles;
}
lock (cacheLock)
{
cachedArticles ??= LoadArticles();
return cachedArticles;
}
}
public HelpArticle? GetByContextKey(string contextKey)
{
if (string.IsNullOrWhiteSpace(contextKey))
{
return null;
}
return GetAllArticles().FirstOrDefault(x => string.Equals(x.ContextKey, contextKey, StringComparison.OrdinalIgnoreCase));
}
public HelpArticle? GetByRelativePath(string relativePath)
{
if (string.IsNullOrWhiteSpace(relativePath))
{
return null;
}
var normalisedPath = relativePath.Replace('\\', '/');
return GetAllArticles().FirstOrDefault(x => string.Equals(x.RelativePath, normalisedPath, StringComparison.OrdinalIgnoreCase));
}
public IReadOnlyList<HelpCoverageItem> GetCoverage() =>
GetAllArticles()
.Select(x => new HelpCoverageItem
{
ContextKey = x.ContextKey,
FilePath = x.RelativePath,
IsValid = x.IsValid,
ValidationIssues = x.ValidationIssues
})
.OrderBy(x => x.ContextKey)
.ThenBy(x => x.FilePath)
.ToList();
private IReadOnlyList<HelpArticle> LoadArticles()
{
var articleRoot = Path.Combine(environment.ContentRootPath, "Help", "Articles");
if (!Directory.Exists(articleRoot))
{
return [];
}
return Directory
.EnumerateFiles(articleRoot, "*.md", SearchOption.AllDirectories)
.OrderBy(x => x, StringComparer.OrdinalIgnoreCase)
.Select(path => ParseArticle(articleRoot, path))
.ToList();
}
private static HelpArticle ParseArticle(string articleRoot, string filePath)
{
var rawContent = File.ReadAllText(filePath);
var relativePath = Path.GetRelativePath(articleRoot, filePath).Replace('\\', '/');
var metadata = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
var markdownContent = rawContent;
var issues = new List<string>();
if (rawContent.StartsWith("---", StringComparison.Ordinal))
{
using var reader = new StringReader(rawContent);
_ = reader.ReadLine();
var frontMatterLines = new List<string>();
string? line;
var frontMatterClosed = false;
while ((line = reader.ReadLine()) is not null)
{
if (line.Trim() == "---")
{
frontMatterClosed = true;
break;
}
frontMatterLines.Add(line);
}
if (frontMatterClosed)
{
metadata = ParseFrontMatter(frontMatterLines);
markdownContent = reader.ReadToEnd().TrimStart();
}
else
{
issues.Add("Front matter was opened but not closed.");
}
}
else
{
issues.Add("Front matter is missing.");
}
var article = new HelpArticle
{
Title = GetValue(metadata, "title"),
Category = GetValue(metadata, "category"),
Tags = SplitTags(GetValue(metadata, "tags")),
ContextKey = GetValue(metadata, "contextKey"),
Micro = GetValue(metadata, "micro"),
QuickTitle = GetValue(metadata, "quickTitle"),
QuickSummary = GetValue(metadata, "quickSummary"),
Screenshot = GetValue(metadata, "screenshot"),
MarkdownContent = markdownContent,
RelativePath = relativePath
};
issues.AddRange(Validate(article));
article.ValidationIssues = issues;
return article;
}
private static Dictionary<string, string> ParseFrontMatter(IEnumerable<string> lines)
{
var metadata = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (var line in lines)
{
var separatorIndex = line.IndexOf(':');
if (separatorIndex <= 0)
{
continue;
}
var key = line[..separatorIndex].Trim();
var value = line[(separatorIndex + 1)..].Trim().Trim('"');
metadata[key] = value;
}
return metadata;
}
private static IReadOnlyList<string> Validate(HelpArticle article)
{
var issues = new List<string>();
if (string.IsNullOrWhiteSpace(article.Title)) issues.Add("Title is required.");
if (string.IsNullOrWhiteSpace(article.ContextKey)) issues.Add("ContextKey is required.");
if (string.IsNullOrWhiteSpace(article.QuickTitle)) issues.Add("QuickTitle is required.");
if (string.IsNullOrWhiteSpace(article.QuickSummary)) issues.Add("QuickSummary is required.");
return issues;
}
private static string GetValue(IReadOnlyDictionary<string, string> metadata, string key) =>
metadata.TryGetValue(key, out var value) ? value : string.Empty;
private static IReadOnlyList<string> SplitTags(string tags) =>
string.IsNullOrWhiteSpace(tags)
? []
: tags.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
}

View File

@ -0,0 +1,17 @@
using PlotLine.Models;
namespace PlotLine.ViewModels;
public sealed class HelpDiagnosticsViewModel
{
public IReadOnlyList<HelpArticle> Articles { get; set; } = [];
public IReadOnlyList<HelpCoverageItem> Coverage { get; set; } = [];
public int TotalArticles => Articles.Count;
public int TotalValidArticles => Articles.Count(x => x.IsValid);
public int TotalInvalidArticles => Articles.Count(x => !x.IsValid);
}
public sealed class HelpArticleDiagnosticViewModel
{
public HelpArticle Article { get; set; } = new();
}

View File

@ -0,0 +1,52 @@
@model HelpArticleDiagnosticViewModel
@{
ViewData["Title"] = $"Help Article - {Model.Article.ContextKey}";
}
<nav class="breadcrumb-trail" aria-label="Breadcrumb">
<a asp-action="Index">Help Diagnostics</a>
<span>@Model.Article.ContextKey</span>
</nav>
<div class="page-heading">
<div>
<p class="eyebrow">Parsed help article</p>
<h1>@Model.Article.Title</h1>
<p class="lead-text">@Model.Article.RelativePath</p>
</div>
<a class="btn btn-outline-secondary" asp-action="Index">Back to diagnostics</a>
</div>
@if (!Model.Article.IsValid)
{
<div class="alert alert-warning">
<strong>Validation issues:</strong>
@string.Join("; ", Model.Article.ValidationIssues)
</div>
}
<section class="asset-detail-grid">
<article class="plain-card">
<h2>Metadata</h2>
<dl class="row">
<dt class="col-sm-3">Context key</dt>
<dd class="col-sm-9">@Model.Article.ContextKey</dd>
<dt class="col-sm-3">Category</dt>
<dd class="col-sm-9">@Model.Article.Category</dd>
<dt class="col-sm-3">Tags</dt>
<dd class="col-sm-9">@string.Join(", ", Model.Article.Tags)</dd>
<dt class="col-sm-3">Micro</dt>
<dd class="col-sm-9">@Model.Article.Micro</dd>
<dt class="col-sm-3">Quick title</dt>
<dd class="col-sm-9">@Model.Article.QuickTitle</dd>
<dt class="col-sm-3">Quick summary</dt>
<dd class="col-sm-9">@Model.Article.QuickSummary</dd>
<dt class="col-sm-3">Screenshot</dt>
<dd class="col-sm-9">@Model.Article.Screenshot</dd>
</dl>
</article>
<article class="plain-card">
<h2>Markdown body</h2>
<pre class="mb-0 text-wrap">@Model.Article.MarkdownContent</pre>
</article>
</section>

View File

@ -0,0 +1,80 @@
@model HelpDiagnosticsViewModel
@{
ViewData["Title"] = "Help Diagnostics";
}
<div class="page-heading">
<div>
<p class="eyebrow">Developer diagnostics</p>
<h1>Help Diagnostics</h1>
<p class="lead-text">Markdown article discovery and front matter validation.</p>
</div>
</div>
<section class="row g-3 mb-4">
<div class="col-md-4">
<div class="plain-card">
<p class="eyebrow">Articles found</p>
<p class="writer-kpi">@Model.TotalArticles</p>
</div>
</div>
<div class="col-md-4">
<div class="plain-card">
<p class="eyebrow">Valid</p>
<p class="writer-kpi">@Model.TotalValidArticles</p>
</div>
</div>
<div class="col-md-4">
<div class="plain-card">
<p class="eyebrow">Invalid</p>
<p class="writer-kpi">@Model.TotalInvalidArticles</p>
</div>
</div>
</section>
<section class="list-section">
<h2>Coverage</h2>
@if (!Model.Articles.Any())
{
<p class="muted">No help articles were found.</p>
}
else
{
<div class="table-responsive">
<table class="table align-middle">
<thead>
<tr>
<th>Context key</th>
<th>Title</th>
<th>Category</th>
<th>Status</th>
<th>Validation issues</th>
</tr>
</thead>
<tbody>
@foreach (var article in Model.Articles.OrderBy(x => x.ContextKey).ThenBy(x => x.RelativePath))
{
<tr>
<td>
<a asp-action="Article" asp-route-path="@article.RelativePath">@article.ContextKey</a>
</td>
<td>@article.Title</td>
<td>@article.Category</td>
<td>
@if (article.IsValid)
{
<span class="badge text-bg-success">Valid</span>
}
else
{
<span class="badge text-bg-warning">Invalid</span>
}
</td>
<td>@string.Join("; ", article.ValidationIssues)</td>
</tr>
}
</tbody>
</table>
</div>
}
</section>