@shipload/item-renderer 1.0.0-next.5 → 1.0.0-next.51

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.
@@ -0,0 +1,170 @@
1
+ import {el, escapeXml} from './svg.ts'
2
+
3
+ export const entityIconSlugs = ['ship', 'container'] as const
4
+
5
+ export type EntityIconSlug = (typeof entityIconSlugs)[number]
6
+
7
+ export interface EntityIconSvgOpts {
8
+ size?: number
9
+ title?: string
10
+ className?: string
11
+ }
12
+
13
+ export interface EntityIconInlineOpts {
14
+ x: number
15
+ y: number
16
+ size: number
17
+ }
18
+
19
+ const entityIconLabels: Record<EntityIconSlug, string> = {
20
+ ship: 'Ship',
21
+ container: 'Container',
22
+ }
23
+
24
+ const entityIconNames: Record<string, EntityIconSlug> = {
25
+ ship: 'ship',
26
+ roustabout: 'ship',
27
+ prospector: 'ship',
28
+ hauler: 'ship',
29
+ container: 'container',
30
+ }
31
+
32
+ function normalizeEntityName(name: string): string {
33
+ return name
34
+ .trim()
35
+ .toLowerCase()
36
+ .replace(/\s*\(?t\d+\)?$/, '')
37
+ }
38
+
39
+ export function entityIconSlugForName(name: string): EntityIconSlug | null {
40
+ return entityIconNames[normalizeEntityName(name)] ?? null
41
+ }
42
+
43
+ function shipIcon(): string {
44
+ return [
45
+ '<svg x="8" y="2" width="48" height="60" viewBox="0 0 952 1267" fill="none" xmlns="http://www.w3.org/2000/svg">',
46
+ '<g filter="url(#entity-ship-shadow)">',
47
+ '<path d="M641.917 1218.78L644.962 1209.26L641.917 1218.78ZM781.212 1109.06L771.239 1109.79V1109.79L781.212 1109.06ZM573.695 1196.97L570.65 1206.5V1206.5L573.695 1196.97ZM378.825 1196.97L381.87 1206.5V1206.5L378.825 1196.97ZM310.602 1218.78L307.558 1209.26L307.558 1209.26L310.602 1218.78ZM171.308 1109.06L181.281 1109.79V1109.79L171.308 1109.06ZM183.165 946.885L193.138 947.614C193.408 943.924 191.619 940.387 188.486 938.418L183.165 946.885ZM177.407 943.266L182.728 934.799C179.817 932.97 176.17 932.764 173.072 934.254C169.973 935.745 167.858 938.723 167.47 942.139L177.407 943.266ZM69.7567 951.738L79.3927 949.064C78.5326 945.965 76.2339 943.468 73.2155 942.355C70.1972 941.243 66.828 941.65 64.1617 943.45L69.7567 951.738ZM39 972.5L29 972.5C29 976.198 31.0412 979.595 34.3069 981.33C37.5727 983.066 41.5297 982.858 44.595 980.788L39 972.5ZM39 854.453L49 854.453C49 851.706 47.8698 849.08 45.8747 847.191L39 854.453ZM193 537L193 527L193 537ZM213.134 537L213.134 547C218.374 547 222.725 542.955 223.107 537.729L213.134 537ZM217.043 483.53L227.017 484.259C227.383 479.245 223.97 474.738 219.045 473.732L217.043 483.53ZM187.633 477.521L185.631 487.319V487.319L187.633 477.521ZM146.398 391.078L155.272 395.687V395.687L146.398 391.078ZM228.333 233.342L219.458 228.733L219.458 228.733L228.333 233.342ZM236.543 221.351L244.048 227.959C245.373 226.454 246.215 224.586 246.465 222.597L236.543 221.351ZM715.187 215.614L705.298 217.095C705.652 219.458 706.84 221.617 708.648 223.179L715.187 215.614ZM729.187 233.342L738.061 228.733L738.061 228.733L729.187 233.342ZM811.122 391.078L802.247 395.687V395.687L811.122 391.078ZM769.887 477.521L767.885 467.723V467.723L769.887 477.521ZM735.55 484.536L733.548 474.739C728.623 475.745 725.21 480.252 725.577 485.265L735.55 484.536ZM739.386 537L729.412 537.729C729.795 542.955 734.146 547 739.386 547L739.386 537ZM937.998 716L947.998 716L937.998 716ZM937.998 796.434L947.998 796.434L937.998 796.434ZM912.999 854.53L906.124 847.268C904.129 849.157 902.999 851.783 902.999 854.53L912.999 854.53ZM912.999 972.5L907.404 980.788C910.469 982.858 914.426 983.066 917.692 981.33C920.958 979.595 922.999 976.198 922.999 972.5L912.999 972.5ZM882.577 951.964L888.172 943.676C885.522 941.887 882.176 941.473 879.171 942.562C876.165 943.651 873.861 946.112 872.971 949.183L882.577 951.964ZM774.239 943.828L784.153 942.52C783.708 939.141 781.571 936.221 778.485 934.774C775.399 933.327 771.789 933.551 768.905 935.369L774.239 943.828ZM769.356 946.907L764.022 938.448C760.897 940.419 759.113 943.952 759.383 947.636L769.356 946.907ZM641.917 1218.78L638.872 1228.31C717.805 1253.54 797.228 1190.98 791.185 1108.33L781.212 1109.06L771.239 1109.79C776.248 1178.31 710.402 1230.17 644.962 1209.26L641.917 1218.78ZM573.695 1196.97L570.65 1206.5L638.872 1228.31L641.917 1218.78L644.962 1209.26L576.739 1187.45L573.695 1196.97ZM378.825 1196.97L381.87 1206.5C443.266 1186.87 509.253 1186.87 570.65 1206.5L573.695 1196.97L576.739 1187.45C511.382 1166.56 441.137 1166.56 375.78 1187.45L378.825 1196.97ZM310.602 1218.78L313.647 1228.31L381.87 1206.5L378.825 1196.97L375.78 1187.45L307.558 1209.26L310.602 1218.78ZM171.308 1109.06L161.334 1108.33C155.291 1190.98 234.715 1253.54 313.647 1228.31L310.602 1218.78L307.558 1209.26C242.118 1230.17 176.271 1178.31 181.281 1109.79L171.308 1109.06ZM183.165 946.885L173.191 946.156L161.334 1108.33L171.308 1109.06L181.281 1109.79L193.138 947.614L183.165 946.885ZM177.407 943.266L172.085 951.733L177.844 955.352L183.165 946.885L188.486 938.418L182.728 934.799L177.407 943.266ZM122.76 992L122.76 1002C156.161 1002 183.666 976.815 187.343 944.393L177.407 943.266L167.47 942.139C164.927 964.567 145.872 982 122.76 982L122.76 992ZM69.7567 951.738L60.1207 954.412C67.733 981.848 92.8801 1002 122.76 1002L122.76 992L122.76 982C102.098 982 84.6649 968.066 79.3927 949.064L69.7567 951.738ZM39 972.5L44.595 980.788L75.3516 960.026L69.7567 951.738L64.1617 943.45L33.4051 964.212L39 972.5ZM39 854.453L29 854.453L29 972.5L39 972.5L49 972.5L49 854.453L39 854.453ZM14 796.356L3.99998 796.356C3.99997 821.334 14.3573 844.895 32.1253 861.715L39 854.453L45.8747 847.191C32.0541 834.108 24 815.781 24 796.356L14 796.356ZM14 716L3.99998 716L3.99998 796.356L14 796.356L24 796.356L24 716L14 716ZM193 537L193 527C88.6182 527 3.99999 611.618 3.99998 716L14 716L24 716C24 622.664 99.6638 547 193 547L193 537ZM213.134 537L213.134 527L193 527L193 537L193 547L213.134 547L213.134 537ZM217.043 483.53L207.07 482.801L203.16 536.271L213.134 537L223.107 537.729L227.017 484.259L217.043 483.53ZM187.633 477.521L185.631 487.319L215.041 493.327L217.043 483.53L219.045 473.732L189.635 467.723L187.633 477.521ZM146.398 391.078L137.524 386.468C116.096 427.719 140.088 478.014 185.631 487.319L187.633 477.521L189.635 467.723C157.103 461.077 139.967 425.152 155.272 395.687L146.398 391.078ZM228.333 233.342L219.458 228.733L137.524 386.468L146.398 391.078L155.272 395.687L237.207 237.952L228.333 233.342ZM236.543 221.351L229.037 214.743C225.335 218.948 222.108 223.633 219.458 228.733L228.333 233.342L237.207 237.952C239.101 234.305 241.406 230.96 244.048 227.959L236.543 221.351ZM476.26 10L476.26 2.06687e-05C348.849 9.53008e-06 242.322 95.0405 226.62 220.105L236.543 221.351L246.465 222.597C260.916 107.486 358.981 20 476.26 20L476.26 10ZM715.187 215.614L725.077 214.133C706.786 92.0264 601.635 3.16294e-05 476.26 2.06687e-05L476.26 10L476.26 20C591.662 20 688.463 104.711 705.298 217.095L715.187 215.614ZM729.187 233.342L738.061 228.733C733.904 220.73 728.325 213.752 721.727 208.049L715.187 215.614L708.648 223.179C713.358 227.251 717.341 232.232 720.313 237.952L729.187 233.342ZM811.122 391.078L819.996 386.468L738.061 228.733L729.187 233.342L720.313 237.952L802.247 395.687L811.122 391.078ZM769.887 477.521L771.888 487.319C817.432 478.014 841.423 427.719 819.996 386.468L811.122 391.078L802.247 395.687C817.553 425.152 800.416 461.077 767.885 467.723L769.887 477.521ZM735.55 484.536L737.552 494.334L771.888 487.319L769.887 477.521L767.885 467.723L733.548 474.739L735.55 484.536ZM739.386 537L749.359 536.271L745.523 483.807L735.55 484.536L725.577 485.265L729.412 537.729L739.386 537ZM758.998 537L758.998 527L739.386 527L739.386 537L739.386 547L758.998 547L758.998 537ZM937.998 716L947.998 716C947.998 611.618 863.38 527 758.998 527L758.998 537L758.998 547C852.334 547 927.998 622.664 927.998 716L937.998 716ZM937.998 796.434L947.998 796.434L947.998 716L937.998 716L927.998 716L927.998 796.434L937.998 796.434ZM912.999 854.53L919.874 861.792C937.643 844.971 947.998 821.409 947.998 796.434L937.998 796.434L927.998 796.434C927.998 815.857 919.945 834.184 906.124 847.268L912.999 854.53ZM912.999 972.5L922.999 972.5L922.999 854.53L912.999 854.53L902.999 854.53L902.999 972.5L912.999 972.5ZM882.577 951.964L876.982 960.252L907.404 980.788L912.999 972.5L918.594 964.212L888.172 943.676L882.577 951.964ZM829.26 992L829.26 1002C859.116 1002 884.282 982.033 892.182 954.745L882.577 951.964L872.971 949.183C867.48 968.149 849.975 982 829.26 982L829.26 992ZM774.239 943.828L764.325 945.136C768.559 977.233 796.009 1002 829.26 1002L829.26 992L829.26 982C806.175 982 787.092 964.8 784.153 942.52L774.239 943.828ZM769.356 946.907L774.69 955.366L779.573 952.287L774.239 943.828L768.905 935.369L764.022 938.448L769.356 946.907ZM781.212 1109.06L791.185 1108.33L779.33 946.178L769.356 946.907L759.383 947.636L771.239 1109.79L781.212 1109.06Z" fill="#F4EDAE"/>',
48
+ '</g>',
49
+ '<path d="M510.587 519.271C489.586 523.561 467.934 523.561 446.933 519.271L189.835 466.743C157.954 460.23 141.161 425.025 156.16 396.149L238.095 238.413C246.525 222.184 263.29 212 281.578 212L675.941 212C694.229 212 710.996 222.184 719.426 238.413L801.36 396.149C816.359 425.024 799.565 460.23 767.685 466.743L510.587 519.271Z" fill="#ED942E" stroke="black" stroke-width="22"/>',
50
+ '<path d="M829.26 981C853.836 981 873.76 961.077 873.76 936.5L873.76 647.5C873.76 622.923 853.836 603 829.26 603C804.683 603 784.76 622.923 784.76 647.5L784.76 936.5C784.76 961.077 804.683 981 829.26 981Z" fill="#7E7876" stroke="black" stroke-width="22"/>',
51
+ '<path d="M901.999 689.7L785.999 689.7L785.999 873.498L901.999 951.804L901.999 689.7Z" fill="#484155" stroke="black" stroke-width="22"/>',
52
+ '<path d="M646.998 885.977C646.998 940.296 706.855 973.315 752.802 944.342L894.802 854.799C914.842 842.162 926.998 820.127 926.998 796.435L926.998 716C926.998 623.216 851.782 548 758.998 548L715.998 548C677.89 548 646.998 578.892 646.998 617L646.998 885.977Z" fill="#ED942E" stroke="black" stroke-width="22"/>',
53
+ '<path d="M122.76 981C98.4592 981 78.7598 961.301 78.7598 937L78.7597 647C78.7597 622.7 98.4592 603 122.76 603C147.06 603 166.76 622.7 166.76 647L166.76 937C166.76 961.301 147.06 981 122.76 981Z" fill="#7E7876" stroke="black" stroke-width="22"/>',
54
+ '<path d="M50 689.7L166 689.7L166 873.498L50 951.804L50 689.7Z" fill="#484155" stroke="black" stroke-width="22"/>',
55
+ '<path d="M306 886.231C306 940.504 246.234 973.532 200.283 944.651L57.2832 854.776C37.1928 842.15 25 820.085 25 796.357L25 716C25 623.216 100.216 548 193 548L237 548C275.108 548 306 578.892 306 617L306 886.231Z" fill="#ED942E" stroke="black" stroke-width="22"/>',
56
+ '<path d="M307.253 1208.3C242.488 1229.01 177.32 1177.67 182.278 1109.86L246.259 234.799C255.071 114.28 355.419 21 476.26 21C597.101 21.0001 697.449 114.28 706.261 234.799L770.241 1109.86C775.199 1177.67 710.032 1229.01 645.267 1208.3L577.044 1186.5C511.489 1165.54 441.031 1165.54 375.476 1186.5L307.253 1208.3Z" fill="#484155" stroke="black" stroke-width="22"/>',
57
+ '<path d="M477.999 857L477.999 513" stroke="black" stroke-width="22"/>',
58
+ '<path d="M236.76 326L246.646 315.723C372.54 184.856 581.979 184.856 707.873 315.723L717.76 326" stroke="black" stroke-width="22"/>',
59
+ '<path d="M475.76 1177C475.76 1166.48 475.76 1055.95 475.76 1002" stroke="black" stroke-width="22"/>',
60
+ '<path d="M475.76 1053C401.201 1053 340.76 992.558 340.76 918L340.76 273C340.76 198.442 401.201 138 475.76 138C550.318 138 610.76 198.442 610.76 273L610.76 918C610.76 992.558 550.318 1053 475.76 1053Z" fill="#7E7876" stroke="black" stroke-width="22"/>',
61
+ '<path d="M496.76 649L455.76 649" stroke="#F4EDAE" stroke-width="22" stroke-linecap="round" stroke-linejoin="round"/>',
62
+ '<path d="M496.76 584L455.76 584" stroke="#F4EDAE" stroke-width="22" stroke-linecap="round" stroke-linejoin="round"/>',
63
+ '<circle cx="476.499" cy="922.5" r="129.5" fill="#ED942E" stroke="black" stroke-width="22"/>',
64
+ '<path d="M563.707 922.209C563.707 874.045 524.662 835 476.498 835V922.209H563.707Z" fill="black"/>',
65
+ '<path d="M388.999 922.209C388.999 970.373 428.174 1009.42 476.498 1009.42L476.498 922.209L388.999 922.209Z" fill="black"/>',
66
+ '<mask id="entity-ship-stripe-mask" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="411" y="201" width="132" height="219">',
67
+ '<path d="M411.857 266.5C411.857 230.325 441.183 201 477.357 201C513.532 201 542.857 230.325 542.857 266.5V354.5C542.857 390.675 513.532 420 477.357 420C441.183 420 411.857 390.675 411.857 354.5V266.5Z" fill="#D9D9D9"/>',
68
+ '</mask>',
69
+ '<g mask="url(#entity-ship-stripe-mask)">',
70
+ '<path d="M381.425 443.593L573.655 320.031" stroke="#F4EDAE" stroke-width="22"/>',
71
+ '<path d="M381.425 373.793L573.655 250.231" stroke="#F4EDAE" stroke-width="22"/>',
72
+ '<path d="M381.425 303.994L573.655 180.431" stroke="#F4EDAE" stroke-width="22"/>',
73
+ '</g>',
74
+ '<path d="M581.111 998L752.111 1169" stroke="black" stroke-width="22"/>',
75
+ '<path d="M374.408 998L203.408 1169" stroke="black" stroke-width="22"/>',
76
+ '<defs>',
77
+ '<filter id="entity-ship-shadow" x="0" y="0" width="951.998" height="1241.96" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">',
78
+ '<feFlood flood-opacity="0" result="BackgroundImageFix"/>',
79
+ '<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>',
80
+ '<feOffset dy="4"/>',
81
+ '<feGaussianBlur stdDeviation="2"/>',
82
+ '<feComposite in2="hardAlpha" operator="out"/>',
83
+ '<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>',
84
+ '<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1832_9175"/>',
85
+ '<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1832_9175" result="shape"/>',
86
+ '</filter>',
87
+ '</defs>',
88
+ '</svg>',
89
+ ].join('')
90
+ }
91
+
92
+ function containerIcon(): string {
93
+ return [
94
+ '<svg x="4" y="4" width="56" height="56" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg">',
95
+ '<g clip-path="url(#entity-container-clip)">',
96
+ '<path d="M374.51 81.5C400.901 81.5 423.354 98.3471 431.709 121.871H592.292C600.647 98.3473 623.099 81.5001 649.49 81.5H768.137C797.718 81.4979 823.779 81.4952 844.811 84.2578C867.536 87.2429 890.207 94.07 908.959 112.13C909.948 113.082 910.919 114.053 911.871 115.042C929.931 133.794 936.758 156.464 939.743 179.189C942.506 200.221 942.502 226.281 942.5 255.862V374.129C942.5 400.875 925.304 423.602 901.367 431.865V592.135C925.304 600.398 942.5 623.125 942.5 649.871V768.138C942.502 797.719 942.506 823.779 939.743 844.811C936.758 867.536 929.931 890.207 911.871 908.959C910.919 909.948 909.948 910.919 908.959 911.871C890.207 929.931 867.536 936.758 844.811 939.743C823.779 942.506 797.719 942.502 768.138 942.5H649.871C623.125 942.5 600.399 925.304 592.136 901.367H431.865C423.602 925.304 400.875 942.5 374.129 942.5H255.862C226.281 942.502 200.221 942.506 179.189 939.743C156.464 936.758 133.794 929.931 115.042 911.871C114.053 910.919 113.082 909.948 112.13 908.959C94.07 890.207 87.2429 867.536 84.2578 844.811C81.4952 823.779 81.4979 797.718 81.5 768.137V649.49C81.5001 623.099 98.3471 600.646 121.871 592.291V431.709C98.3471 423.354 81.5 400.901 81.5 374.51V255.863C81.4979 226.282 81.4952 200.221 84.2578 179.189C87.243 156.464 94.0702 133.794 112.13 115.042C113.082 114.053 114.053 113.082 115.042 112.13C133.794 94.0702 156.464 87.243 179.189 84.2578C200.221 81.4952 226.282 81.4979 255.863 81.5H374.51ZM258.348 122.5C226.61 122.5 202.856 122.501 184.529 124.908C166.216 127.314 153.396 132.115 143.483 141.661C142.865 142.257 142.257 142.865 141.661 143.483C132.115 153.396 127.314 166.216 124.908 184.529C122.501 202.856 122.5 226.61 122.5 258.348V374.51C122.5 385.382 131.314 394.195 142.186 394.195C153.61 394.195 162.871 403.457 162.871 414.881V609.119C162.871 620.543 153.61 629.805 142.186 629.805C131.314 629.805 122.5 638.618 122.5 649.49V765.652C122.5 797.39 122.501 821.144 124.908 839.471C127.314 857.784 132.115 870.605 141.661 880.518C142.257 881.136 142.865 881.744 143.483 882.34C153.396 891.886 166.217 896.686 184.529 899.092C202.856 901.499 226.61 901.5 258.348 901.5H374.129C385.211 901.5 394.195 892.516 394.195 881.434C394.195 869.799 403.627 860.367 415.262 860.367H608.738C620.373 860.367 629.805 869.799 629.805 881.434C629.805 892.516 638.789 901.5 649.871 901.5H765.652C797.39 901.5 821.144 901.499 839.471 899.092C857.784 896.686 870.605 891.886 880.518 882.34C881.136 881.744 881.744 881.136 882.34 880.518C891.886 870.605 896.686 857.784 899.092 839.471C901.499 821.144 901.5 797.39 901.5 765.652V649.871C901.5 638.789 892.516 629.805 881.434 629.805C869.799 629.805 860.367 620.373 860.367 608.738V415.262C860.367 403.627 869.799 394.195 881.434 394.195C892.516 394.195 901.5 385.211 901.5 374.129V258.348C901.5 226.61 901.499 202.856 899.092 184.529C896.686 166.217 891.886 153.396 882.34 143.483C881.744 142.865 881.136 142.257 880.518 141.661C870.605 132.115 857.784 127.314 839.471 124.908C821.144 122.501 797.39 122.5 765.652 122.5H649.49C638.618 122.5 629.805 131.314 629.805 142.186C629.805 153.61 620.543 162.871 609.119 162.871H414.881C403.457 162.871 394.195 153.61 394.195 142.186C394.195 131.314 385.382 122.5 374.51 122.5H258.348Z" fill="#F4EDAE" stroke="#F4EDAE"/>',
97
+ '<path fill-rule="evenodd" clip-rule="evenodd" d="M394.695 142.186C394.695 131.037 385.658 122 374.51 122H258.348C194.9 122 163.177 122 143.136 141.301C142.513 141.901 141.901 142.513 141.301 143.136C122 163.177 122 194.9 122 258.348V374.51C122 385.658 131.037 394.695 142.186 394.695V394.695C153.334 394.695 162.371 403.733 162.371 414.881V609.119C162.371 620.267 153.334 629.305 142.186 629.305V629.305C131.037 629.305 122 638.342 122 649.49V765.652C122 829.1 122 860.823 141.301 880.864C141.901 881.487 142.513 882.099 143.136 882.699C163.177 902 194.9 902 258.348 902H374.129C385.487 902 394.695 892.792 394.695 881.434V881.434C394.695 870.075 403.903 860.867 415.262 860.867H608.738C620.097 860.867 629.305 870.075 629.305 881.434V881.434C629.305 892.792 638.513 902 649.871 902H765.652C829.1 902 860.823 902 880.864 882.699C881.487 882.099 882.099 881.487 882.699 880.864C902 860.823 902 829.1 902 765.652V649.871C902 638.513 892.792 629.305 881.434 629.305V629.305C870.075 629.305 860.867 620.097 860.867 608.738V415.262C860.867 403.903 870.075 394.695 881.434 394.695V394.695C892.792 394.695 902 385.487 902 374.129V258.348C902 194.9 902 163.177 882.699 143.136C882.099 142.513 881.487 141.901 880.864 141.301C860.823 122 829.1 122 765.652 122H649.49C638.342 122 629.305 131.037 629.305 142.186V142.186C629.305 153.334 620.267 162.371 609.119 162.371H414.881C403.733 162.371 394.695 153.334 394.695 142.186V142.186Z" fill="#484155" stroke="#484155"/>',
98
+ '<mask id="entity-container-mask" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="121" y="121" width="782" height="782">',
99
+ '<path fill-rule="evenodd" clip-rule="evenodd" d="M394.695 142.186C394.695 131.037 385.658 122 374.51 122H258.348C194.9 122 163.177 122 143.136 141.301C142.513 141.901 141.901 142.513 141.301 143.136C122 163.177 122 194.9 122 258.348V374.51C122 385.658 131.037 394.695 142.186 394.695V394.695C153.334 394.695 162.371 403.733 162.371 414.881V609.119C162.371 620.267 153.334 629.305 142.186 629.305V629.305C131.037 629.305 122 638.342 122 649.49V765.652C122 829.1 122 860.823 141.301 880.864C141.901 881.487 142.513 882.099 143.136 882.699C163.177 902 194.9 902 258.348 902H374.129C385.487 902 394.695 892.792 394.695 881.434V881.434C394.695 870.075 403.903 860.867 415.262 860.867H608.738C620.097 860.867 629.305 870.075 629.305 881.434V881.434C629.305 892.792 638.513 902 649.871 902H765.652C829.1 902 860.823 902 880.864 882.699C881.487 882.099 882.099 881.487 882.699 880.864C902 860.823 902 829.1 902 765.652V649.871C902 638.513 892.792 629.305 881.434 629.305V629.305C870.075 629.305 860.867 620.097 860.867 608.738V415.262C860.867 403.903 870.075 394.695 881.434 394.695V394.695C892.792 394.695 902 385.487 902 374.129V258.348C902 194.9 902 163.177 882.699 143.136C882.099 142.513 881.487 141.901 880.864 141.301C860.823 122 829.1 122 765.652 122H649.49C638.342 122 629.305 131.037 629.305 142.186V142.186C629.305 153.334 620.267 162.371 609.119 162.371H414.881C403.733 162.371 394.695 153.334 394.695 142.186V142.186Z" fill="#253A51" stroke="#253A51"/>',
100
+ '</mask>',
101
+ '<g mask="url(#entity-container-mask)">',
102
+ '<path d="M885.654 130.798L901.062 146.503L901.411 146.86L901.055 147.21L743.055 302.21L742.697 302.561L742.348 302.203L726.941 286.499L726.591 286.142L726.947 285.792L884.947 130.792L885.305 130.441L885.654 130.798Z" fill="#7E7876" stroke="#7E7876"/>',
103
+ '<path d="M885.65 904.198L901.065 888.501L901.416 888.145L901.059 887.794L734.059 723.794L733.702 723.444L733.352 723.801L717.938 739.497L717.587 739.854L717.943 740.204L884.943 904.204L885.301 904.555L885.65 904.198Z" fill="#7E7876" stroke="#7E7876"/>',
104
+ '<path d="M129.352 130.802L113.937 146.499L113.586 146.855L113.943 147.206L280.943 311.206L281.3 311.556L281.65 311.199L297.064 295.503L297.415 295.146L297.059 294.796L130.059 130.796L129.701 130.445L129.352 130.802Z" fill="#7E7876" stroke="#7E7876"/>',
105
+ '<path d="M135.35 898.202L119.939 882.5L119.59 882.143L119.946 881.793L280.946 723.793L281.304 723.442L281.653 723.8L297.062 739.501L297.413 739.858L297.057 740.208L136.057 898.208L135.699 898.559L135.35 898.202Z" fill="#7E7876" stroke="#7E7876"/>',
106
+ '</g>',
107
+ '<path fill-rule="evenodd" clip-rule="evenodd" d="M374.51 109C392.838 109 407.695 123.858 407.695 142.186C407.695 146.154 410.912 149.371 414.881 149.371H609.119C613.088 149.371 616.305 146.154 616.305 142.186C616.305 123.858 631.162 109 649.49 109L766.605 109C797.521 109 822.015 109 841.229 111.523C860.995 114.12 877.059 119.587 889.882 131.937C890.623 132.65 891.35 133.377 892.063 134.118C904.413 146.941 909.88 163.005 912.477 182.771C915 201.984 915 226.479 915 257.394V374.129C915 392.667 899.972 407.695 881.434 407.695C877.255 407.695 873.867 411.083 873.867 415.262V608.738C873.867 612.917 877.255 616.305 881.434 616.305C899.972 616.305 915 631.333 915 649.871V766.606C915 797.521 915 822.016 912.477 841.229C909.88 860.995 904.413 877.059 892.063 889.882C891.35 890.623 890.623 891.35 889.882 892.063C877.059 904.413 860.995 909.88 841.229 912.477C822.016 915 797.521 915 766.606 915H649.871C631.333 915 616.305 899.972 616.305 881.434C616.305 877.255 612.917 873.867 608.738 873.867H415.262C411.083 873.867 407.695 877.255 407.695 881.434C407.695 899.972 392.667 915 374.129 915H257.394C226.479 915 201.984 915 182.771 912.477C163.005 909.88 146.941 904.413 134.118 892.063C133.377 891.35 132.65 890.623 131.937 889.882C119.587 877.059 114.12 860.995 111.523 841.229C109 822.015 109 797.521 109 766.605L109 649.49C109 631.162 123.858 616.305 142.186 616.305C146.154 616.305 149.371 613.088 149.371 609.119V414.881C149.371 410.912 146.154 407.695 142.186 407.695C123.858 407.695 109 392.838 109 374.51L109 257.395C109 226.479 109 201.985 111.523 182.771C114.12 163.005 119.587 146.941 131.937 134.118C132.65 133.377 133.377 132.65 134.118 131.937C146.941 119.587 163.005 114.12 182.771 111.523C201.985 109 226.479 109 257.395 109L374.51 109ZM374.51 122C385.658 122 394.695 131.037 394.695 142.186C394.695 153.334 403.733 162.371 414.881 162.371H609.119C620.267 162.371 629.305 153.334 629.305 142.186C629.305 131.037 638.342 122 649.49 122H765.652C829.1 122 860.823 122 880.864 141.301C881.487 141.901 882.099 142.513 882.699 143.136C902 163.177 902 194.9 902 258.348V374.129C902 385.487 892.792 394.695 881.434 394.695C870.075 394.695 860.867 403.903 860.867 415.262V608.738C860.867 620.097 870.075 629.305 881.434 629.305C892.792 629.305 902 638.513 902 649.871V765.652C902 829.1 902 860.823 882.699 880.864C882.099 881.487 881.487 882.099 880.864 882.699C860.823 902 829.1 902 765.652 902H649.871C638.513 902 629.305 892.792 629.305 881.434C629.305 870.075 620.097 860.867 608.738 860.867H415.262C403.903 860.867 394.695 870.075 394.695 881.434C394.695 892.792 385.487 902 374.129 902H258.348C194.9 902 163.177 902 143.136 882.699C142.513 882.099 141.901 881.487 141.301 880.864C122 860.823 122 829.1 122 765.652V649.49C122 638.342 131.037 629.305 142.186 629.305C153.334 629.305 162.371 620.267 162.371 609.119V414.881C162.371 403.733 153.334 394.695 142.186 394.695C131.037 394.695 122 385.658 122 374.51V258.348C122 194.9 122 163.177 141.301 143.136C141.901 142.513 142.513 141.901 143.136 141.301C163.177 122 194.9 122 258.348 122H374.51Z" fill="black"/>',
108
+ '<path d="M524 240.5C585.268 240.5 631.27 240.499 666.712 245.264C702.168 250.031 727.131 259.576 746.277 278.723C765.424 297.869 774.969 322.832 779.736 358.288C784.501 393.73 784.5 439.732 784.5 501V524C784.5 585.268 784.501 631.27 779.736 666.712C774.969 702.168 765.424 727.131 746.277 746.277C727.131 765.424 702.168 774.969 666.712 779.736C631.27 784.501 585.268 784.5 524 784.5H500C438.732 784.5 392.73 784.501 357.288 779.736C321.832 774.969 296.869 765.424 277.723 746.277C258.576 727.131 249.031 702.168 244.264 666.712C239.499 631.27 239.5 585.268 239.5 524V501C239.5 439.732 239.499 393.73 244.264 358.288C249.031 322.832 258.576 297.869 277.723 278.723C296.869 259.576 321.832 250.031 357.288 245.264C392.73 240.499 438.732 240.5 500 240.5H524ZM500 268.5C437.94 268.5 394.161 268.559 361.019 273.015C328.668 277.364 310.598 285.445 297.521 298.521C284.445 311.598 276.364 329.668 272.015 362.019C267.559 395.161 267.5 438.94 267.5 501V524C267.5 586.06 267.559 629.839 272.015 662.981C276.364 695.332 284.445 713.402 297.521 726.479C310.598 739.555 328.668 747.636 361.019 751.985C394.161 756.441 437.94 756.5 500 756.5H524C586.06 756.5 629.839 756.441 662.981 751.985C695.332 747.636 713.402 739.555 726.479 726.479C739.555 713.402 747.636 695.332 751.985 662.981C756.441 629.839 756.5 586.06 756.5 524V501C756.5 438.94 756.441 395.161 751.985 362.019C747.636 329.668 739.555 311.598 726.479 298.521C713.402 285.445 695.332 277.364 662.981 273.015C629.839 268.559 586.06 268.5 524 268.5H500Z" fill="#7E7876" stroke="#7E7876"/>',
109
+ '<path d="M427.698 404.468C422.231 409.935 413.367 409.935 407.899 404.468V404.468C402.432 399 402.432 390.136 407.899 384.669L444.669 347.899C450.136 342.432 459.001 342.432 464.468 347.899V347.899C469.935 353.366 469.935 362.231 464.468 367.698L427.698 404.468Z" fill="#F4EDAE"/>',
110
+ '<path d="M427.698 696.468C422.231 701.935 413.367 701.935 407.899 696.468V696.468C402.432 691.001 402.432 682.136 407.899 676.669L444.669 639.9C450.136 634.432 459.001 634.432 464.468 639.9V639.9C469.935 645.367 469.935 654.231 464.468 659.699L427.698 696.468Z" fill="#F4EDAE"/>',
111
+ '<path d="M503.698 404.468C498.231 409.935 489.367 409.935 483.899 404.468V404.468C478.432 399 478.432 390.136 483.899 384.669L520.669 347.899C526.136 342.432 535.001 342.432 540.468 347.899V347.899C545.935 353.366 545.935 362.231 540.468 367.698L503.698 404.468Z" fill="#F4EDAE"/>',
112
+ '<path d="M503.698 696.468C498.231 701.935 489.367 701.935 483.899 696.468V696.468C478.432 691.001 478.432 682.136 483.899 676.669L520.669 639.9C526.136 634.432 535.001 634.432 540.468 639.9V639.9C545.935 645.367 545.935 654.231 540.468 659.699L503.698 696.468Z" fill="#F4EDAE"/>',
113
+ '<path d="M579.698 404.468C574.231 409.935 565.367 409.935 559.899 404.468V404.468C554.432 399 554.432 390.136 559.899 384.669L596.669 347.899C602.136 342.432 611.001 342.432 616.468 347.899V347.899C621.935 353.366 621.935 362.231 616.468 367.698L579.698 404.468Z" fill="#F4EDAE"/>',
114
+ '<path d="M579.698 696.468C574.231 701.935 565.367 701.935 559.899 696.468V696.468C554.432 691.001 554.432 682.136 559.899 676.669L596.669 639.9C602.136 634.432 611.001 634.432 616.468 639.9V639.9C621.935 645.367 621.935 654.231 616.468 659.699L579.698 696.468Z" fill="#F4EDAE"/>',
115
+ '<path d="M448 236C448 225.507 458.888 217 472.32 217H551.68C565.112 217 576 225.507 576 236V298C576 308.493 565.112 317 551.68 317H472.32C458.888 317 448 308.493 448 298V236Z" fill="#484155"/>',
116
+ '<path d="M448 726C448 715.507 458.888 707 472.32 707H551.68C565.112 707 576 715.507 576 726V788C576 798.493 565.112 807 551.68 807H472.32C458.888 807 448 798.493 448 788V726Z" fill="#484155"/>',
117
+ '<path d="M211 472.32C211 458.888 219.507 448 230 448H292C302.493 448 311 458.888 311 472.32V551.68C311 565.112 302.493 576 292 576H230C219.507 576 211 565.112 211 551.68V472.32Z" fill="#484155"/>',
118
+ '<path d="M714 472.32C714 458.888 722.507 448 733 448H795C805.493 448 814 458.888 814 472.32V551.68C814 565.112 805.493 576 795 576H733C722.507 576 714 565.112 714 551.68V472.32Z" fill="#484155"/>',
119
+ '<path d="M310 512C310 476.654 332.386 448 360 448H664C691.614 448 714 476.654 714 512V512C714 547.346 691.614 576 664 576H360C332.386 576 310 547.346 310 512V512Z" fill="#F4EDAE"/>',
120
+ '<path fill-rule="evenodd" clip-rule="evenodd" d="M374.51 102C396.704 102 414.695 119.992 414.695 142.186C414.695 142.225 414.699 142.242 414.699 142.244C414.7 142.247 414.701 142.249 414.702 142.252C414.705 142.259 414.718 142.285 414.75 142.317C414.781 142.348 414.807 142.361 414.815 142.365C414.817 142.366 414.819 142.366 414.822 142.367C414.824 142.367 414.842 142.371 414.881 142.371H609.119C609.158 142.371 609.176 142.367 609.178 142.367C609.181 142.366 609.183 142.366 609.185 142.365C609.193 142.361 609.219 142.348 609.25 142.317C609.282 142.285 609.295 142.259 609.298 142.252C609.299 142.249 609.3 142.247 609.301 142.244C609.301 142.242 609.305 142.225 609.305 142.186C609.305 119.992 627.296 102 649.49 102L767.053 102C797.586 101.999 822.468 101.999 842.141 104.583C862.66 107.278 880.406 113.092 894.738 126.895C895.542 127.669 896.331 128.458 897.105 129.262C910.908 143.595 916.722 161.34 919.417 181.859C922.001 201.532 922.001 226.414 922 256.946V374.129C922 396.533 903.838 414.695 881.434 414.695C881.319 414.695 881.259 414.715 881.219 414.732C881.166 414.754 881.099 414.795 881.033 414.861C880.967 414.927 880.926 414.994 880.904 415.047C880.887 415.087 880.867 415.147 880.867 415.262V608.738C880.867 608.853 880.887 608.913 880.904 608.953C880.926 609.006 880.967 609.073 881.033 609.139C881.099 609.205 881.166 609.246 881.219 609.268C881.259 609.285 881.319 609.305 881.434 609.305C903.838 609.305 922 627.467 922 649.871V767.054C922.001 797.586 922.001 822.468 919.417 842.141C916.722 862.66 910.908 880.405 897.105 894.738C896.331 895.542 895.542 896.331 894.738 897.105C880.406 910.908 862.66 916.722 842.141 919.417C822.468 922.001 797.586 922.001 767.054 922H649.871C627.467 922 609.305 903.838 609.305 881.434C609.305 881.318 609.285 881.259 609.268 881.219C609.246 881.166 609.205 881.099 609.139 881.033C609.073 880.967 609.006 880.926 608.953 880.904C608.913 880.887 608.853 880.867 608.738 880.867H415.262C415.147 880.867 415.087 880.887 415.047 880.904C414.994 880.926 414.927 880.967 414.861 881.033C414.795 881.099 414.754 881.166 414.732 881.219C414.723 881.24 414.713 881.267 414.706 881.304C414.7 881.338 414.695 881.379 414.695 881.434C414.695 903.838 396.533 922 374.129 922H256.946C226.414 922.001 201.532 922.001 181.86 919.417C161.34 916.722 143.595 910.908 129.262 897.105C128.458 896.331 127.669 895.542 126.895 894.738C113.092 880.405 107.278 862.66 104.583 842.141C101.999 822.468 101.999 797.586 102 767.053L102 649.49C102 627.296 119.992 609.305 142.186 609.305C142.207 609.305 142.221 609.304 142.231 609.303C142.239 609.302 142.243 609.301 142.244 609.301C142.247 609.3 142.249 609.299 142.252 609.298C142.259 609.295 142.285 609.282 142.317 609.25C142.348 609.219 142.361 609.193 142.365 609.185C142.365 609.184 142.366 609.182 142.366 609.181L142.367 609.178C142.367 609.176 142.369 609.17 142.37 609.158C142.37 609.149 142.371 609.136 142.371 609.119V414.881C142.371 414.842 142.367 414.824 142.367 414.822C142.366 414.819 142.366 414.817 142.365 414.815C142.361 414.807 142.348 414.781 142.317 414.75C142.285 414.718 142.259 414.705 142.252 414.702C142.25 414.701 142.248 414.7 142.246 414.7L142.244 414.699C142.242 414.699 142.225 414.695 142.186 414.695C119.992 414.695 102 396.704 102 374.51L102 256.947C101.999 226.414 101.999 201.532 104.583 181.859C107.278 161.34 113.092 143.595 126.895 129.262C127.669 128.458 128.458 127.669 129.262 126.895C143.595 113.092 161.34 107.278 181.86 104.583C201.532 101.999 226.414 101.999 256.947 102L374.51 102ZM374.51 122C385.658 122 394.695 131.037 394.695 142.186C394.695 153.334 403.733 162.371 414.881 162.371H609.119C620.267 162.371 629.305 153.334 629.305 142.186C629.305 131.037 638.342 122 649.49 122H765.652C829.1 122 860.824 122 880.864 141.301C881.487 141.901 882.099 142.513 882.699 143.136C902 163.177 902 194.9 902 258.348V374.129C902 385.487 892.792 394.695 881.434 394.695C870.075 394.695 860.867 403.903 860.867 415.262V608.738C860.867 620.097 870.075 629.305 881.434 629.305C892.792 629.305 902 638.513 902 649.871V765.652C902 829.1 902 860.823 882.699 880.864C882.099 881.487 881.487 882.099 880.864 882.699C860.824 902 829.1 902 765.652 902H649.871C638.513 902 629.305 892.792 629.305 881.434C629.305 870.075 620.097 860.867 608.738 860.867H415.262C403.903 860.867 394.695 870.075 394.695 881.434C394.695 892.792 385.487 902 374.129 902H258.348C194.9 902 163.177 902 143.136 882.699C142.513 882.099 141.901 881.487 141.301 880.864C122 860.823 122 829.1 122 765.652V649.49C122 638.342 131.037 629.305 142.186 629.305C153.334 629.305 162.371 620.267 162.371 609.119V414.881C162.371 403.733 153.334 394.695 142.186 394.695C131.037 394.695 122 385.658 122 374.51V258.348C122 194.9 122 163.177 141.301 143.136C141.901 142.513 142.513 141.901 143.136 141.301C163.177 122 194.9 122 258.348 122H374.51Z" fill="black" stroke="black"/>',
121
+ '<path d="M512 901C523.598 901 533 891.598 533 880H491C491 891.598 500.402 901 512 901Z" fill="black"/>',
122
+ '<path fill-rule="evenodd" clip-rule="evenodd" d="M364.5 490C357.044 490 351 496.044 351 503.5C351 508.194 347.194 512 342.5 512C337.806 512 334 508.194 334 503.5C334 486.655 347.655 473 364.5 473C369.194 473 373 476.806 373 481.5C373 486.194 369.194 490 364.5 490Z" fill="white" fill-opacity="0.5"/>',
123
+ '<path fill-rule="evenodd" clip-rule="evenodd" d="M652.5 535C659.956 535 666 528.956 666 521.5C666 516.806 669.806 513 674.5 513C679.194 513 683 516.806 683 521.5C683 538.345 669.345 552 652.5 552C647.806 552 644 548.194 644 543.5C644 538.806 647.806 535 652.5 535Z" fill="white" fill-opacity="0.5"/>',
124
+ '<path d="M512 122C500.402 122 491 131.402 491 143H533C533 131.402 523.598 122 512 122Z" fill="black"/>',
125
+ '</g>',
126
+ '<defs>',
127
+ '<clipPath id="entity-container-clip">',
128
+ '<rect width="1024" height="1024" fill="white"/>',
129
+ '</clipPath>',
130
+ '</defs>',
131
+ '</svg>',
132
+ ].join('')
133
+ }
134
+
135
+ const iconBodies: Record<EntityIconSlug, string> = {
136
+ ship: shipIcon(),
137
+ container: containerIcon(),
138
+ }
139
+
140
+ export function entityIconBody(slug: EntityIconSlug): string {
141
+ return iconBodies[slug]
142
+ }
143
+
144
+ export function entityIcon(slug: EntityIconSlug, opts: EntityIconInlineOpts): string {
145
+ const scale = opts.size / 64
146
+ return el(
147
+ 'g',
148
+ {transform: `translate(${opts.x} ${opts.y}) scale(${scale})`, 'data-entity': slug},
149
+ entityIconBody(slug)
150
+ )
151
+ }
152
+
153
+ export function entityIconSvg(slug: EntityIconSlug, opts: EntityIconSvgOpts = {}): string {
154
+ const size = opts.size ?? 64
155
+ const title = opts.title ?? `${entityIconLabels[slug]} entity icon`
156
+ const children = `<title>${escapeXml(title)}</title>${entityIconBody(slug)}`
157
+ return el(
158
+ 'svg',
159
+ {
160
+ xmlns: 'http://www.w3.org/2000/svg',
161
+ width: size,
162
+ height: size,
163
+ viewBox: '0 0 64 64',
164
+ role: 'img',
165
+ class: opts.className,
166
+ 'aria-label': title,
167
+ },
168
+ children
169
+ )
170
+ }
@@ -0,0 +1,297 @@
1
+ import {el, escapeXml} from './svg.ts'
2
+
3
+ export const moduleIconSlugs = [
4
+ 'engine',
5
+ 'generator',
6
+ 'gatherer',
7
+ 'loader',
8
+ 'warp',
9
+ 'crafter',
10
+ 'launcher',
11
+ 'storage',
12
+ 'hauler',
13
+ 'battery',
14
+ 'any',
15
+ ] as const
16
+
17
+ export type ModuleIconSlug = (typeof moduleIconSlugs)[number]
18
+
19
+ export interface ModuleIconSvgOpts {
20
+ size?: number
21
+ title?: string
22
+ className?: string
23
+ }
24
+
25
+ export interface ModuleIconInlineOpts {
26
+ x: number
27
+ y: number
28
+ size: number
29
+ }
30
+
31
+ const moduleIconLabels: Record<ModuleIconSlug, string> = {
32
+ engine: 'Engine',
33
+ generator: 'Power Core',
34
+ gatherer: 'Limpet Bay',
35
+ loader: 'Shuttle Bay',
36
+ warp: 'Warp Drive',
37
+ crafter: 'Fabricator',
38
+ launcher: 'Drive Coil',
39
+ storage: 'Cargo Hold',
40
+ hauler: 'Tractor Beam',
41
+ battery: 'Battery Bank',
42
+ any: 'Any',
43
+ }
44
+
45
+ const moduleIconNames: Record<string, ModuleIconSlug> = {
46
+ engine: 'engine',
47
+ generator: 'generator',
48
+ 'power core': 'generator',
49
+ 'power-core': 'generator',
50
+ gatherer: 'gatherer',
51
+ 'limpet bay': 'gatherer',
52
+ 'limpet-bay': 'gatherer',
53
+ loader: 'loader',
54
+ 'shuttle bay': 'loader',
55
+ 'shuttle-bay': 'loader',
56
+ warp: 'warp',
57
+ 'warp drive': 'warp',
58
+ 'warp-drive': 'warp',
59
+ crafter: 'crafter',
60
+ fabricator: 'crafter',
61
+ launcher: 'launcher',
62
+ 'drive coil': 'launcher',
63
+ 'drive-coil': 'launcher',
64
+ storage: 'storage',
65
+ 'cargo hold': 'storage',
66
+ 'cargo-hold': 'storage',
67
+ hauler: 'hauler',
68
+ 'tractor beam': 'hauler',
69
+ 'tractor-beam': 'hauler',
70
+ battery: 'battery',
71
+ 'battery bank': 'battery',
72
+ 'battery-bank': 'battery',
73
+ any: 'any',
74
+ }
75
+
76
+ function normalizeModuleName(name: string): string {
77
+ return name
78
+ .trim()
79
+ .toLowerCase()
80
+ .replace(/\s*\(?t\d+\)?$/, '')
81
+ }
82
+
83
+ export function moduleIconSlugForName(name: string): ModuleIconSlug | null {
84
+ return moduleIconNames[normalizeModuleName(name)] ?? null
85
+ }
86
+
87
+ export function moduleIconSlugForType(
88
+ moduleType: string | null | undefined
89
+ ): ModuleIconSlug | null {
90
+ return moduleType ? (moduleIconNames[normalizeModuleName(moduleType)] ?? null) : null
91
+ }
92
+
93
+ function engineIcon(): string {
94
+ return [
95
+ '<path d="M88 57 C88 38 106 28 128 28 C150 28 168 38 168 57 L168 139 C168 164 152 181 128 181 C104 181 88 164 88 139 Z" fill="#553686" stroke="#120d1b" stroke-width="10" stroke-linejoin="round"/>',
96
+ '<path d="M98 54 C103 44 114 39 128 39 C142 39 153 44 158 54 L158 69 C148 76 108 76 98 69 Z" fill="#f1daa4" stroke="#120d1b" stroke-width="8" stroke-linejoin="round"/>',
97
+ '<path d="M88 123 C100 136 108 141 128 141 C148 141 156 136 168 123 L168 143 C159 163 144 173 128 173 C112 173 97 163 88 143 Z" fill="#2b2342" stroke="#120d1b" stroke-width="8" stroke-linejoin="round"/>',
98
+ '<path d="M104 139 V163 M128 142 V172 M152 139 V163" stroke="#120d1b" stroke-width="7" stroke-linecap="round"/>',
99
+ '<path d="M97 68 C112 76 144 76 159 68" fill="none" stroke="#7450a7" stroke-width="7" stroke-linecap="round"/>',
100
+ '<path d="M96 161 C94 188 108 213 121 232 L128 207 L135 232 C148 213 162 188 160 161 C149 174 138 185 128 193 C118 185 107 174 96 161 Z" fill="#39e4ef" stroke="#120d1b" stroke-width="9" stroke-linejoin="round"/>',
101
+ '<path d="M105 183 L98 198 M151 183 L158 198" fill="none" stroke="#eefcff" stroke-width="7" stroke-linecap="round"/>',
102
+ ].join('')
103
+ }
104
+
105
+ function generatorIcon(): string {
106
+ return [
107
+ '<path d="M68 63 C68 46 93 37 128 37 C163 37 188 46 188 63 L188 193 C188 210 163 219 128 219 C93 219 68 210 68 193 Z" fill="#493172" stroke="#120d1b" stroke-width="10" stroke-linejoin="round"/>',
108
+ '<path d="M80 58 C88 49 105 44 128 44 C151 44 168 49 176 58 L176 76 C164 86 92 86 80 76 Z" fill="#2b2342" stroke="#120d1b" stroke-width="8" stroke-linejoin="round"/>',
109
+ '<rect x="89" y="80" width="78" height="108" rx="12" fill="#35dfe9" stroke="#120d1b" stroke-width="9"/>',
110
+ '<path d="M136 98 L110 143 H130 L119 175 L148 124 H130 Z" fill="#eefcff" stroke="#35dfe9" stroke-width="4" stroke-linejoin="round"/>',
111
+ '<rect x="47" y="91" width="21" height="78" rx="8" fill="#f1daa4" stroke="#120d1b" stroke-width="8"/>',
112
+ '<rect x="188" y="91" width="21" height="78" rx="8" fill="#f1daa4" stroke="#120d1b" stroke-width="8"/>',
113
+ '<rect x="106" y="27" width="44" height="27" rx="8" fill="#f1daa4" stroke="#120d1b" stroke-width="8"/>',
114
+ '<path d="M80 174 C92 184 164 184 176 174 L176 196 C164 207 92 207 80 196 Z" fill="#2b2342" stroke="#120d1b" stroke-width="8" stroke-linejoin="round"/>',
115
+ ].join('')
116
+ }
117
+
118
+ function gathererIcon(): string {
119
+ return [
120
+ '<path d="M48 102 L76 70 C86 59 101 61 108 72 L93 91 C86 88 77 93 70 103 L70 151 C86 171 106 181 128 181 C150 181 170 171 186 151 L186 103 C179 93 170 88 163 91 L148 72 C155 61 170 59 180 70 L208 102 L208 176 C187 201 159 214 128 214 C97 214 69 201 48 176 Z" fill="#1e61b5" stroke="#120d1b" stroke-width="10" stroke-linejoin="round"/>',
121
+ '<path d="M68 94 L87 71 C94 62 105 65 108 74 L90 103 L90 150 L106 170 C97 177 84 175 76 165 L62 148 L62 106 Z" fill="#f1daa4" stroke="#120d1b" stroke-width="8" stroke-linejoin="round"/>',
122
+ '<path d="M188 94 L169 71 C162 62 151 65 148 74 L166 103 L166 150 L150 170 C159 177 172 175 180 165 L194 148 L194 106 Z" fill="#f1daa4" stroke="#120d1b" stroke-width="8" stroke-linejoin="round"/>',
123
+ '<path d="M73 160 C90 176 108 184 128 184 C148 184 166 176 183 160 L183 187 C166 202 148 210 128 210 C108 210 90 202 73 187 Z" fill="#1f6fc7" stroke="#120d1b" stroke-width="8" stroke-linejoin="round"/>',
124
+ '<path d="M94 103 C96 82 112 72 128 72 C144 72 160 82 162 103 L162 147 C155 162 142 170 128 170 C114 170 101 162 94 147 Z" fill="#332954" stroke="#120d1b" stroke-width="9" stroke-linejoin="round"/>',
125
+ '<ellipse cx="128" cy="93" rx="30" ry="20" fill="#42e2ef" stroke="#120d1b" stroke-width="8"/>',
126
+ '<rect x="116" y="127" width="24" height="34" rx="9" fill="#3ee4ef" stroke="#120d1b" stroke-width="7"/>',
127
+ '<rect x="98" y="182" width="60" height="24" rx="8" fill="#3ee4ef" stroke="#f1daa4" stroke-width="7"/>',
128
+ ].join('')
129
+ }
130
+
131
+ function loaderIcon(): string {
132
+ return [
133
+ '<path d="M51 84 H86 L86 103 H77 V177 L106 208 H75 L48 181 V97 Z" fill="#3f3268" stroke="#120d1b" stroke-width="10" stroke-linejoin="round"/>',
134
+ '<path d="M205 84 H170 L170 103 H179 V177 L150 208 H181 L208 181 V97 Z" fill="#3f3268" stroke="#120d1b" stroke-width="10" stroke-linejoin="round"/>',
135
+ '<path d="M48 84 L72 55 H100 L86 84 Z" fill="#f59416" stroke="#120d1b" stroke-width="9" stroke-linejoin="round"/>',
136
+ '<path d="M208 84 L184 55 H156 L170 84 Z" fill="#f59416" stroke="#120d1b" stroke-width="9" stroke-linejoin="round"/>',
137
+ '<path d="M78 102 H98 V164 H78 Z M158 102 H178 V164 H158 Z" fill="#f1daa4" stroke="#120d1b" stroke-width="7" stroke-linejoin="round"/>',
138
+ '<path d="M93 120 L109 96 H147 L163 120 V184 H93 Z" fill="#6f696d" stroke="#120d1b" stroke-width="9" stroke-linejoin="round"/>',
139
+ '<rect x="105" y="124" width="46" height="48" fill="#302b33" stroke="#120d1b" stroke-width="7"/>',
140
+ '<path d="M111 86 H145 L153 99 V120 H103 V99 Z" fill="#9b9184" stroke="#120d1b" stroke-width="8" stroke-linejoin="round"/>',
141
+ '<rect x="121" y="75" width="14" height="31" rx="4" fill="#f59416" stroke="#120d1b" stroke-width="6"/>',
142
+ '<rect x="75" y="151" width="32" height="35" rx="7" fill="#3f3268" stroke="#120d1b" stroke-width="8"/>',
143
+ '<rect x="149" y="151" width="32" height="35" rx="7" fill="#3f3268" stroke="#120d1b" stroke-width="8"/>',
144
+ '<rect x="84" y="161" width="14" height="20" rx="4" fill="#f59416" stroke="#120d1b" stroke-width="5"/>',
145
+ '<rect x="158" y="161" width="14" height="20" rx="4" fill="#f59416" stroke="#120d1b" stroke-width="5"/>',
146
+ '<rect x="73" y="193" width="52" height="41" rx="9" fill="#f59416" stroke="#120d1b" stroke-width="9"/>',
147
+ '<rect x="131" y="193" width="52" height="41" rx="9" fill="#f59416" stroke="#120d1b" stroke-width="9"/>',
148
+ ].join('')
149
+ }
150
+
151
+ function warpIcon(): string {
152
+ return [
153
+ '<circle cx="128" cy="128" r="94" fill="#3f3268" stroke="#120d1b" stroke-width="10"/>',
154
+ '<circle cx="128" cy="128" r="73" fill="#2b2245" stroke="#120d1b" stroke-width="7"/>',
155
+ '<rect x="108" y="17" width="40" height="22" rx="5" fill="#f59416" stroke="#120d1b" stroke-width="7"/>',
156
+ '<rect x="108" y="217" width="40" height="22" rx="5" fill="#f59416" stroke="#120d1b" stroke-width="7"/>',
157
+ '<rect x="17" y="108" width="22" height="40" rx="5" fill="#f59416" stroke="#120d1b" stroke-width="7"/>',
158
+ '<rect x="217" y="108" width="22" height="40" rx="5" fill="#f59416" stroke="#120d1b" stroke-width="7"/>',
159
+ '<path d="M79 159 C64 119 88 78 128 70 C160 64 183 79 183 104 C183 129 153 144 130 128 C113 116 119 96 143 99" fill="none" stroke="#3ee4ef" stroke-width="17" stroke-linecap="round" stroke-linejoin="round"/>',
160
+ '<path d="M177 97 C192 137 168 178 128 186 C96 192 73 177 73 152 C73 127 103 112 126 128 C143 140 137 160 113 157" fill="none" stroke="#3ee4ef" stroke-width="17" stroke-linecap="round" stroke-linejoin="round"/>',
161
+ '<circle cx="128" cy="128" r="17" fill="#2b2245" stroke="#120d1b" stroke-width="8"/>',
162
+ '<path d="M67 101 C79 74 101 58 128 56 M189 155 C177 182 155 198 128 200" fill="none" stroke="#60448e" stroke-width="7" stroke-linecap="round"/>',
163
+ ].join('')
164
+ }
165
+
166
+ function crafterIcon(): string {
167
+ return [
168
+ '<path d="M60 28 H196 L228 64 V194 L197 228 H59 L28 194 V64 Z" fill="#3f2b63" stroke="#120d1b" stroke-width="10" stroke-linejoin="round"/>',
169
+ '<rect x="101" y="43" width="54" height="18" rx="4" fill="#39e4ef" stroke="#120d1b" stroke-width="5"/>',
170
+ '<circle cx="83" cy="77" r="25" fill="#e72a9a" stroke="#120d1b" stroke-width="8"/>',
171
+ '<circle cx="173" cy="77" r="25" fill="#e72a9a" stroke="#120d1b" stroke-width="8"/>',
172
+ '<circle cx="83" cy="77" r="10" fill="#2c2343" stroke="#120d1b" stroke-width="6"/>',
173
+ '<circle cx="173" cy="77" r="10" fill="#2c2343" stroke="#120d1b" stroke-width="6"/>',
174
+ '<path d="M65 94 H93 L83 147 L54 142 Z" fill="#e72a9a" stroke="#120d1b" stroke-width="8" stroke-linejoin="round"/>',
175
+ '<path d="M191 94 H163 L173 147 L202 142 Z" fill="#e72a9a" stroke="#120d1b" stroke-width="8" stroke-linejoin="round"/>',
176
+ '<path d="M59 142 L83 151 L95 176 L80 185 L65 166 L49 160 Z" fill="#e72a9a" stroke="#120d1b" stroke-width="8" stroke-linejoin="round"/>',
177
+ '<path d="M197 142 L173 151 L161 176 L176 185 L191 166 L207 160 Z" fill="#e72a9a" stroke="#120d1b" stroke-width="8" stroke-linejoin="round"/>',
178
+ '<path d="M128 78 L143 93 H164 V116 L148 128 L164 140 V163 H143 L128 178 L113 163 H92 V140 L108 128 L92 116 V93 H113 Z" fill="#ffcb1f" stroke="#120d1b" stroke-width="9" stroke-linejoin="round"/>',
179
+ '<circle cx="128" cy="128" r="22" fill="#2b2631" stroke="#120d1b" stroke-width="8"/>',
180
+ '<path d="M79 186 L64 203 M177 186 L192 203" stroke="#e72a9a" stroke-width="12" stroke-linecap="round"/>',
181
+ ].join('')
182
+ }
183
+
184
+ function launcherIcon(): string {
185
+ return [
186
+ '<rect x="35" y="78" width="186" height="100" rx="28" fill="#3f3268" stroke="#120d1b" stroke-width="10"/>',
187
+ '<rect x="21" y="93" width="30" height="70" rx="12" fill="#f1daa4" stroke="#120d1b" stroke-width="8"/>',
188
+ '<rect x="205" y="93" width="30" height="70" rx="12" fill="#f1daa4" stroke="#120d1b" stroke-width="8"/>',
189
+ '<path d="M61 78 C51 98 51 158 61 178 H86 C75 155 75 101 86 78 Z" fill="#2d2248" stroke="#120d1b" stroke-width="8" stroke-linejoin="round"/>',
190
+ '<path d="M195 78 C205 98 205 158 195 178 H170 C181 155 181 101 170 78 Z" fill="#2d2248" stroke="#120d1b" stroke-width="8" stroke-linejoin="round"/>',
191
+ '<rect x="86" y="94" width="22" height="68" rx="7" fill="#39e4ef" stroke="#120d1b" stroke-width="6"/>',
192
+ '<rect x="118" y="94" width="22" height="68" rx="7" fill="#39e4ef" stroke="#120d1b" stroke-width="6"/>',
193
+ '<rect x="150" y="94" width="22" height="68" rx="7" fill="#39e4ef" stroke="#120d1b" stroke-width="6"/>',
194
+ '<path d="M89 178 H167" stroke="#6d4c9b" stroke-width="7" stroke-linecap="round"/>',
195
+ ].join('')
196
+ }
197
+
198
+ function storageIcon(): string {
199
+ return [
200
+ '<path d="M35 53 L58 30 H198 L221 53 V198 L199 224 H57 L35 198 Z" fill="#3f3268" stroke="#120d1b" stroke-width="10" stroke-linejoin="round"/>',
201
+ '<path d="M60 58 H196 V189 H60 Z" fill="#161321" stroke="#120d1b" stroke-width="8" stroke-linejoin="round"/>',
202
+ '<rect x="75" y="72" width="50" height="50" rx="9" fill="#f59416" stroke="#120d1b" stroke-width="8"/>',
203
+ '<rect x="132" y="72" width="50" height="50" rx="9" fill="#f59416" stroke="#120d1b" stroke-width="8"/>',
204
+ '<rect x="75" y="132" width="50" height="50" rx="9" fill="#f59416" stroke="#120d1b" stroke-width="8"/>',
205
+ '<rect x="132" y="132" width="50" height="50" rx="9" fill="#f59416" stroke="#120d1b" stroke-width="8"/>',
206
+ '<rect x="87" y="84" width="26" height="26" fill="#ffac2e" stroke="#9a4a13" stroke-width="5"/>',
207
+ '<rect x="144" y="84" width="26" height="26" fill="#ffac2e" stroke="#9a4a13" stroke-width="5"/>',
208
+ '<rect x="87" y="144" width="26" height="26" fill="#ffac2e" stroke="#9a4a13" stroke-width="5"/>',
209
+ '<rect x="144" y="144" width="26" height="26" fill="#ffac2e" stroke="#9a4a13" stroke-width="5"/>',
210
+ '<rect x="86" y="203" width="84" height="25" rx="8" fill="#f1daa4" stroke="#120d1b" stroke-width="8"/>',
211
+ ].join('')
212
+ }
213
+
214
+ function haulerIcon(): string {
215
+ return [
216
+ '<path d="M30 83 L56 57 H105 L128 80 V176 L103 201 H43 L30 188 Z" fill="#3f3268" stroke="#120d1b" stroke-width="10" stroke-linejoin="round"/>',
217
+ '<path d="M99 76 L124 96 V160 L99 181 L80 169 V88 Z" fill="#f1daa4" stroke="#120d1b" stroke-width="8" stroke-linejoin="round"/>',
218
+ '<rect x="49" y="119" width="22" height="54" rx="5" fill="#39e4ef" stroke="#120d1b" stroke-width="7"/>',
219
+ '<path d="M126 105 L210 55 C218 50 227 55 227 64 V192 C227 201 218 206 210 201 L126 151 Z" fill="#35dfe9" fill-opacity=".86" stroke="#120d1b" stroke-width="8" stroke-linejoin="round"/>',
220
+ '<path d="M141 112 L210 72 V184 L141 144 Z" fill="#54edf4" stroke="#9effff" stroke-width="5" stroke-linejoin="round"/>',
221
+ '<path d="M178 96 H216 V160 H178 Z" fill="#f59416" stroke="#120d1b" stroke-width="8" stroke-linejoin="round"/>',
222
+ '<path d="M216 96 L237 110 V174 L216 160 Z" fill="#d76d10" stroke="#120d1b" stroke-width="8" stroke-linejoin="round"/>',
223
+ '<path d="M178 96 L199 82 H237 L216 96 Z" fill="#ffac2e" stroke="#120d1b" stroke-width="8" stroke-linejoin="round"/>',
224
+ ].join('')
225
+ }
226
+
227
+ function batteryIcon(): string {
228
+ return [
229
+ '<path d="M35 54 H221 V198 L199 224 H57 L35 198 Z" fill="#3f3268" stroke="#120d1b" stroke-width="10" stroke-linejoin="round"/>',
230
+ '<path d="M48 77 H208 V188 H48 Z" fill="#15141d" stroke="#120d1b" stroke-width="8" stroke-linejoin="round"/>',
231
+ '<rect x="37" y="48" width="182" height="25" rx="5" fill="#f1daa4" stroke="#120d1b" stroke-width="8"/>',
232
+ '<rect x="68" y="92" width="40" height="82" rx="13" fill="#35dfe9" stroke="#120d1b" stroke-width="8"/>',
233
+ '<rect x="118" y="92" width="40" height="82" rx="13" fill="#35dfe9" stroke="#120d1b" stroke-width="8"/>',
234
+ '<rect x="168" y="92" width="40" height="82" rx="13" fill="#35dfe9" stroke="#120d1b" stroke-width="8"/>',
235
+ '<path d="M78 106 V158 M128 106 V158 M178 106 V158" stroke="#9effff" stroke-width="7" stroke-linecap="round"/>',
236
+ '<path d="M54 188 L72 206 H184 L202 188" fill="none" stroke="#120d1b" stroke-width="8" stroke-linecap="round" stroke-linejoin="round"/>',
237
+ ].join('')
238
+ }
239
+
240
+ function anyIcon(): string {
241
+ return [
242
+ '<path d="M49 74 L83 40 H108 L108 69 L91 86 L91 170 L108 187 L108 216 H82 L49 183 Z" fill="#3f3268" stroke="#120d1b" stroke-width="10" stroke-linejoin="round"/>',
243
+ '<path d="M207 74 L173 40 H148 L148 69 L165 86 L165 170 L148 187 L148 216 H174 L207 183 Z" fill="#3f3268" stroke="#120d1b" stroke-width="10" stroke-linejoin="round"/>',
244
+ '<path d="M84 91 L105 70 L105 186 L84 165 Z" fill="#f1daa4" stroke="#120d1b" stroke-width="8" stroke-linejoin="round"/>',
245
+ '<path d="M172 91 L151 70 L151 186 L172 165 Z" fill="#f1daa4" stroke="#120d1b" stroke-width="8" stroke-linejoin="round"/>',
246
+ '<path d="M128 72 L164 128 L128 184 L92 128 Z" fill="#35dfe9" stroke="#120d1b" stroke-width="9" stroke-linejoin="round"/>',
247
+ '<path d="M128 73 L145 128 L128 183 L111 128 Z" fill="#7af4f8" stroke="#35dfe9" stroke-width="4" stroke-linejoin="round"/>',
248
+ '<path d="M95 128 H161" stroke="#9effff" stroke-width="7" stroke-linecap="round"/>',
249
+ '<path d="M101 217 L113 199 H143 L155 217 V236 H101 Z" fill="#f59416" stroke="#120d1b" stroke-width="8" stroke-linejoin="round"/>',
250
+ ].join('')
251
+ }
252
+
253
+ const iconBodies: Record<ModuleIconSlug, string> = {
254
+ engine: engineIcon(),
255
+ generator: generatorIcon(),
256
+ gatherer: gathererIcon(),
257
+ loader: loaderIcon(),
258
+ warp: warpIcon(),
259
+ crafter: crafterIcon(),
260
+ launcher: launcherIcon(),
261
+ storage: storageIcon(),
262
+ hauler: haulerIcon(),
263
+ battery: batteryIcon(),
264
+ any: anyIcon(),
265
+ }
266
+
267
+ export function moduleIconBody(slug: ModuleIconSlug): string {
268
+ return iconBodies[slug]
269
+ }
270
+
271
+ export function moduleIcon(slug: ModuleIconSlug, opts: ModuleIconInlineOpts): string {
272
+ const scale = opts.size / 256
273
+ return el(
274
+ 'g',
275
+ {transform: `translate(${opts.x} ${opts.y}) scale(${scale})`, 'data-module': slug},
276
+ moduleIconBody(slug)
277
+ )
278
+ }
279
+
280
+ export function moduleIconSvg(slug: ModuleIconSlug, opts: ModuleIconSvgOpts = {}): string {
281
+ const size = opts.size ?? 64
282
+ const title = opts.title ?? `${moduleIconLabels[slug]} module icon`
283
+ const children = `<title>${escapeXml(title)}</title>${moduleIconBody(slug)}`
284
+ return el(
285
+ 'svg',
286
+ {
287
+ xmlns: 'http://www.w3.org/2000/svg',
288
+ width: size,
289
+ height: size,
290
+ viewBox: '0 0 256 256',
291
+ role: 'img',
292
+ class: opts.className,
293
+ 'aria-label': title,
294
+ },
295
+ children
296
+ )
297
+ }
@@ -71,7 +71,7 @@ export function moduleSlot(props: ModuleSlotProps): string {
71
71
  )
