@crvouga/atlas 0.10.2 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +7 -0
  3. package/dist/server/src/settings-page.d.ts +4 -4
  4. package/dist/server/src/settings-page.js +17 -78
  5. package/docs/reference/server.md +4 -4
  6. package/docs/ui.md +142 -0
  7. package/docs/workspaces.md +130 -0
  8. package/llms-full.txt +278 -0
  9. package/llms.txt +2 -0
  10. package/package.json +17 -10
  11. package/packages/visualizer/src/admin/SettingsPage.tsx +456 -0
  12. package/packages/visualizer/src/admin/main.tsx +11 -0
  13. package/packages/visualizer/src/app/AppShell.tsx +62 -38
  14. package/packages/visualizer/src/app/gazetteer.tsx +90 -83
  15. package/packages/visualizer/src/app/router.tsx +33 -15
  16. package/packages/visualizer/src/components/ActivityIndicator.tsx +4 -5
  17. package/packages/visualizer/src/components/DataIssues.tsx +36 -54
  18. package/packages/visualizer/src/components/Header.tsx +197 -125
  19. package/packages/visualizer/src/components/Legend.tsx +13 -41
  20. package/packages/visualizer/src/components/ProjectPicker.tsx +61 -26
  21. package/packages/visualizer/src/components/Screen.tsx +80 -71
  22. package/packages/visualizer/src/components/SpecLogo.tsx +15 -0
  23. package/packages/visualizer/src/components/Status.tsx +20 -34
  24. package/packages/visualizer/src/components/labels.tsx +8 -3
  25. package/packages/visualizer/src/components/spec-logos/APACHE-2.0.txt +206 -0
  26. package/packages/visualizer/src/components/spec-logos/CAMUNDA-MIT.txt +21 -0
  27. package/packages/visualizer/src/components/spec-logos/FILE-ICONS-ISC.txt +13 -0
  28. package/packages/visualizer/src/components/spec-logos/README.md +23 -0
  29. package/packages/visualizer/src/components/spec-logos/SIMPLE-ICONS-CC0.txt +30 -0
  30. package/packages/visualizer/src/components/spec-logos/alloy.svg +1 -0
  31. package/packages/visualizer/src/components/spec-logos/dmn.svg +3 -0
  32. package/packages/visualizer/src/components/spec-logos/gherkin.svg +1 -0
  33. package/packages/visualizer/src/components/spec-logos/openslo.svg +63 -0
  34. package/packages/visualizer/src/components/spec-logos/quint.svg +1 -0
  35. package/packages/visualizer/src/evidence/artifacts.tsx +263 -110
  36. package/packages/visualizer/src/evidence/chart-source.tsx +18 -0
  37. package/packages/visualizer/src/evidence/check-detail.tsx +283 -139
  38. package/packages/visualizer/src/evidence/decision-table.tsx +35 -26
  39. package/packages/visualizer/src/evidence/document-page.tsx +51 -29
  40. package/packages/visualizer/src/evidence/feature-page.tsx +121 -53
  41. package/packages/visualizer/src/evidence/marks.tsx +112 -79
  42. package/packages/visualizer/src/evidence/model-page.tsx +77 -46
  43. package/packages/visualizer/src/evidence/model-trace.tsx +55 -35
  44. package/packages/visualizer/src/evidence/overview.tsx +137 -69
  45. package/packages/visualizer/src/evidence/recording-page.tsx +70 -31
  46. package/packages/visualizer/src/evidence/recording.tsx +135 -68
  47. package/packages/visualizer/src/evidence/snapshot.tsx +99 -43
  48. package/packages/visualizer/src/evidence/source-text.tsx +33 -0
  49. package/packages/visualizer/src/evidence/sources-panel.tsx +138 -144
  50. package/packages/visualizer/src/evidence/spec-header.tsx +52 -36
  51. package/packages/visualizer/src/evidence/spec-index.tsx +61 -32
  52. package/packages/visualizer/src/evidence/spec-page.tsx +56 -25
  53. package/packages/visualizer/src/evidence/test-filter.tsx +38 -14
  54. package/packages/visualizer/src/explorer/Explorer.tsx +688 -261
  55. package/packages/visualizer/src/explorer/ExplorerIndex.tsx +136 -68
  56. package/packages/visualizer/src/explorer/RunsView.tsx +307 -94
  57. package/packages/visualizer/src/explorer/TestDetail.tsx +393 -118
  58. package/packages/visualizer/src/explorer/parts.tsx +35 -13
  59. package/packages/visualizer/src/find/Finder.tsx +106 -90
  60. package/packages/visualizer/src/find/open.ts +17 -20
  61. package/packages/visualizer/src/live/test-progress.tsx +60 -22
  62. package/packages/visualizer/src/looks/Stage.tsx +223 -110
  63. package/packages/visualizer/src/main.tsx +9 -11
  64. package/packages/visualizer/src/map/ChartMap.tsx +125 -69
  65. package/packages/visualizer/src/map/EventEdge.tsx +59 -37
  66. package/packages/visualizer/src/map/map.module.css +12 -546
  67. package/packages/visualizer/src/map/nodes.tsx +124 -70
  68. package/packages/visualizer/src/reachable.test.ts +3 -2
  69. package/packages/visualizer/src/spec-kind.ts +2 -2
  70. package/packages/visualizer/src/ui/grammars/ALLOY-MIT.txt +21 -0
  71. package/packages/visualizer/src/ui/grammars/QUINT-APACHE-2.0.txt +206 -0
  72. package/packages/visualizer/src/ui/grammars/README.md +14 -0
  73. package/packages/visualizer/src/ui/grammars/alloy.json +71 -0
  74. package/packages/visualizer/src/ui/grammars/quint.json +152 -0
  75. package/packages/visualizer/src/ui/highlight.ts +50 -0
  76. package/packages/visualizer/src/ui/theme.tsx +109 -0
  77. package/packages/visualizer/src/views/ChartPage.tsx +46 -26
  78. package/packages/visualizer/src/views/ChartTests.tsx +59 -22
  79. package/packages/visualizer/src/views/ProductPage.tsx +16 -15
  80. package/packages/visualizer/src/views/SidePanel.tsx +241 -131
  81. package/packages/visualizer/src/app/gazetteer.module.css +0 -107
  82. package/packages/visualizer/src/components/ActivityIndicator.module.css +0 -4
  83. package/packages/visualizer/src/components/DataIssues.module.css +0 -136
  84. package/packages/visualizer/src/components/Header.module.css +0 -114
  85. package/packages/visualizer/src/components/Legend.module.css +0 -84
  86. package/packages/visualizer/src/components/Screen.module.css +0 -312
  87. package/packages/visualizer/src/components/Status.module.css +0 -116
  88. package/packages/visualizer/src/components/icons.tsx +0 -156
  89. package/packages/visualizer/src/evidence/artifacts.module.css +0 -213
  90. package/packages/visualizer/src/evidence/evidence.module.css +0 -907
  91. package/packages/visualizer/src/evidence/recording.module.css +0 -460
  92. package/packages/visualizer/src/evidence/sources.module.css +0 -168
  93. package/packages/visualizer/src/evidence/spec-snapshot.tsx +0 -26
  94. package/packages/visualizer/src/explorer/explorer.module.css +0 -1238
  95. package/packages/visualizer/src/find/Finder.module.css +0 -168
  96. package/packages/visualizer/src/looks/stage.module.css +0 -300
  97. package/packages/visualizer/src/styles/global.css +0 -105
  98. package/packages/visualizer/src/styles/tokens.css +0 -80
  99. package/packages/visualizer/src/views/views.module.css +0 -546
