@shipload/item-renderer 1.0.0-next.5 → 1.0.0-next.50
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.
- package/package.json +2 -2
- package/src/index.ts +81 -8
- package/src/links.ts +11 -8
- package/src/meta.ts +1 -1
- package/src/payload/codec.ts +24 -2
- package/src/primitives/component-icon.ts +229 -0
- package/src/primitives/entity-icon.ts +170 -0
- package/src/primitives/module-icon.ts +297 -0
- package/src/primitives/module-slot.ts +4 -4
- package/src/primitives/quantity-badge.ts +15 -5
- package/src/primitives/resource-icon.ts +242 -0
- package/src/primitives/span-paragraph.ts +1 -1
- package/src/primitives/stat-bar.ts +17 -1
- package/src/primitives/stat-stars.ts +36 -0
- package/src/primitives/station-entity-icon.ts +261 -0
- package/src/primitives/text.ts +27 -1
- package/src/render.ts +12 -7
- package/src/templates/_shared.ts +113 -2
- package/src/templates/component.ts +41 -61
- package/src/templates/index.ts +2 -1
- package/src/templates/item-cell.ts +147 -38
- package/src/templates/module.ts +63 -145
- package/src/templates/packed-entity.ts +25 -5
- package/src/templates/resource.ts +40 -68
- package/src/templates/ship-panel.ts +99 -85
- package/src/tokens/colors.ts +13 -13
- package/test/fixtures/cargo-items.ts +10 -3
- package/src/primitives/category-icon.ts +0 -110
- package/src/primitives/compact-row.ts +0 -38
package/src/templates/module.ts
CHANGED
|
@@ -1,23 +1,31 @@
|
|
|
1
1
|
import type {ResolvedItem} from '@shipload/sdk'
|
|
2
|
-
import {describeModuleForItem,
|
|
2
|
+
import {describeModuleForItem, formatLocation, renderDescription} from '@shipload/sdk'
|
|
3
3
|
import type {CargoItem} from '../payload/codec.ts'
|
|
4
4
|
import {panel} from '../primitives/panel.ts'
|
|
5
5
|
import {iconHex} from '../primitives/icon-hex.ts'
|
|
6
|
+
import {moduleIcon, moduleIconSlugForName} from '../primitives/module-icon.ts'
|
|
6
7
|
import {text} from '../primitives/text.ts'
|
|
7
|
-
import {divider} from '../primitives/divider.ts'
|
|
8
|
-
import {compactRow} from '../primitives/compact-row.ts'
|
|
9
8
|
import {quantityBadge} from '../primitives/quantity-badge.ts'
|
|
10
9
|
import {spanParagraph} from '../primitives/span-paragraph.ts'
|
|
11
10
|
import {tokens} from '../tokens/index.ts'
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
import {
|
|
12
|
+
shortCode,
|
|
13
|
+
formatMass,
|
|
14
|
+
tierBorder,
|
|
15
|
+
metaRowBlock,
|
|
16
|
+
titleText,
|
|
17
|
+
capabilityColor,
|
|
18
|
+
BADGE_Y,
|
|
19
|
+
HEADER_H,
|
|
20
|
+
ICON_Y,
|
|
21
|
+
META_BLOCK_GAP,
|
|
22
|
+
CAP_HEADER_H,
|
|
23
|
+
BODY_TAIL,
|
|
24
|
+
} from './_shared.ts'
|
|
18
25
|
|
|
19
26
|
export interface RenderModuleOpts {
|
|
20
27
|
mode?: 'values' | 'ranges'
|
|
28
|
+
location?: {x: number; y: number}
|
|
21
29
|
}
|
|
22
30
|
|
|
23
31
|
export function renderModule(
|
|
@@ -31,162 +39,72 @@ export function renderModule(
|
|
|
31
39
|
const innerW = w - pad * 2
|
|
32
40
|
|
|
33
41
|
const group = resolved.attributes?.[0]
|
|
34
|
-
const attrs = group?.attributes ?? []
|
|
35
42
|
const desc = mode === 'values' ? describeModuleForItem(resolved) : undefined
|
|
36
43
|
|
|
37
44
|
const capabilityName = group?.capability ?? resolved.name.replace(/\s+T\d+$/i, '')
|
|
38
45
|
|
|
39
|
-
const headerH = 48
|
|
40
|
-
const metaRowH = 28
|
|
41
|
-
const sepY = pad + headerH + metaRowH + 6
|
|
42
|
-
|
|
43
|
-
let bodyHeight = 0
|
|
44
|
-
if (mode === 'ranges') {
|
|
45
|
-
bodyHeight = 20 + 8
|
|
46
|
-
} else if (desc && group) {
|
|
47
|
-
const plain = renderDescription(desc)
|
|
48
|
-
.map((s) => s.text)
|
|
49
|
-
.join('')
|
|
50
|
-
const lines = plain.split(/\s+/).reduce(
|
|
51
|
-
(acc, word) => {
|
|
52
|
-
const last = acc[acc.length - 1] ?? ''
|
|
53
|
-
if (last.length === 0) return [...acc.slice(0, -1), word]
|
|
54
|
-
if (last.length + 1 + word.length <= 36)
|
|
55
|
-
return [...acc.slice(0, -1), `${last} ${word}`]
|
|
56
|
-
return [...acc, word]
|
|
57
|
-
},
|
|
58
|
-
['']
|
|
59
|
-
)
|
|
60
|
-
const lineCount = lines.filter((l) => l.length > 0).length
|
|
61
|
-
bodyHeight = 20 + lineCount * 14 + 8
|
|
62
|
-
} else if (group && attrs.length > 0) {
|
|
63
|
-
const capHeaderH = 22
|
|
64
|
-
const attrsH = attrs.length * 18
|
|
65
|
-
bodyHeight = capHeaderH + attrsH + 8
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const height = headerH + metaRowH + 14 + bodyHeight + pad
|
|
69
|
-
|
|
70
|
-
const chrome = panel({width: w, height, borderColor: tierBorder(resolved.tier)})
|
|
71
|
-
|
|
72
46
|
const quantity = Number(BigInt(item.quantity.toString()))
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
x: pad,
|
|
78
|
-
y: pad + 4,
|
|
79
|
-
color: iconColor,
|
|
80
|
-
code: shortCode(resolved.itemId),
|
|
81
|
-
})
|
|
47
|
+
const metaRows = [
|
|
48
|
+
{label: 'Mass', value: formatMass(resolved.mass * Math.max(quantity, 1))},
|
|
49
|
+
...(opts?.location ? [{label: 'Location', value: formatLocation(opts.location)}] : []),
|
|
50
|
+
]
|
|
82
51
|
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
value: resolved.name,
|
|
87
|
-
size: tokens.typography.sizes.title,
|
|
88
|
-
weight: 700,
|
|
89
|
-
family: tokens.typography.display,
|
|
90
|
-
})
|
|
52
|
+
const metaYStart = pad + HEADER_H
|
|
53
|
+
const {svg: metaSvg, height: metaH} = metaRowBlock(pad, metaYStart, innerW, metaRows)
|
|
54
|
+
const bodyYStart = metaYStart + metaH + META_BLOCK_GAP
|
|
91
55
|
|
|
92
|
-
const
|
|
93
|
-
x: pad,
|
|
94
|
-
y: pad + headerH + 4,
|
|
95
|
-
value: 'Type',
|
|
96
|
-
size: tokens.typography.sizes.body,
|
|
97
|
-
color: tokens.colors.text.secondary,
|
|
98
|
-
})
|
|
99
|
-
const subtitleValue = text({
|
|
100
|
-
x: w - pad,
|
|
101
|
-
y: pad + headerH + 4,
|
|
102
|
-
value: `MODULE · ${formatTier(resolved.tier)}`,
|
|
103
|
-
size: tokens.typography.sizes.body,
|
|
104
|
-
weight: 600,
|
|
105
|
-
anchor: 'end',
|
|
106
|
-
})
|
|
56
|
+
const iconColor = group ? capabilityColor(group.capability) : capabilityColor(capabilityName)
|
|
107
57
|
|
|
108
|
-
const
|
|
58
|
+
const capLabel = (group?.capability ?? capabilityName).toUpperCase()
|
|
59
|
+
const capHeader = text({
|
|
109
60
|
x: pad,
|
|
110
|
-
y:
|
|
111
|
-
value:
|
|
112
|
-
size: tokens.typography.sizes.
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
y: pad + headerH + metaRowH - 8,
|
|
118
|
-
value: formatMass(resolved.mass),
|
|
119
|
-
size: tokens.typography.sizes.body,
|
|
120
|
-
weight: 600,
|
|
121
|
-
anchor: 'end',
|
|
61
|
+
y: bodyYStart + 16,
|
|
62
|
+
value: capLabel,
|
|
63
|
+
size: tokens.typography.sizes.subtitle,
|
|
64
|
+
weight: 700,
|
|
65
|
+
family: tokens.typography.sans,
|
|
66
|
+
color: iconColor,
|
|
67
|
+
letterSpacing: 1,
|
|
122
68
|
})
|
|
123
69
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (mode === 'ranges') {
|
|
128
|
-
const accentColor = capabilityColor(capabilityName)
|
|
129
|
-
capSection = text({
|
|
130
|
-
x: pad,
|
|
131
|
-
y: sepY + 16,
|
|
132
|
-
value: capabilityName.toUpperCase(),
|
|
133
|
-
size: tokens.typography.sizes.subtitle,
|
|
134
|
-
weight: 700,
|
|
135
|
-
family: tokens.typography.sans,
|
|
136
|
-
color: accentColor,
|
|
137
|
-
letterSpacing: 1,
|
|
138
|
-
})
|
|
139
|
-
} else if (desc && group) {
|
|
140
|
-
const accentColor = capabilityColor(group.capability)
|
|
141
|
-
const capHeader = text({
|
|
142
|
-
x: pad,
|
|
143
|
-
y: sepY + 16,
|
|
144
|
-
value: group.capability.toUpperCase(),
|
|
145
|
-
size: tokens.typography.sizes.subtitle,
|
|
146
|
-
weight: 700,
|
|
147
|
-
family: tokens.typography.sans,
|
|
148
|
-
color: accentColor,
|
|
149
|
-
letterSpacing: 1,
|
|
150
|
-
})
|
|
70
|
+
let bodyHeight: number
|
|
71
|
+
let capSection: string
|
|
72
|
+
if (mode === 'values' && desc && group) {
|
|
151
73
|
const spans = renderDescription(desc)
|
|
152
|
-
const {svg: paraSvg} = spanParagraph({
|
|
74
|
+
const {svg: paraSvg, lineCount} = spanParagraph({
|
|
153
75
|
x: pad,
|
|
154
|
-
y:
|
|
76
|
+
y: bodyYStart + 36,
|
|
155
77
|
spans,
|
|
156
78
|
charsPerLine: 36,
|
|
157
79
|
lineHeight: 14,
|
|
80
|
+
highlightColor: tokens.colors.text.primary,
|
|
158
81
|
})
|
|
82
|
+
bodyHeight = CAP_HEADER_H + lineCount * 14 + BODY_TAIL
|
|
159
83
|
capSection = capHeader + paraSvg
|
|
160
|
-
} else
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
x: pad,
|
|
164
|
-
y: capY,
|
|
165
|
-
value: group.capability.toUpperCase(),
|
|
166
|
-
size: 10,
|
|
167
|
-
weight: 700,
|
|
168
|
-
family: tokens.typography.sans,
|
|
169
|
-
color: capabilityColor(group.capability),
|
|
170
|
-
letterSpacing: 0.8,
|
|
171
|
-
})
|
|
172
|
-
|
|
173
|
-
const attrRows = attrs
|
|
174
|
-
.map((attr, i) => {
|
|
175
|
-
const displayValue = String(attr.value)
|
|
176
|
-
return compactRow({
|
|
177
|
-
x: pad,
|
|
178
|
-
y: capY + 14 + i * 18,
|
|
179
|
-
width: innerW,
|
|
180
|
-
label: attr.label,
|
|
181
|
-
value: displayValue,
|
|
182
|
-
})
|
|
183
|
-
})
|
|
184
|
-
.join('')
|
|
185
|
-
|
|
186
|
-
capSection = capHeader + attrRows
|
|
84
|
+
} else {
|
|
85
|
+
bodyHeight = CAP_HEADER_H + BODY_TAIL
|
|
86
|
+
capSection = capHeader
|
|
187
87
|
}
|
|
188
88
|
|
|
189
|
-
const
|
|
89
|
+
const height = bodyYStart + bodyHeight + pad
|
|
90
|
+
|
|
91
|
+
const chrome = panel({width: w, height, borderColor: tierBorder(resolved.tier)})
|
|
92
|
+
|
|
93
|
+
const badge = quantityBadge({x: w - pad, y: pad + BADGE_Y, quantity, tone: iconColor})
|
|
94
|
+
|
|
95
|
+
const moduleSlug = moduleIconSlugForName(resolved.name)
|
|
96
|
+
const icon = moduleSlug
|
|
97
|
+
? moduleIcon(moduleSlug, {x: pad, y: pad + ICON_Y - 2, size: 28})
|
|
98
|
+
: iconHex({
|
|
99
|
+
x: pad,
|
|
100
|
+
y: pad + ICON_Y,
|
|
101
|
+
color: iconColor,
|
|
102
|
+
code: shortCode(resolved.itemId),
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
const name = titleText(pad + 34, pad + 22, resolved)
|
|
106
|
+
|
|
107
|
+
const inner = `${chrome}${icon}${name}${badge}${metaSvg}${capSection}`
|
|
190
108
|
|
|
191
109
|
return `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${height}" viewBox="0 0 ${w} ${height}">${inner}</svg>`
|
|
192
110
|
}
|
|
@@ -1,27 +1,47 @@
|
|
|
1
1
|
import type {ResolvedItem, ResolvedModuleSlot} from '@shipload/sdk'
|
|
2
|
-
import {describeModuleForSlot, renderDescription} from '@shipload/sdk'
|
|
2
|
+
import {baseName, describeModuleForSlot, renderDescription} from '@shipload/sdk'
|
|
3
3
|
import type {CargoItem} from '../payload/codec.ts'
|
|
4
4
|
import {renderShipPanel, type ShipPanelSlot} from './ship-panel.ts'
|
|
5
5
|
|
|
6
|
+
function capabilityFromName(name: string): string {
|
|
7
|
+
return name.replace(/\s+T\d+\s*$/i, '').trim()
|
|
8
|
+
}
|
|
9
|
+
|
|
6
10
|
function slotToPanelSlot(slot: ResolvedModuleSlot): ShipPanelSlot {
|
|
7
11
|
if (!slot.installed || !slot.attributes || !slot.name) {
|
|
8
12
|
return {installed: false}
|
|
9
13
|
}
|
|
14
|
+
const capability = capabilityFromName(slot.name)
|
|
10
15
|
const desc = describeModuleForSlot(slot)
|
|
11
16
|
if (desc) {
|
|
12
|
-
return {
|
|
17
|
+
return {
|
|
18
|
+
name: slot.name,
|
|
19
|
+
installed: true,
|
|
20
|
+
capability,
|
|
21
|
+
description: renderDescription(desc),
|
|
22
|
+
}
|
|
13
23
|
}
|
|
14
24
|
const shorthand = slot.attributes.map((a) => `${a.value} ${a.label.toLowerCase()}`).join(' · ')
|
|
15
|
-
return {name: slot.name, installed: true, description: shorthand}
|
|
25
|
+
return {name: slot.name, installed: true, capability, description: shorthand}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface RenderPackedEntityOpts {
|
|
29
|
+
mode?: 'values' | 'ranges'
|
|
30
|
+
location?: {x: number; y: number}
|
|
16
31
|
}
|
|
17
32
|
|
|
18
|
-
export function renderPackedEntity(
|
|
33
|
+
export function renderPackedEntity(
|
|
34
|
+
item: CargoItem,
|
|
35
|
+
resolved: ResolvedItem,
|
|
36
|
+
opts?: RenderPackedEntityOpts
|
|
37
|
+
): string {
|
|
19
38
|
const quantity = Number(BigInt(item.quantity.toString()))
|
|
20
39
|
const slots = (resolved.moduleSlots ?? []).map(slotToPanelSlot)
|
|
21
40
|
return renderShipPanel({
|
|
22
|
-
name:
|
|
41
|
+
name: baseName(resolved),
|
|
23
42
|
tier: resolved.tier,
|
|
24
43
|
quantity,
|
|
44
|
+
location: opts?.location,
|
|
25
45
|
attributes: resolved.attributes ?? [],
|
|
26
46
|
slots,
|
|
27
47
|
})
|
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
import type {ResolvedItem} from '@shipload/sdk'
|
|
2
|
-
import {getStatDefinitions, categoryColors,
|
|
2
|
+
import {getStatDefinitions, categoryColors, formatLocation, starsForStat} from '@shipload/sdk'
|
|
3
3
|
import type {CargoItem} from '../payload/codec.ts'
|
|
4
4
|
import {panel} from '../primitives/panel.ts'
|
|
5
|
-
import {
|
|
6
|
-
import {text} from '../primitives/text.ts'
|
|
7
|
-
import {divider} from '../primitives/divider.ts'
|
|
5
|
+
import {resourceIcon} from '../primitives/resource-icon.ts'
|
|
8
6
|
import {statBar} from '../primitives/stat-bar.ts'
|
|
9
7
|
import {quantityBadge} from '../primitives/quantity-badge.ts'
|
|
10
8
|
import {tokens} from '../tokens/index.ts'
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
9
|
+
import {
|
|
10
|
+
formatMass,
|
|
11
|
+
tierBorder,
|
|
12
|
+
metaRowBlock,
|
|
13
|
+
titleText,
|
|
14
|
+
BADGE_Y,
|
|
15
|
+
HEADER_H,
|
|
16
|
+
ICON_Y,
|
|
17
|
+
STAT_BLOCK_GAP,
|
|
18
|
+
STAT_ROW_H,
|
|
19
|
+
BOTTOM_PAD,
|
|
20
|
+
} from './_shared.ts'
|
|
20
21
|
|
|
21
22
|
function categoryColor(category?: string): string {
|
|
22
23
|
if (!category) return tokens.colors.text.muted
|
|
@@ -26,6 +27,7 @@ function categoryColor(category?: string): string {
|
|
|
26
27
|
|
|
27
28
|
export interface RenderResourceOpts {
|
|
28
29
|
mode?: 'values' | 'ranges'
|
|
30
|
+
location?: {x: number; y: number}
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
type StatRow = {
|
|
@@ -69,83 +71,53 @@ export function renderResource(
|
|
|
69
71
|
}))
|
|
70
72
|
}
|
|
71
73
|
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const height = headerH + metaRowH + 14 + statsH + pad
|
|
74
|
+
const metaRows = [
|
|
75
|
+
...(opts?.location ? [{label: 'Location', value: formatLocation(opts.location)}] : []),
|
|
76
|
+
]
|
|
76
77
|
|
|
77
|
-
const
|
|
78
|
+
const metaYStart = pad + HEADER_H
|
|
79
|
+
const {svg: metaSvg, height: metaH} = metaRowBlock(pad, metaYStart, innerW, metaRows)
|
|
80
|
+
const statsYStart = metaYStart + metaH + STAT_BLOCK_GAP
|
|
81
|
+
const statsBottom =
|
|
82
|
+
statsYStart + Math.max(0, rows.length - 1) * STAT_ROW_H + tokens.spacing.statBarHeight
|
|
83
|
+
const height = statsBottom + BOTTOM_PAD
|
|
78
84
|
|
|
79
|
-
const
|
|
80
|
-
const badge = quantityBadge({x: w - pad, y: pad, quantity})
|
|
81
|
-
|
|
82
|
-
const icon = iconHex({
|
|
83
|
-
x: pad,
|
|
84
|
-
y: pad + 4,
|
|
85
|
-
color: categoryColor(resolved.category),
|
|
86
|
-
code: shortCode(resolved.itemId),
|
|
87
|
-
})
|
|
85
|
+
const chrome = panel({width: w, height, borderColor: tierBorder(resolved.tier)})
|
|
88
86
|
|
|
89
|
-
const
|
|
90
|
-
x: pad + 34,
|
|
91
|
-
y: pad + 22,
|
|
92
|
-
value: displayName(resolved),
|
|
93
|
-
size: tokens.typography.sizes.title,
|
|
94
|
-
weight: 700,
|
|
95
|
-
family: tokens.typography.display,
|
|
96
|
-
})
|
|
87
|
+
const identity = categoryColor(resolved.category)
|
|
97
88
|
|
|
98
|
-
const
|
|
99
|
-
const
|
|
100
|
-
x: pad,
|
|
101
|
-
y: pad + headerH + 4,
|
|
102
|
-
value: 'Category',
|
|
103
|
-
size: tokens.typography.sizes.body,
|
|
104
|
-
color: tokens.colors.text.secondary,
|
|
105
|
-
})
|
|
106
|
-
const catValue = text({
|
|
107
|
-
x: w - pad,
|
|
108
|
-
y: pad + headerH + 4,
|
|
109
|
-
value: catLabel,
|
|
110
|
-
size: tokens.typography.sizes.body,
|
|
111
|
-
weight: 600,
|
|
112
|
-
anchor: 'end',
|
|
113
|
-
})
|
|
114
|
-
const massLabel = text({
|
|
115
|
-
x: pad,
|
|
116
|
-
y: pad + headerH + metaRowH - 8,
|
|
117
|
-
value: 'Mass',
|
|
118
|
-
size: tokens.typography.sizes.body,
|
|
119
|
-
color: tokens.colors.text.secondary,
|
|
120
|
-
})
|
|
121
|
-
const massValue = text({
|
|
89
|
+
const quantity = Number(BigInt(item.quantity.toString()))
|
|
90
|
+
const badge = quantityBadge({
|
|
122
91
|
x: w - pad,
|
|
123
|
-
y: pad +
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
anchor: 'end',
|
|
92
|
+
y: pad + BADGE_Y,
|
|
93
|
+
quantity,
|
|
94
|
+
label: formatMass(quantity * resolved.mass),
|
|
95
|
+
tone: identity,
|
|
128
96
|
})
|
|
129
97
|
|
|
130
|
-
const
|
|
131
|
-
|
|
98
|
+
const icon = resolved.category
|
|
99
|
+
? resourceIcon(resolved.category, {x: pad, y: pad + ICON_Y - 2, size: 28})
|
|
100
|
+
: ''
|
|
101
|
+
|
|
102
|
+
const name = titleText(pad + 34, pad + 22, resolved)
|
|
132
103
|
|
|
133
104
|
const statsSvg = rows
|
|
134
105
|
.map((row, i) =>
|
|
135
106
|
statBar({
|
|
136
107
|
x: pad,
|
|
137
|
-
y:
|
|
108
|
+
y: statsYStart + i * STAT_ROW_H,
|
|
138
109
|
width: innerW,
|
|
139
110
|
label: row.label,
|
|
140
111
|
abbreviation: row.abbreviation,
|
|
141
112
|
value: row.value,
|
|
142
113
|
color: row.color,
|
|
143
114
|
inverted: row.inverted,
|
|
115
|
+
stars: row.value !== null ? starsForStat(row.value) : undefined,
|
|
144
116
|
})
|
|
145
117
|
)
|
|
146
118
|
.join('')
|
|
147
119
|
|
|
148
|
-
const inner = `${chrome}${icon}${name}${badge}${
|
|
120
|
+
const inner = `${chrome}${icon}${name}${badge}${metaSvg}${statsSvg}`
|
|
149
121
|
|
|
150
122
|
return `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${height}" viewBox="0 0 ${w} ${height}">${inner}</svg>`
|
|
151
123
|
}
|