72
72
  }
73
73
 
74
- const accent = props.accentColor ?? tokens.colors.text.accent
74
+ const accent = props.accentColor ?? tokens.colors.accent.component
75
75
  const label = `${props.capability ?? 'Module'}: `
76
76
 
77
77
  const desc = props.description
@@ -89,7 +89,7 @@ export function moduleSlot(props: ModuleSlotProps): string {
89
89
  value: label.trimEnd(),
90
90
  size: tokens.typography.sizes.body,
91
91
  weight: 600,
92
- color: tokens.colors.text.primary,
92
+ color: accent,
93
93
  })
94
94
  )
95
95
  }
@@ -99,9 +99,9 @@ export function moduleSlot(props: ModuleSlotProps): string {
99
99
  const combined = label + descPlain
100
100
  const lines = wrapText({value: combined, charsPerLine: 36})
101
101
 
102
- const highlightColor = tokens.colors.text.accent
102
+ const highlightColor = tokens.colors.text.primary
103
103
  const bodyColor = tokens.colors.text.secondary
104
- const labelColor = tokens.colors.text.primary
104
+ const labelColor = accent
105
105
  const size = tokens.typography.sizes.body
106
106
  const fontFamily = escapeXml(tokens.typography.sans)
107
107
  const labelEnd = label.length
@@ -6,11 +6,19 @@ export interface QuantityBadgeProps {
6
6
  x: number
7
7
  y: number
8
8
  quantity: number
9
+ label?: string
10
+ tone: string
9
11
  }
10
12
 
11
- export function quantityBadge({x, y, quantity}: QuantityBadgeProps): string {
12
- if (quantity <= 1) return ''
13
- const label = `×${quantity}`
13
+ export function quantityBadge({
14
+ x,
15
+ y,
16
+ quantity,
17
+ label: labelOverride,
18
+ tone,
19
+ }: QuantityBadgeProps): string {
20
+ if (quantity <= 0) return ''
21
+ const label = labelOverride ?? `×${quantity}`
14
22
  const w = label.length * 7 + 12
15
23
  const h = tokens.spacing.quantityBadgeHeight
16
24
  return (
@@ -21,7 +29,9 @@ export function quantityBadge({x, y, quantity}: QuantityBadgeProps): string {
21
29
  height: h,
22
30
  rx: h / 2,
23
31
  ry: h / 2,
24
- fill: tokens.colors.text.accent,
32
+ fill: tokens.colors.surface.panel,
33
+ stroke: tone,
34
+ 'stroke-width': 1.5,
25
35
  }) +
26
36
  text({
27
37
  x: x - w / 2,
@@ -30,7 +40,7 @@ export function quantityBadge({x, y, quantity}: QuantityBadgeProps): string {
30
40
  size: tokens.typography.sizes.label,
31
41
  weight: 700,
32
42
  family: tokens.typography.mono,
33
- color: tokens.colors.surface.background,
43
+ color: tokens.colors.text.primary,
34
44
  anchor: 'middle',
35
45
  })
36
46
  )