@@ -0,0 +1 @@
1
+ <svg viewBox="0 0 512 512" version="1.1" xmlns="http://www.w3.org/2000/svg" width="512px" height="512px"><path d="M505.9067383,391.3167419c-1.9882202-1.2072144-4.0441284-2.4555664-4.4449768-2.7857971c-2.7047119-2.2290649-5.3686829-4.631134-7.9449463-6.954071c-3.5515137-3.202301-7.2239075-6.5136414-11.1118469-9.4763184c-1.2219543-0.9310303-2.4984741-1.8641357-3.7954407-2.6467285c10.8281555-6.5928955,21.0152893-11.00177,31.0691223-13.4526672c0.7668457-0.1868896,1.5579529-0.3283386,2.3213501-0.4414368v36.8015747c-1.4026794,0.0848083-2.6984253,0.1762695-3.9220276,0.2768555C507.3363953,392.1847839,506.6029663,391.739502,505.9067383,391.3167419z M334.6712341,368.9454346c0.0527954,0.238739,0.0791321,0.4535828,0.0915833,0.6474609c1.5794678,1.552002,3.085968,2.8637695,4.5900269,3.997467c0.5892029,0.3833618,1.1938171,0.7979736,1.7983704,1.2331238c1.6985474,1.131012,3.114502,1.9319153,4.4543152,2.5195923c2.3634033,1.0365295,4.4266968,1.5449219,6.3057556,1.5536499c-0.9069214-21.1407166-1.0591736-46.5664978-1.2201538-73.4483643c-0.2966614-49.5463562-0.6329346-105.7034607-5.954895-148.6080475c-6.01828-48.5173798-17.2938232-72.1024704-34.4709473-72.1030884c-1.7828979-0.000061-3.6660767,0.2585373-5.5970764,0.7685394c-25.2353821,6.6658325-58.4373016,10.3368149-93.4897308,10.3368149c-30.3443756,0-75.8174133-2.8103485-120.1284485-16.1987991C49.5760689,67.1125336,18.9527874,48.2956772,0,23.7037697l0.0000614,268.9431458c4.6186762-0.2954407,9.5692539-0.4340515,15.4223356-0.4340515c4.9925718,0,9.9637766,0.1021423,14.7712431,0.2009277c1.3553543,0.027832,2.7019901,0.0555725,4.038496,0.0807495c10.3425217,0.1950378,20.9237442,0.6860352,31.4640846,1.4597778c8.4638367-10.2627563,25.273941-24.5184021,57.6892166-35.4081421c16.0383759-5.3879547,32.3448486-8.1199036,48.4665375-8.1199036c12.0640411,0,23.9324646,1.5531006,35.2755432,4.6160889c37.770752,10.1995239,70.5679779,36.0386963,103.1838837,81.2988281c3.1065063,1.293396,6.3551636,4.48703,8.6299133,7.0718079c-0.6476746-2.3256836-1.2742615-4.524292-1.8992615-6.6658936c-6.65448-22.8026123-14.6377563-43.8547363-23.7279053-62.5716248c-15.2195435-31.3377991-32.0744019-54.4687653-51.5276794-70.7145844c-11.1168976-9.2838898-33.3225098-24.8586121-57.1380768-24.8596497c-1.7100372-0.000061-3.4225769,0.082077-5.0900116,0.2440491c-0.327713,0.037262-0.678833,0.0574036-1.0198212,0.0574036c-4.867691,0-7.6378937-3.9600983-7.8586731-7.871994c-0.2095337-3.7145844,1.9100647-7.9781494,7.0592499-8.5610962c2.0975494-0.1867523,4.1960907-0.2795258,6.2736969-0.2795258c17.9443207,0,36.2525177,6.7342224,54.4160156,20.0157166c18.4157562,13.4660492,32.1970062,30.3768463,40.515152,42.1921082c15.5446472,22.0800629,28.1781616,48.6380157,39.7585449,83.5795746C325.6146851,328.8337402,330.5695496,350.383606,334.6712341,368.9454346z M232.8213959,499.6643066c-0.7213287,0-1.4414215,0.0063171-2.1629333,0.0190125c-2.8770905,0.0505981-5.9026947,0.23172-9.4755096,0.5683899c-3.0326538,3.8323364-5.9365082,7.7721863-8.6596832,11.748291h74.8784332c-0.6366577-0.3006592-1.2580872-0.587677-1.8710022-0.8642578C268.9049072,503.631012,250.6782837,499.6643066,232.8213959,499.6643066z M391.27948,428.6816101c6.0384216,0,10.5497131-7.0609131,13.843811-12.2165527l0.482605-0.754303c2.0703735-3.2259216,4.0874329-6.5734253,6.038269-9.8106384c2.5160217-4.1755981,5.1177979-8.4933167,7.8818054-12.6326904c14.1216125-21.1487427,23.6819153-24.0001831,31.1713562-24.0001831c1.5512695,0,3.132843,0.1671753,4.7120056,0.4974976C474.1539307,354.1101379,492.6838684,344.1481934,512,339.3430786v-16.0788269c-1.6700745-0.8259583-3.2780457-1.7054443-4.8405457-2.5599365c-4.083252-2.2331848-7.940033-4.3424683-12.6175842-5.4407349c-3.2544861-0.7641296-6.5870056-1.1516724-9.9049683-1.1516724c-20.2616577,0-38.2507935,14.4998169-49.7738647,26.6635742c-14.3085938,15.104248-26.8860474,34.3274841-39.5820923,60.4964294l-0.023407,0.0480652c-3.7885437,7.80896-7.7018433,15.874939-10.9719238,24.1673889c1.5927734,1.5735474,3.1387939,2.4897461,4.9230652,2.9328918C389.9066772,428.593689,390.6033936,428.6816101,391.27948,428.6816101z M498.2315063,490.2285767c-13.388916,5.6862183-27.5002441,12.829071-43.0007324,21.7714233H512v-26.5100098C507.0099487,486.8546143,502.5917969,488.376709,498.2315063,490.2285767z M425.3007812,499.4590454l0.2105103-0.001709c1.5509033-0.0248108,4.0146179-0.8549805,6.6229858-1.7339172c3.3512878-1.129364,7.1496277-2.4093323,10.3510437-2.4093323c2.3826294,0,4.2497559,0.7137756,5.5494385,2.121521c1.4094849,1.5264587,1.6169434,3.3591614,1.4503784,5.1261597c9.816925-6.5220337,19.519165-12.5127869,28.8831787-17.833313C489.9494019,478.1482544,500.508606,472.3953247,512,468.8170471v-16.8721313c-0.1316223-0.0272522-0.262207-0.0543213-0.3916931-0.0812073c-0.8528442-0.1771851-1.6584778-0.3444824-2.3416748-0.4498596c-7.7167053-1.1896362-15.8641357-1.7928467-24.2159729-1.7928467c-7.2407227,0-14.8301392,0.4534607-22.5574036,1.3478699c-10.2573853,1.1871338-20.8169861,3.2503662-31.3879089,6.1326599c-14.4362183,8.6638184-11.4327087,33.0924072-9.9276123,41.4022217C422.2686157,499.1379395,423.6523132,499.4590454,425.3007812,499.4590454z M512,305.5908508V0h-77.7280884c-11.6720886,7.4448676-38.1645203,27.3103504-41.3017578,55.7893524c-2.2252502,20.1995316-8.6766357,269.3074951-10.6762085,355.6675415c0.6743774-1.5868225,1.3470154-3.1384583,2.0351868-4.6792908c10.5391235-23.5948486,21.3808899-42.9328918,33.1449585-59.1192932c21.4612122-29.5282288,38.2827148-38.4454041,49.0686951-42.5840454c6.9026489-2.6484985,14.0892029-3.9914551,21.3600769-3.9914551C495.7207642,301.0828247,503.6248779,302.5596619,512,305.5908508z M401.1506653,459.5325317c3.6091614-1.8826599,7.4124451-3.7037659,11.6066284-5.5572815c8.4682617-16.1976318,20.9973755-30.7404785,35.3268738-40.993988c11.2789917-8.0707703,23.9174194-13.662323,38.6065979-17.076355c0.3656006-4.1283875-0.5410156-7.4679565-2.700592-9.9382935c-2.4505005-2.8031921-6.4874573-4.3469543-11.3674927-4.3469543c-3.0109558,0-6.2440491,0.6000061-9.3497925,1.735199c-3.0959473,1.1316223-6.2703857,2.5321045-9.6998291,4.2796021c-20.5230103,17.1070557-39.6418762,38.1210022-56.8291321,62.4608765c-0.9249878,2.4291687-2.5976257,6.8633423-1.3722534,8.6340942c0.2004089,0.2896729,0.8099976,1.1706238,2.9428406,1.1706238C399.1213684,459.9000549,400.0508423,459.7796631,401.1506653,459.5325317z M512,435.8431396v-27.0612183c-0.6609802-0.0187683-1.3134155-0.0545654-1.9509583-0.0895691c-0.8413391-0.0461731-1.6360474-0.0898132-2.368103-0.0898132l-0.1881104,0.0010376c-15.0732422,0.166687-29.0137634,3.09552-41.434021,8.7050476c-14.3737488,6.4918518-27.7913818,17.2885132-38.1764526,30.6508484c23.3139343-8.1933289,48.1202087-12.3441162,73.8115234-12.3442383C505.1119995,435.6152344,508.571228,435.691803,512,435.8431396z M218.1866455,482.7236023c-18.8322754-4.7961121-34.8102264-7.1305237-48.7727203-7.1305237c-12.0540314,0-22.8657227,1.7673035-32.1347656,5.2528687c-1.0392303,0.4208069-2.1173248,0.6409607-3.1698151,0.6409607c-4.3018188,0-7.1706772-3.580719-7.781311-7.1177368c-0.7022324-4.0677185,1.3449783-7.5860901,5.2154999-8.9634094c9.583374-3.2192383,20.4962463-4.8471375,32.4608307-4.8471375c16.2374725,0,34.9786224,3.0162048,55.7027283,8.9648438c3.0140533,0.8651733,6.2578583,1.8719177,9.8934174,3.0707092c14.412323-11.5157166,28.5320282-18.34552,41.9784393-20.3037109c4.898407-0.7583008,17.2876892-3.117218,23.1791077-5.8934937c-4.875824-2.3903503-9.7303772-4.4091187-14.4644165-6.0137939c-14.416687-4.8869324-28.766449-7.408844-42.65065-7.4956665c-0.2998505-0.0018921-0.6005096-0.0028076-0.9005432-0.0028076c-15.528183-0.0001221-30.6141357,2.4609985-44.8387146,7.3150024c-1.2014313,0.4100647-2.6768188,1.1234741-4.2389526,1.8787537c-3.3673859,1.6283875-7.1839905,3.4739075-10.5986633,3.4739075c-0.6139374,0-1.2048645-0.0604858-1.7563171-0.1798096c-3.5195312-0.7618103-6.0051575-3.6321411-6.3323364-7.3123779c-0.3275909-3.6847534,1.6186829-6.9539185,4.958374-8.3286743c15.0740509-6.3076477,32.590683-9.3754883,53.5428009-9.3754883c6.6641693,0,13.5708466,0.3052673,21.114563,0.9332275c6.4182129,0.5341797,12.0974426,0.8049927,16.8800354,0.8049927c16.4429016,0,17.3446045-3.1721497,17.3748779-3.3072205c0.1922913-0.855835-1.4028931-3.019043-6.5854797-5.4711914l-0.059967-0.0283813l-0.0573425-0.0335693c-0.2958069-0.1733398-0.6802063-0.3950806-1.0291748-0.5733032c-9.4683228-4.8323975-19.4544983-8.7861023-29.6805573-11.7512512c-12.3604431-3.5838013-24.7378387-5.4011536-36.78862-5.4014587c-9.2161713-0.0001831-18.2914886,1.0728149-26.9739685,3.1893005c-9.4209747,2.2964783-18.8284607,5.9985657-27.9640808,11.0041504c-14.5600433,16.9994507-27.995575,39.1376648-39.9385376,65.8077393C107.3662186,489.8301697,103.1642303,501.1779175,100.2788544,512h43.2884369c19.9890747-13.483551,44.7906952-22.2330627,69.9508209-24.6784058L218.1866455,482.7236023z M293.5035706,462.1083069c-4.7232971-2.8439331-9.5757141-3.3801575-14.1224976-3.5219116c-2.2225647,0.2644043-4.0562439,0.5905457-5.7622681,1.0246582c-10.1026611,2.5702515-20.2217712,8.3619385-30.8748779,17.6808777c17.8566284,6.8173523,36.0757446,15.5748901,58.4962463,28.110199c1.5149841-4.2584534,2.7310181-8.9302979,3.6211853-13.916748C304.8018188,479.1054688,303.8786926,468.3557739,293.5035706,462.1083069z M198.6313171,504.3270569c-7.385498,1.9719238-14.6631165,4.5405273-21.7407227,7.6729431h16.1026459c0.2182617-0.4779663,0.474472-0.9147949,0.7730865-1.3196106C195.3208466,508.575592,196.9489136,506.449646,198.6313171,504.3270569z M116.9049606,438.7043762c-11.4611359,12.0882263-22.0529251,26.4790039-31.5902481,42.9377441C79.2779312,492.059845,74.3686142,502.7828369,70.3426437,512H83.451561c4.9470749-20.57724,15.0034943-40.5792542,24.229126-57.4843445C110.6285629,449.1136475,113.7129211,443.8276672,116.9049606,438.7043762z M274.1224976,376.0948486c-4.9735413-2.2328491-9.552063-3.9964294-13.9944153-5.3908386c-7.8893738-2.4761658-17.1880035-3.8398743-26.1828461-3.8398743c-3.1059113,0-6.1356201,0.1627502-9.0049133,0.4836731c-6.621994,0.7409058-14.7272797,2.662262-20.6491852,4.8950195c-9.8444824,3.711792-19.421051,9.1605835-28.544693,16.2337952c8.8646851-2.1094055,18.0254059-3.1759644,27.3220673-3.1759644c24.3227539,0,49.7184448,6.9590454,77.6342163,21.2741089c2.9433899,0.9047241,5.5387878,1.3634033,7.7162781,1.3634033c2.5661926,0,4.5647888-0.6400452,5.940155-1.9021912c1.0259094-0.9413757,1.657959-2.0238953,2.1598816-2.9320984C291.1136475,391.3270569,283.581665,382.2431335,274.1224976,376.0948486z M146.3314056,399.8283997l0.6604919-0.7109375c1.0017242-1.0799561,2.0375671-2.1967163,3.0969391-3.2725525c19.1668091-19.4646301,39.5576477-31.7660217,60.6051025-36.5626221c0.2848816-0.0822754,0.5389862-0.1644592,0.7644958-0.2446594c-0.0642853-0.0079651-0.1308899-0.0157776-0.1999054-0.0233765c-10.5660553-1.1651611-19.8023682-1.7315369-28.2368622-1.7315369c-1.6999512,0-3.3873291,0.0237732-5.0153503,0.0707397c-43.7891693,1.2623901-83.9879608,16.3554077-113.1914825,42.4987183C35.4439468,426.1454468,15.0298996,461.8154602,0.6600565,512h52.7514114c-0.0608406-1.1967773,0.142189-2.3753662,0.6061516-3.4794922c9.5460472-22.7148132,22.4001732-44.2469177,37.1728859-62.2686157C107.6790924,426.1370239,126.2296143,410.5193481,146.3314056,399.8283997z M302.2546082,352.1455078c0.4702148-2.4465942,0.5567932-5.0545959,0.5195923-8.2078857c-0.0037537-0.3177185-0.019104-0.6517639-0.0344543-0.9940491c-0.0210571-0.4679565-0.0426025-0.9475708-0.041626-1.4370117c-6.1270752-8.7380066-12.7108765-17.1125793-19.5745239-24.8985596c-14.5324097-16.4848938-30.8231964-29.6563721-48.4198303-39.1487732c-18.214325-9.8256226-38.9366302-15.019165-59.9267578-15.019165c-25.5462189,0-50.7083054,7.4155884-72.7660217,21.4451294c-5.4580612,3.4715576-10.5303268,7.4133606-15.4297028,11.9959412c7.4983444,0.835907,14.8109055,1.8021851,21.781311,2.8786926c24.3635254,3.7625122,45.0170746,8.4888306,63.1408386,14.4489136c22.4605865,7.3861694,41.4681396,16.7114563,58.1087341,28.5085449c1.1655273,0.7752991,11.3788147,4.7902527,19.2092285,4.7902527c1.5245667,0,2.8677673-0.1549988,3.9922638-0.4608154c6.0761414-1.6521912,14.0704956-3.5487976,22.0229187-3.5487976c8.164032,0,15.4377441,1.9484253,22.2367249,5.9565735C298.8320923,349.4915161,300.5151672,350.6884766,302.2546082,352.1455078z M92.4402618,365.7033997c17.4549408-8.5953979,36.2481537-14.4567261,55.8576813-17.4211121c10.251236-1.54953,20.9550629-2.3352661,31.8139191-2.3353271c3.1395569,0,6.3416595,0.065979,9.5173798,0.1960754c5.9248505,0.2427673,11.6092224,0.7050781,16.8952484,1.3741455c0.684494,0.0867004,1.3527222,0.1305847,1.9861908,0.1305847c3.1363525,0,5.4457855-1.0815735,5.8834076-2.7555237c0.3993225-1.5275574-0.7566833-4.0481873-5.1989746-6.5413818c-10.9342499-6.1367798-22.9043427-10.2626343-34.2110748-13.8070374c-23.9891357-7.52005-50.9696732-12.5457153-82.4832993-15.3641968c-5.7657242-0.5156555-11.7086258-0.95578-17.6792755-1.3093567c-1.9574509,3.116272-5.9399567,8.6774902-9.4254074,8.6774902c-0.9333878,0-1.7492638-0.3783569-2.3594055-1.0941772c-1.1761398-1.3797302-1.5022697-3.9440613-1.0031319-8.0057678c-7.8656654-0.3160706-15.0199509-0.4699097-21.8223839-0.4699097c-8.9654331,0-17.6211929,0.2662048-26.46175,0.8139038c-5.0504665,0.3127441-9.5640965,0.6854858-13.7493248,1.1359863v158.1146851c11.9763632-27.1571045,26.6814842-49.2485962,44.8174133-67.364563C58.6049843,385.9055786,74.6275787,374.474823,92.4402618,365.7033997z"/></svg>
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
2
+ <path d="M4.99929,2.99597 C3.895,2.99597 2.99968,3.89098 2.9993,4.99527 L2.99435987,19.0076 C2.99397,20.1124 3.88951,21.0083 4.99435987,21.0083 L18.9944,21.0083 C20.0987,21.0083 20.994,20.1133 20.9944,19.009 L20.9993001,4.99667 C20.9997,3.89183 20.1041,2.99597 18.9993,2.99597 L4.99929,2.99597 Z M11,11 L18.9937,11.0081 L18.9937,14.0041 L11,13.996 L11,11 Z M11,5.0041 L18.9937,5.01488526 L18.9937,9.0041 L11,8.99331474 L11,5.0041 Z M5,5.0041 L9,5.01488526 L9,9.0041 L5,8.99331474 L5,5.0041 Z M8.97665,13.996 L4.99365,14 L4.99365,11.0041 L8.97665,11 L8.97665,13.996 Z M4.99365,16 L8.97665,15.996 L8.97665,19 L4.99365,19.0042 L4.99365,16 Z M11,19.0042 L11,15.9919 L18.9937,16 L18.9937,19.0083 L11,19.0042 Z" fill="currentColor" ></path>
3
+ </svg>
@@ -0,0 +1 @@
1
+ <svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Cucumber</title><path d="M12.16 0C7.523-.03 3.223 3.007 1.886 7.715.241 13.51 3.746 19.51 9.601 20.925V24l.626-.094c6.715-1.008 11.876-6.254 12.271-12.472.258-4.041-1.795-8.064-5.108-10.01a9.62 9.62 0 0 0-1.999-.895A10.606 10.606 0 0 0 12.16 0zm-.084 1.087h.013c1.011 0 2.015.161 2.976.477a8.511 8.511 0 0 1 1.769.792c2.97 1.744 4.809 5.363 4.576 9.008-.35 5.52-4.816 10.208-10.72 11.36V20.04l-.44-.085a9.518 9.518 0 0 1 1.826-18.868zm2.042 4.306a.958.958 0 0 0-.843.333 3.83 3.83 0 0 0-.366.632c-.321.743-.436 1.803.043 2.385l-.001.003a3.08 3.08 0 0 0 1.918-1.641 1.68 1.68 0 0 0 .165-.698.996.996 0 0 0-.916-1.014zm-4.061.092a1.014 1.014 0 0 0-.926.982c.01.241.07.478.18.694a3.282 3.282 0 0 0 2.062 1.6c.413-.593.216-1.65-.118-2.386a2.17 2.17 0 0 0-.377-.574.934.934 0 0 0-.821-.316zM7.628 8c-.858.035-1.2 1.126-.516 1.645.198.146.41.273.632.38a3 3 0 0 0 2.53-.07A3.17 3.17 0 0 0 8.54 8.167 1.993 1.993 0 0 0 7.628 8zm7.762 3.216a3.32 3.32 0 0 0-1.228.296 3.087 3.087 0 0 0 1.718 1.755c.236.105.49.161.747.166a.933.933 0 0 0 .681-1.663c-.659-.46-1.105-.575-1.918-.554zm-5.85.03c-.297-.012-.635.01-.983.026-.559 0-1.228.278-1.494.565-.695.61-.188 1.751.73 1.644a1.71 1.71 0 0 0 .747-.167c.744-.322 1.628-1.202 1.734-1.89-.182-.12-.438-.165-.735-.178zm2.086 1.214c-.863.32-1.56.975-1.93 1.817a1.601 1.601 0 0 0-.167.681c-.02.908 1.091 1.361 1.711.698.14-.199.262-.41.362-.632.322-.69.449-1.928.024-2.564zm1.497.173a2.792 2.792 0 0 0-.116 2.442c.089.211.21.408.362.58.577.627 1.81.186 1.763-.63a1.62 1.62 0 0 0-.167-.697 3.043 3.043 0 0 0-1.842-1.695z"/></svg>
@@ -0,0 +1,63 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <svg
3
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
4
+ xmlns:cc="http://creativecommons.org/ns#"
5
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
6
+ xmlns:svg="http://www.w3.org/2000/svg"
7
+ xmlns="http://www.w3.org/2000/svg"
8
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
9
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
10
+ width="28mm"
11
+ height="28mm"
12
+ viewBox="0 0 28 28"
13
+ version="1.1"
14
+ id="svg146"
15
+ inkscape:version="1.0.2 (e86c870879, 2021-01-15, custom)"
16
+ sodipodi:docname="logo2.svg">
17
+ <defs
18
+ id="defs140" />
19
+ <sodipodi:namedview
20
+ id="base"
21
+ pagecolor="#ffffff"
22
+ bordercolor="#666666"
23
+ borderopacity="1.0"
24
+ inkscape:pageopacity="0.0"
25
+ inkscape:pageshadow="2"
26
+ inkscape:zoom="3.959798"
27
+ inkscape:cx="221.86556"
28
+ inkscape:cy="101.82051"
29
+ inkscape:document-units="mm"
30
+ inkscape:current-layer="layer1"
31
+ inkscape:document-rotation="0"
32
+ showgrid="false"
33
+ inkscape:window-width="3159"
34
+ inkscape:window-height="1669"
35
+ inkscape:window-x="0"
36
+ inkscape:window-y="38"
37
+ inkscape:window-maximized="0" />
38
+ <metadata
39
+ id="metadata143">
40
+ <rdf:RDF>
41
+ <cc:Work
42
+ rdf:about="">
43
+ <dc:format>image/svg+xml</dc:format>
44
+ <dc:type
45
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
46
+ <dc:title></dc:title>
47
+ </cc:Work>
48
+ </rdf:RDF>
49
+ </metadata>
50
+ <g
51
+ inkscape:label="Layer 1"
52
+ inkscape:groupmode="layer"
53
+ id="layer1">
54
+ <g
55
+ id="g56"
56
+ transform="matrix(0.15739557,0,0,-0.15739557,25.12113,17.835388)">
57
+ <path
58
+ d="m 0,0 -32.173,-40.343 c -1.892,-2.375 -4.721,-3.739 -7.759,-3.744 -3.868,0 -7.407,2.259 -9.032,5.753 l -6.701,14.423 c -0.584,1.257 -0.637,2.663 -0.149,3.96 0.487,1.297 1.454,2.32 2.723,2.882 0.625,0.276 1.287,0.417 1.969,0.417 2.052,0 3.953,-1.239 4.843,-3.154 l 2.266,-4.877 c 1.007,-2.168 3.119,-3.513 5.514,-3.513 1.853,0 3.579,0.833 4.738,2.286 l 14.422,18.086 c 1.33,1.667 1.688,3.859 0.959,5.862 -0.73,2.003 -2.413,3.451 -4.504,3.873 l -5.812,1.174 c -2.84,0.574 -4.747,3.257 -4.252,5.979 0.443,2.439 2.558,4.209 5.027,4.209 0.34,0 0.685,-0.035 1.023,-0.103 l 6.897,-1.393 c 0.402,-0.082 0.813,-0.123 1.218,-0.123 1.846,0 3.571,0.836 4.734,2.293 1.155,1.449 1.581,3.32 1.168,5.131 l -5.272,23.102 c -0.635,2.78 -3.075,4.722 -5.933,4.722 -1.362,0 -2.655,-0.449 -3.738,-1.3 l -5.197,-4.086 c -0.911,-0.716 -2.003,-1.095 -3.16,-1.095 -1.732,0 -3.338,0.868 -4.293,2.323 -1.444,2.196 -0.872,5.254 1.301,6.961 l 5.176,4.069 c 1.657,1.304 2.511,3.333 2.282,5.431 -0.229,2.098 -1.5,3.897 -3.401,4.811 l -21.738,10.468 c -0.835,0.404 -1.721,0.607 -2.633,0.607 -1.604,0 -3.177,-0.657 -4.317,-1.804 -1.141,-1.146 -1.764,-2.676 -1.756,-4.305 l 0.037,-6.505 c 0.006,-1.373 -0.522,-2.666 -1.491,-3.639 -0.967,-0.974 -2.257,-1.51 -3.631,-1.51 -0.104,0 -0.209,0.003 -0.315,0.009 -2.68,0.159 -4.791,2.497 -4.807,5.321 l -0.035,6.418 c -0.016,3.331 -2.74,6.043 -6.069,6.043 -0.913,0 -1.799,-0.205 -2.635,-0.607 l -21.571,-10.388 c -1.901,-0.915 -3.172,-2.714 -3.401,-4.811 -0.228,-2.096 0.625,-4.127 2.283,-5.431 l 5.314,-4.178 c 2.172,-1.706 2.743,-4.763 1.302,-6.957 -0.956,-1.456 -2.563,-2.326 -4.296,-2.326 -1.155,0 -2.248,0.379 -3.159,1.096 l -5.504,4.327 c -1.083,0.851 -2.376,1.301 -3.738,1.301 -2.859,0 -5.298,-1.942 -5.933,-4.723 l -5.324,-23.326 c -0.413,-1.813 0.012,-3.684 1.169,-5.133 1.162,-1.456 2.886,-2.292 4.732,-2.292 0.406,0 0.817,0.041 1.22,0.123 l 6.814,1.376 c 0.338,0.068 0.681,0.103 1.021,0.103 2.469,0 4.584,-1.767 5.029,-4.203 0.5,-2.725 -1.407,-5.41 -4.25,-5.985 l -5.746,-1.16 c -2.091,-0.422 -3.773,-1.869 -4.503,-3.873 -0.729,-2.003 -0.372,-4.195 0.958,-5.863 l 14.555,-18.251 c 1.158,-1.454 2.886,-2.286 4.737,-2.286 2.395,0 4.508,1.346 5.515,3.513 l 2.433,5.24 c 0.838,1.801 2.662,2.964 4.649,2.964 0.745,0 1.471,-0.161 2.154,-0.478 2.561,-1.19 3.676,-4.241 2.486,-6.802 l -6.769,-14.569 c -1.623,-3.495 -5.162,-5.753 -9.017,-5.753 l -0.112,10e-4 c -3.008,0.033 -5.8,1.397 -7.67,3.742 l -32.291,40.491 c -0.977,1.226 -1.336,2.805 -0.987,4.332 l 13.212,57.886 c 0.347,1.528 1.358,2.794 2.769,3.473 l 53.494,25.761 c 0.69,0.332 1.458,0.507 2.222,0.507 0.765,0 1.532,-0.175 2.223,-0.507 l 53.493,-25.761 c 1.412,-0.679 2.422,-1.945 2.769,-3.473 L 0.989,4.333 C 1.338,2.806 0.978,1.227 0,0 m -73.747,10.642 c -3.348,1.669 -5.381,5.581 -4.094,9.776 0.752,2.452 2.706,4.401 5.158,5.148 4.218,1.287 8.144,-0.781 9.793,-4.162 l 21.139,-30.996 c 0.501,-0.733 -0.386,-1.616 -1.117,-1.112 l -30.613,21.081 c -0.11,0.073 -0.195,0.165 -0.266,0.265 M 15.402,8.019 2.336,65.264 C 0.93,71.429 -3.145,76.538 -8.841,79.281 l -52.905,25.478 c -2.779,1.338 -5.879,2.046 -8.963,2.046 -3.084,0 -6.183,-0.708 -8.963,-2.046 l -52.903,-25.478 c -5.697,-2.743 -9.773,-7.852 -11.178,-14.015 L -156.819,8.021 c -1.408,-6.165 0.046,-12.535 3.988,-17.48 l 31.935,-40.045 c 4.782,-5.996 11.922,-9.486 19.592,-9.573 h 0.296 60.761 c 7.768,0.012 15,3.646 19.842,9.719 l 31.819,39.9 c 3.942,4.944 5.396,11.314 3.988,17.477"
59
+ style="fill:#231f20;fill-opacity:1;fill-rule:nonzero;stroke:none"
60
+ id="path58" />
61
+ </g>
62
+ </g>
63
+ </svg>
@@ -0,0 +1 @@
1
+ <?xml version="1.0" encoding="UTF-8"?><svg id="Layer_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 124.1 137.72"><defs><style>.cls-1{fill:#2d0075;}.cls-1,.cls-2{stroke-width:0px;}.cls-2{fill:#9d6ce5;}</style></defs><g id="Logos"><path class="cls-1" d="m32.19,112.13l11.59-14.99c-4.37-2.04-8.37-4.89-11.98-8.56-8.16-8.27-12.24-18.53-12.24-30.77s4.08-22.5,12.24-30.77c3.61-3.66,7.59-6.5,11.95-8.54l-11.35-14.68c-5.12,2.76-9.89,6.3-14.29,10.64C6.03,26.37,0,40.93,0,58.16s5.92,31.68,17.76,43.35c4.42,4.35,9.23,7.89,14.42,10.62Z"/><path class="cls-2" d="m115.34,89.35c5.84-9.58,8.75-20.2,8.75-31.88,0-17.23-5.89-31.68-17.68-43.35-6.61-6.54-14.13-11.25-22.55-14.13l-12.08,15.63c7.91,1.67,14.78,5.47,20.61,11.42,8.1,8.27,12.15,18.53,12.15,30.77s-4.05,22.5-12.15,30.77c-8.1,8.27-18.22,12.41-30.34,12.41-.12,0-.23,0-.34,0l28.39,36.73h22.78l-21.08-26.35c9.86-5.1,17.71-12.44,23.54-22.01Z"/></g></svg>
@@ -1,16 +1,14 @@
1
- import type { LayerHeader, LayerRow } from '@crvouga/atlas/reports/evidence-types';
2
1
  import type { CheckDetail, EvidenceHolder } from '@crvouga/atlas/reports/evidence-detail';
3
2
  import { roleOfType } from '@crvouga/atlas/reports/evidence-detail';
3
+ import type { LayerHeader, LayerRow } from '@crvouga/atlas/reports/evidence-types';
4
4
  import { contentTypeOf } from '@crvouga/atlas/reports/media-types';
5
5
  import type { Annotation, Attachment, Attempt, Step } from '@crvouga/atlas/schema';
6
+ import { Accordion, Alert, Anchor, Box, Code, Flex, Image, List, Stack, Text, Title } from '@mantine/core';
6
7
  import { useQuery } from '@tanstack/react-query';
7
8
  import { useMemo, type ReactNode } from 'react';
8
-
9
9
  import { reportAssetUrl, reportJson, reportText } from './api';
10
- import styles from './artifacts.module.css';
11
10
  import { duration, plural } from './format';
12
11
  import { StatusMark } from './marks';
13
-
14
12
  /**
15
13
  * How one kind of attachment is shown. The first renderer that matches wins, so a new kind of artifact (a
16
14
  * flame graph, a HAR, a coverage map) is one more entry, and an attachment nobody has a renderer for is still
@@ -20,14 +18,20 @@ export type ArtifactRenderer = {
20
18
  readonly id: string;
21
19
  readonly matches: (attachment: Attachment, type: string) => boolean;
22
20
  /** `url` is where the bytes are, if they are anywhere a browser can reach; `path` is the report API path of a stored file. */
23
- readonly render: (attachment: Attachment, context: { url: string | null; path: string | null; type: string }) => ReactNode;
21
+ readonly render: (
22
+ attachment: Attachment,
23
+ context: {
24
+ url: string | null;
25
+ path: string | null;
26
+ type: string;
27
+ }
28
+ ) => ReactNode;
24
29
  /** Wide renderers take a whole row of the gallery. */
25
30
  readonly wide?: boolean;
26
31
  };
27
-
28
- const mediaType = (attachment: Attachment) => (attachment.contentType ?? contentTypeOf(attachment.name)).split(';')[0]!.trim().toLowerCase();
32
+ const mediaType = (attachment: Attachment) =>
33
+ (attachment.contentType ?? contentTypeOf(attachment.name)).split(';')[0]!.trim().toLowerCase();
29
34
  const label = (attachment: Attachment) => attachment.description ?? attachment.name;
30
-
31
35
  /** Only addresses a browser may follow: never `javascript:` or `data:` from a producer. */
32
36
  function safeUri(uri: string | undefined): string | null {
33
37
  if (!uri) return null;
@@ -38,21 +42,34 @@ function safeUri(uri: string | undefined): string | null {
38
42
  return null;
39
43
  }
40
44
  }
41
-
42
45
  function TextValue({ attachment, path }: { attachment: Attachment; path: string | null }) {
43
- const stored = useQuery({ queryKey: ['artifact-text', path ?? ''], queryFn: ({ signal }) => reportText(path!, signal), enabled: attachment.text === undefined && path !== null, staleTime: Number.POSITIVE_INFINITY, retry: false });
46
+ const stored = useQuery({
47
+ queryKey: ['artifact-text', path ?? ''],
48
+ queryFn: ({ signal }) => reportText(path!, signal),
49
+ enabled: attachment.text === undefined && path !== null,
50
+ staleTime: Number.POSITIVE_INFINITY,
51
+ retry: false
52
+ });
44
53
  const raw = attachment.text ?? stored.data;
45
- if (raw === undefined) return <p className={styles.where}>{stored.isError ? 'Could not be read.' : 'Loading…'}</p>;
46
- const shown = raw.length > 200_000 ? raw.slice(0, 200_000) : raw;
54
+ if (raw === undefined)
55
+ return (
56
+ <Text component="div" c="dimmed">
57
+ {stored.isError ? 'Could not be read.' : 'Loading…'}
58
+ </Text>
59
+ );
60
+ const shown = raw.length > 200000 ? raw.slice(0, 200000) : raw;
47
61
  const pretty = /json/.test(mediaType(attachment)) ? tryPretty(shown) : shown;
48
62
  return (
49
63
  <>
50
- <pre className={styles.text}>{pretty}</pre>
51
- {shown.length < raw.length && <p className={styles.where}>Showing the first 200 000 characters.</p>}
64
+ <Code block>{pretty}</Code>
65
+ {shown.length < raw.length && (
66
+ <Text component="div" c="dimmed">
67
+ Showing the first 200 000 characters.
68
+ </Text>
69
+ )}
52
70
  </>
53
71
  );
54
72
  }
55
-
56
73
  function tryPretty(text: string) {
57
74
  try {
58
75
  return JSON.stringify(JSON.parse(text), null, 2);
@@ -60,64 +77,131 @@ function tryPretty(text: string) {
60
77
  return text;
61
78
  }
62
79
  }
63
-
64
80
  /** In order: the first that matches. Add to the front with {@link registerArtifactRenderer}. */
65
81
  export const artifactRenderers: ArtifactRenderer[] = [
66
- { id: 'image', matches: (_, type) => type.startsWith('image/') && type !== 'image/svg+xml', render: (a, { url }) => (url ? <a href={url} target="_blank" rel="noreferrer"><img src={url} alt={label(a)} loading="lazy" width={a.width} height={a.height} /></a> : null) },
82
+ {
83
+ id: 'image',
84
+ matches: (_, type) => type.startsWith('image/') && type !== 'image/svg+xml',
85
+ render: (a, { url }) =>
86
+ url ? (
87
+ <Anchor href={url} target="_blank" rel="noreferrer">
88
+ <Image src={url} alt={label(a)} loading="lazy" width={a.width} height={a.height} fit="contain" maw="100%" />
89
+ </Anchor>
90
+ ) : null
91
+ },
67
92
  // SVG can carry script: shown as a picture only, never inlined.
68
- { id: 'svg', matches: (_, type) => type === 'image/svg+xml', render: (a, { url }) => (url ? <img src={url} alt={label(a)} loading="lazy" /> : null) },
69
- { id: 'video', wide: true, matches: (_, type) => type.startsWith('video/'), render: (a, { url }) => (url ? <video src={url} controls preload="metadata" aria-label={label(a)} /> : null) },
70
- { id: 'audio', matches: (_, type) => type.startsWith('audio/'), render: (a, { url }) => (url ? <audio src={url} controls preload="none" aria-label={label(a)} /> : null) },
93
+ {
94
+ id: 'svg',
95
+ matches: (_, type) => type === 'image/svg+xml',
96
+ render: (a, { url }) => (url ? <Image src={url} alt={label(a)} loading="lazy" fit="contain" maw="100%" /> : null)
97
+ },
98
+ {
99
+ id: 'video',
100
+ wide: true,
101
+ matches: (_, type) => type.startsWith('video/'),
102
+ render: (a, { url }) =>
103
+ url ? <Box src={url} controls preload="metadata" aria-label={label(a)} component="video" maw="100%" w="100%" /> : null
104
+ },
105
+ {
106
+ id: 'audio',
107
+ matches: (_, type) => type.startsWith('audio/'),
108
+ render: (a, { url }) => (url ? <audio src={url} controls preload="none" aria-label={label(a)} /> : null)
109
+ },
71
110
  // A page snapshot runs nothing: the frame has no permissions and the file is served sandboxed.
72
- { id: 'snapshot', wide: true, matches: (_, type) => type === 'text/html' || type === 'application/xhtml+xml', render: (a, { url }) => (url ? <iframe sandbox="" src={url} title={label(a)} loading="lazy" /> : null) },
73
- { id: 'pdf', wide: true, matches: (_, type) => type === 'application/pdf', render: (a, { url }) => (url ? <iframe sandbox="" src={url} title={label(a)} loading="lazy" /> : null) },
74
- { id: 'text', wide: true, matches: (a, type) => a.text !== undefined || type.startsWith('text/') || /(json|xml|yaml|x-ndjson|x-diff)/.test(type), render: (a, { path }) => <TextValue attachment={a} path={path} /> }
111
+ {
112
+ id: 'snapshot',
113
+ wide: true,
114
+ matches: (_, type) => type === 'text/html' || type === 'application/xhtml+xml',
115
+ render: (a, { url }) =>
116
+ url ? <Box component="iframe" w="100%" h="32rem" bd="0" sandbox="" src={url} title={label(a)} loading="lazy" /> : null
117
+ },
118
+ {
119
+ id: 'pdf',
120
+ wide: true,
121
+ matches: (_, type) => type === 'application/pdf',
122
+ render: (a, { url }) =>
123
+ url ? <Box component="iframe" w="100%" h="32rem" bd="0" sandbox="" src={url} title={label(a)} loading="lazy" /> : null
124
+ },
125
+ {
126
+ id: 'text',
127
+ wide: true,
128
+ matches: (a, type) => a.text !== undefined || type.startsWith('text/') || /(json|xml|yaml|x-ndjson|x-diff)/.test(type),
129
+ render: (a, { path }) => <TextValue attachment={a} path={path} />
130
+ }
75
131
  ];
76
-
77
132
  export function registerArtifactRenderer(renderer: ArtifactRenderer) {
78
133
  artifactRenderers.unshift(renderer);
79
134
  }
80
-
81
135
  function sizeOf(bytes: number | undefined) {
82
136
  if (bytes === undefined) return '';
83
137
  if (bytes < 1024) return `${bytes} B`;
84
138
  return bytes < 1024 * 1024 ? `${(bytes / 1024).toFixed(0)} KiB` : `${(bytes / 1024 / 1024).toFixed(1)} MiB`;
85
139
  }
86
-
87
140
  function Artifact({ attachment, layer }: { attachment: Attachment; layer: string }) {
88
141
  const type = mediaType(attachment);
89
- const path = attachment.bundleKey ? `/layers/${encodeURIComponent(layer)}/blobs/${attachment.bundleKey.split('/').map(encodeURIComponent).join('/')}` : null;
142
+ const path = attachment.bundleKey
143
+ ? `/layers/${encodeURIComponent(layer)}/blobs/${attachment.bundleKey.split('/').map(encodeURIComponent).join('/')}`
144
+ : null;
90
145
  const url = path ? reportAssetUrl(path) : safeUri(attachment.uri);
91
146
  const renderer = artifactRenderers.find((candidate) => candidate.matches(attachment, type));
92
147
  const body = renderer?.render(attachment, { url, path, type });
93
148
  return (
94
- <li className={styles.figure} data-wide={renderer?.wide || undefined} data-role={attachment.role ?? roleOfType(type)}>
149
+ <List.Item data-wide={renderer?.wide || undefined} data-role={attachment.role ?? roleOfType(type)}>
95
150
  {body}
96
- <figcaption>
97
- <strong>{attachment.name}</strong>
98
- {attachment.role && <span className={styles.role}>{attachment.role}</span>}
99
- <span>{[type, sizeOf(attachment.bytes), attachment.durationMs ? duration(attachment.durationMs) : ''].filter(Boolean).join(' · ')}</span>
100
- {url && <a href={url} download={attachment.name} target="_blank" rel="noreferrer">{body ? 'Download' : 'Open'}</a>}
101
- {!url && attachment.text === undefined && <span>No file to show</span>}
102
- </figcaption>
103
- </li>
151
+ <Box component="figcaption">
152
+ <Text span inherit fw={600}>
153
+ {attachment.name}
154
+ </Text>
155
+ {attachment.role && (
156
+ <Text span inherit>
157
+ {attachment.role}
158
+ </Text>
159
+ )}
160
+ <Text span inherit>
161
+ {[type, sizeOf(attachment.bytes), attachment.durationMs ? duration(attachment.durationMs) : ''].filter(Boolean).join(' · ')}
162
+ </Text>
163
+ {url && (
164
+ <Anchor href={url} download={attachment.name} target="_blank" rel="noreferrer">
165
+ {body ? 'Download' : 'Open'}
166
+ </Anchor>
167
+ )}
168
+ {!url && attachment.text === undefined && (
169
+ <Text span inherit>
170
+ No file to show
171
+ </Text>
172
+ )}
173
+ </Box>
174
+ </List.Item>
104
175
  );
105
176
  }
106
-
107
177
  export function Attachments({ attachments, layer }: { attachments: readonly Attachment[]; layer: string }) {
108
178
  if (!attachments.length) return null;
109
179
  return (
110
- <section aria-label="Attachments">
111
- <h4>{plural(attachments.length, 'attachment', 'attachments')}</h4>
112
- <ul className={styles.gallery}>
113
- {attachments.map((attachment, index) => <Artifact key={`${attachment.id ?? attachment.name}:${index}`} attachment={attachment} layer={layer} />)}
114
- </ul>
115
- </section>
180
+ <Box aria-label="Attachments" component="section" mb="lg">
181
+ <Title order={4} mb="sm">
182
+ {plural(attachments.length, 'attachment', 'attachments')}
183
+ </Title>
184
+ <List spacing="xs" listStyleType="none">
185
+ {attachments.map((attachment, index) => (
186
+ <Artifact key={`${attachment.id ?? attachment.name}:${index}`} attachment={attachment} layer={layer} />
187
+ ))}
188
+ </List>
189
+ </Box>
116
190
  );
117
191
  }
118
-
119
- const STEP_STATUS: Record<string, 'passed' | 'failed' | 'running' | 'not-run'> = { passed: 'passed', pass: 'passed', ok: 'passed', failed: 'failed', fail: 'failed', error: 'failed', running: 'running', skipped: 'not-run', 'not-run': 'not-run', 'not-reached': 'not-run', pending: 'not-run' };
120
-
192
+ const STEP_STATUS: Record<string, 'passed' | 'failed' | 'running' | 'not-run'> = {
193
+ passed: 'passed',
194
+ pass: 'passed',
195
+ ok: 'passed',
196
+ failed: 'failed',
197
+ fail: 'failed',
198
+ error: 'failed',
199
+ running: 'running',
200
+ skipped: 'not-run',
201
+ 'not-run': 'not-run',
202
+ 'not-reached': 'not-run',
203
+ pending: 'not-run'
204
+ };
121
205
  /** The timeline: ordered by when each step started, nested by `parentId`. A step whose parent is missing is shown at the top. */
122
206
  export function Timeline({ steps, layer }: { steps: readonly Step[]; layer: string }) {
123
207
  const tree = useMemo(() => {
@@ -131,105 +215,157 @@ export function Timeline({ steps, layer }: { steps: readonly Step[]; layer: stri
131
215
  return children;
132
216
  }, [steps]);
133
217
  const render = (parent: string | null): ReactNode => (
134
- <ol>
218
+ <List spacing="xs" listStyleType="none" type="ordered">
135
219
  {(tree.get(parent) ?? []).map((step, index) => {
136
220
  const status = STEP_STATUS[(step.status ?? '').toLowerCase()];
137
221
  return (
138
- <li key={`${step.id ?? step.name}:${index}`} className={styles.step} data-status={status ?? step.status ?? 'none'}>
139
- <div className={styles.stepRow}>
222
+ <List.Item data-evidence-step key={`${step.id ?? step.name}:${index}`} data-status={status ?? step.status ?? 'none'}>
223
+ <Flex gap="xs" align="center" wrap="wrap">
140
224
  <StatusMark status={status ?? 'none'} size={14} />
141
- <span className={styles.stepName}>{step.name}</span>
142
- {step.kind && <span className={styles.stepKind}>{step.kind}</span>}
143
- {step.status && !status && <span className={styles.stepKind}>{step.status}</span>}
144
- <span className={styles.spacer} />
145
- <span className={styles.stepTime}>{[step.offsetMs !== undefined ? `+${duration(step.offsetMs)}` : '', duration(step.durationMs)].filter(Boolean).join(' · ')}</span>
146
- </div>
147
- {step.detail && <p className={styles.stepDetail}>{step.detail}</p>}
225
+ <Text span inherit>
226
+ {step.name}
227
+ </Text>
228
+ {step.kind && (
229
+ <Text span inherit>
230
+ {step.kind}
231
+ </Text>
232
+ )}
233
+ {step.status && !status && (
234
+ <Text span inherit>
235
+ {step.status}
236
+ </Text>
237
+ )}
238
+ <Box flex={1} />
239
+ <Text span inherit c="dimmed">
240
+ {[step.offsetMs !== undefined ? `+${duration(step.offsetMs)}` : '', duration(step.durationMs)].filter(Boolean).join(' · ')}
241
+ </Text>
242
+ </Flex>
243
+ {step.detail && <Text component="div">{step.detail}</Text>}
148
244
  {step.annotations && <Notes notes={step.annotations} />}
149
245
  {step.attachments && <Attachments attachments={step.attachments} layer={layer} />}
150
246
  {step.id && tree.has(step.id) && render(step.id)}
151
- </li>
247
+ </List.Item>
152
248
  );
153
249
  })}
154
- </ol>
250
+ </List>
155
251
  );
156
252
  return (
157
- <section aria-label="Timeline" className={styles.timeline}>
158
- <h4>{plural(steps.length, 'step', 'steps')}</h4>
253
+ <Box aria-label="Timeline" component="section" mb="lg">
254
+ <Title order={4} mb="sm">
255
+ {plural(steps.length, 'step', 'steps')}
256
+ </Title>
159
257
  {render(null)}
160
- </section>
258
+ </Box>
161
259
  );
162
260
  }
163
-
164
261
  export function Notes({ notes }: { notes: readonly Annotation[] }) {
165
262
  if (!notes.length) return null;
166
263
  return (
167
- <ul className={styles.notes} aria-label="Annotations">
264
+ <List aria-label="Annotations" spacing="xs" listStyleType="none">
168
265
  {notes.map((note, index) => (
169
- <li key={index} className={styles.note}>
170
- <span className={styles.level} data-level={note.level ?? 'info'}>{note.level && ['info', 'warning', 'error'].includes(note.level) ? note.level : note.type}</span>
171
- <span>
266
+ <Alert key={index} color={'blue'} my="sm">
267
+ <Text data-level={note.level ?? 'info'} span inherit>
268
+ {note.level && ['info', 'warning', 'error'].includes(note.level) ? note.level : note.type}
269
+ </Text>
270
+ <Text span inherit>
172
271
  {note.message}
173
- {note.source?.file && <span className={styles.where}> {note.source.file}{note.source.line ? `:${note.source.line}` : ''}</span>}
174
- {note.level && !['info', 'warning', 'error'].includes(note.level) && <span className={styles.where}> ({note.level})</span>}
175
- </span>
176
- </li>
272
+ {note.source?.file && (
273
+ <Text span inherit c="dimmed">
274
+ {' '}
275
+ {note.source.file}
276
+ {note.source.line ? `:${note.source.line}` : ''}
277
+ </Text>
278
+ )}
279
+ {note.level && !['info', 'warning', 'error'].includes(note.level) && (
280
+ <Text span inherit c="dimmed">
281
+ {' '}
282
+ ({note.level})
283
+ </Text>
284
+ )}
285
+ </Text>
286
+ </Alert>
177
287
  ))}
178
- </ul>
288
+ </List>
179
289
  );
180
290
  }
181
-
182
291
  function Tries({ attempts, layer }: { attempts: readonly Attempt[]; layer: string }) {
183
292
  if (!attempts.length) return null;
184
293
  return (
185
- <section aria-label="Tries">
186
- <h4>{plural(attempts.length, 'try', 'tries')}</h4>
187
- <ol className={styles.timeline}>
294
+ <Box aria-label="Tries" component="section" mb="lg">
295
+ <Title order={4} mb="sm">
296
+ {plural(attempts.length, 'try', 'tries')}
297
+ </Title>
298
+ <List spacing="xs" listStyleType="none" type="ordered">
188
299
  {attempts.map((attempt) => (
189
- <li key={attempt.index} className={styles.step}>
190
- <div className={styles.stepRow}>
300
+ <List.Item key={attempt.index}>
301
+ <Flex gap="xs" align="center" wrap="wrap">
191
302
  <StatusMark status={attempt.status} size={14} />
192
- <span className={styles.stepName}>Try {attempt.index + 1}</span>
193
- <span className={styles.spacer} />
194
- <span className={styles.stepTime}>{duration(attempt.durationMs)}</span>
195
- </div>
196
- {attempt.detail && <p className={styles.stepDetail}>{attempt.detail}</p>}
303
+ <Text span inherit>
304
+ Try {attempt.index + 1}
305
+ </Text>
306
+ <Box flex={1} />
307
+ <Text span inherit c="dimmed">
308
+ {duration(attempt.durationMs)}
309
+ </Text>
310
+ </Flex>
311
+ {attempt.detail && <Text component="div">{attempt.detail}</Text>}
197
312
  {attempt.attachments && <Attachments attachments={attempt.attachments} layer={layer} />}
198
- </li>
313
+ </List.Item>
199
314
  ))}
200
- </ol>
201
- </section>
315
+ </List>
316
+ </Box>
202
317
  );
203
318
  }
204
-
205
319
  /** What is not known: kept, and shown as it is, so nothing a tool recorded is out of reach. */
206
320
  function Raw({ extensions }: { extensions: Readonly<Record<string, unknown>> }) {
207
321
  const names = Object.keys(extensions);
208
322
  if (!names.length) return null;
209
323
  return (
210
- <details className={styles.raw}>
211
- <summary>{plural(names.length, 'extension', 'extensions')}: {names.join(', ')}</summary>
212
- <pre className={styles.text}>{JSON.stringify(extensions, null, 2)}</pre>
213
- </details>
324
+ <Accordion variant="separated" my="sm">
325
+ <Accordion.Item value="content">
326
+ <Accordion.Control>
327
+ {plural(names.length, 'extension', 'extensions')}: {names.join(', ')}
328
+ </Accordion.Control>
329
+ <Accordion.Panel>
330
+ <Code block>{JSON.stringify(extensions, null, 2)}</Code>
331
+ </Accordion.Panel>
332
+ </Accordion.Item>
333
+ </Accordion>
214
334
  );
215
335
  }
216
-
217
336
  function Facts({ holder }: { holder: EvidenceHolder }) {
218
337
  const labels = Object.entries(holder.labels ?? {});
219
338
  const tags = holder.tags ?? [];
220
- const links = (holder.links ?? []).flatMap((link) => { const url = safeUri(link.url); return url ? [{ ...link, url }] : []; });
339
+ const links = (holder.links ?? []).flatMap((link) => {
340
+ const url = safeUri(link.url);
341
+ return url ? [{ ...link, url }] : [];
342
+ });
221
343
  if (!labels.length && !tags.length && !links.length) return null;
222
344
  return (
223
- <div>
224
- <ul className={styles.chips} aria-label="Facts">
225
- {tags.map((tag) => <li key={`t:${tag}`} className={styles.chip}>{tag}</li>)}
226
- {labels.map(([name, value]) => <li key={`l:${name}`} className={styles.chip}>{name}<b>{value}</b></li>)}
227
- {links.map((link) => <li key={`k:${link.url}`} className={styles.chip}><a href={link.url} target="_blank" rel="noreferrer">{link.name ?? link.rel ?? link.url}</a></li>)}
228
- </ul>
229
- </div>
345
+ <Stack gap="sm">
346
+ <List aria-label="Facts" spacing="xs" listStyleType="none">
347
+ {tags.map((tag) => (
348
+ <List.Item key={`t:${tag}`}>{tag}</List.Item>
349
+ ))}
350
+ {labels.map(([name, value]) => (
351
+ <List.Item key={`l:${name}`}>
352
+ {name}
353
+ <Text span inherit fw={600}>
354
+ {value}
355
+ </Text>
356
+ </List.Item>
357
+ ))}
358
+ {links.map((link) => (
359
+ <List.Item key={`k:${link.url}`}>
360
+ <Anchor href={link.url} target="_blank" rel="noreferrer">
361
+ {link.name ?? link.rel ?? link.url}
362
+ </Anchor>
363
+ </List.Item>
364
+ ))}
365
+ </List>
366
+ </Stack>
230
367
  );
231
368
  }
232
-
233
369
  function Evidence({ holder, layer, steps }: { holder: EvidenceHolder; layer: string; steps?: boolean }) {
234
370
  return (
235
371
  <>
@@ -242,31 +378,48 @@ function Evidence({ holder, layer, steps }: { holder: EvidenceHolder; layer: str
242
378
  </>
243
379
  );
244
380
  }
245
-
246
381
  /** Everything one result carries beyond its outcome, read when it is opened, and what the run around it carried. */
247
382
  export function ResultEvidence({ layer, row }: { layer: LayerHeader; row: LayerRow }) {
248
383
  const wanted = row.evidence !== undefined || layer.evidence !== undefined;
249
384
  const detail = useQuery({
250
385
  queryKey: ['evidence-detail', layer.backend, layer.id, row.kind, row.id],
251
- queryFn: ({ signal }) => reportJson<CheckDetail>(`/evidence/detail/${encodeURIComponent(layer.backend)}/${encodeURIComponent(layer.id)}?check=${encodeURIComponent(row.id)}&kind=${encodeURIComponent(row.kind)}`, signal),
386
+ queryFn: ({ signal }) =>
387
+ reportJson<CheckDetail>(
388
+ `/evidence/detail/${encodeURIComponent(layer.backend)}/${encodeURIComponent(layer.id)}?check=${encodeURIComponent(row.id)}&kind=${encodeURIComponent(row.kind)}`,
389
+ signal
390
+ ),
252
391
  enabled: wanted && /^[0-9a-f]{64}$/.test(layer.id),
253
392
  staleTime: Number.POSITIVE_INFINITY,
254
393
  retry: false
255
394
  });
256
395
  if (!wanted) return null;
257
- if (detail.isLoading) return <p className={styles.where} role="status">Loading what this result carries…</p>;
258
- if (detail.isError || !detail.data) return <p className={styles.where} role="alert">What this result carries could not be read.</p>;
396
+ if (detail.isLoading)
397
+ return (
398
+ <Text role="status" component="div" c="dimmed">
399
+ Loading what this result carries…
400
+ </Text>
401
+ );
402
+ if (detail.isError || !detail.data)
403
+ return (
404
+ <Text role="alert" component="div" c="dimmed">
405
+ What this result carries could not be read.
406
+ </Text>
407
+ );
259
408
  const { check, run } = detail.data;
260
- const hasRun = Object.values(run).some((value) => (Array.isArray(value) ? value.length > 0 : value && typeof value === 'object' ? Object.keys(value).length > 0 : false));
409
+ const hasRun = Object.values(run).some((value) =>
410
+ Array.isArray(value) ? value.length > 0 : value && typeof value === 'object' ? Object.keys(value).length > 0 : false
411
+ );
261
412
  return (
262
- <div className={styles.panel} aria-label="Evidence">
413
+ <Stack aria-label="Evidence" gap="sm" miw={0}>
263
414
  <Evidence holder={check} layer={layer.id} steps />
264
415
  {hasRun && (
265
- <section aria-label="From the run">
266
- <h4>From the run</h4>
416
+ <Box aria-label="From the run" component="section" mb="lg">
417
+ <Title order={4} mb="sm">
418
+ From the run
419
+ </Title>
267
420
  <Evidence holder={run} layer={layer.id} />
268
- </section>
421
+ </Box>
269
422
  )}
270
- </div>
423
+ </Stack>
271
424
  );
272
425
  }