@shipload/item-renderer 0.2.2 → 1.0.0-beta1

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 (88) hide show
  1. package/package.json +47 -49
  2. package/src/assets/stardust-base64.ts +1 -1
  3. package/src/errors.ts +14 -14
  4. package/src/fonts/index.ts +23 -24
  5. package/src/fonts/load-bun.ts +11 -11
  6. package/src/index.ts +28 -28
  7. package/src/links.ts +11 -11
  8. package/src/meta.ts +25 -25
  9. package/src/payload/base64url.ts +21 -21
  10. package/src/payload/codec.ts +17 -17
  11. package/src/primitives/category-icon.ts +90 -67
  12. package/src/primitives/compact-row.ts +32 -32
  13. package/src/primitives/divider.ts +14 -14
  14. package/src/primitives/icon-hex.ts +36 -34
  15. package/src/primitives/module-slot.ts +131 -131
  16. package/src/primitives/panel.ts +18 -18
  17. package/src/primitives/quantity-badge.ts +32 -32
  18. package/src/primitives/span-paragraph.ts +55 -57
  19. package/src/primitives/stat-bar.ts +71 -71
  20. package/src/primitives/svg.ts +15 -15
  21. package/src/primitives/text.ts +33 -33
  22. package/src/primitives/wrap.ts +19 -19
  23. package/src/render.ts +21 -25
  24. package/src/templates/_shared.ts +5 -6
  25. package/src/templates/component.ts +123 -121
  26. package/src/templates/index.ts +23 -23
  27. package/src/templates/item-cell.ts +84 -81
  28. package/src/templates/module.ts +177 -174
  29. package/src/templates/packed-entity.ts +22 -24
  30. package/src/templates/resource.ts +134 -134
  31. package/src/templates/ship-panel.ts +120 -121
  32. package/src/templates/social-card.ts +28 -26
  33. package/src/tokens/colors.ts +38 -38
  34. package/src/tokens/index.ts +5 -5
  35. package/src/tokens/spacing.ts +8 -8
  36. package/src/tokens/typography.ts +17 -17
  37. package/.github/workflows/ci.yml +0 -14
  38. package/.gitignore +0 -6
  39. package/Makefile +0 -50
  40. package/biome.json +0 -18
  41. package/bun.lock +0 -123
  42. package/scripts/check-bundle-size.ts +0 -37
  43. package/scripts/copy-fonts.ts +0 -41
  44. package/scripts/preview.ts +0 -43
  45. package/test/__image_snapshots__/.gitkeep +0 -0
  46. package/test/__image_snapshots__/component-hull-plates.diff.png +0 -0
  47. package/test/__image_snapshots__/component-hull-plates.png +0 -0
  48. package/test/__image_snapshots__/module-engine-t1.diff.png +0 -0
  49. package/test/__image_snapshots__/module-engine-t1.png +0 -0
  50. package/test/__image_snapshots__/module-storage-t1.diff.png +0 -0
  51. package/test/__image_snapshots__/module-storage-t1.png +0 -0
  52. package/test/__image_snapshots__/packed-entity-ship-t1-only-engine.diff.png +0 -0
  53. package/test/__image_snapshots__/packed-entity-ship-t1-only-engine.png +0 -0
  54. package/test/__image_snapshots__/packed-entity-ship-t1-two-modules.diff.png +0 -0
  55. package/test/__image_snapshots__/packed-entity-ship-t1-two-modules.png +0 -0
  56. package/test/__image_snapshots__/resource-ore-t1.diff.png +0 -0
  57. package/test/__image_snapshots__/resource-ore-t1.png +0 -0
  58. package/test/__snapshots__/templates-component.test.ts.snap +0 -5
  59. package/test/__snapshots__/templates-item-cell.test.ts.snap +0 -9
  60. package/test/__snapshots__/templates-module.test.ts.snap +0 -17
  61. package/test/__snapshots__/templates-packed-entity.test.ts.snap +0 -5
  62. package/test/__snapshots__/templates-resource.test.ts.snap +0 -7
  63. package/test/base64url.test.ts +0 -33
  64. package/test/codec.test.ts +0 -43
  65. package/test/errors.test.ts +0 -24
  66. package/test/fixtures/cargo-items.ts +0 -122
  67. package/test/fonts.test.ts +0 -28
  68. package/test/links-meta.test.ts +0 -43
  69. package/test/pixel.test.ts +0 -66
  70. package/test/primitives-category-icon.test.ts +0 -79
  71. package/test/primitives-compact-row.test.ts +0 -44
  72. package/test/primitives-domain.test.ts +0 -72
  73. package/test/primitives-layout.test.ts +0 -56
  74. package/test/primitives-module-slot.test.ts +0 -88
  75. package/test/render.test.ts +0 -40
  76. package/test/sanity.test.ts +0 -6
  77. package/test/sdk-link.test.ts +0 -19
  78. package/test/snapshots/.gitkeep +0 -0
  79. package/test/svg.test.ts +0 -28
  80. package/test/templates-component.test.ts +0 -36
  81. package/test/templates-dispatch.test.ts +0 -35
  82. package/test/templates-item-cell.test.ts +0 -94
  83. package/test/templates-module.test.ts +0 -63
  84. package/test/templates-packed-entity.test.ts +0 -47
  85. package/test/templates-resource.test.ts +0 -71
  86. package/test/templates-ship-panel.test.ts +0 -87
  87. package/test/tokens.test.ts +0 -32
  88. package/tsconfig.json +0 -20
@@ -1,24 +1,24 @@
1
- import { el } from './svg.ts'
2
- import { tokens } from '../tokens/index.ts'
1
+ import {el} from './svg.ts'
2
+ import {tokens} from '../tokens/index.ts'
3
3
 
4
4
  export interface PanelProps {
5
- width: number
6
- height: number
7
- borderColor?: string
5
+ width: number
6
+ height: number
7
+ borderColor?: string
8
8
  }
9
9
 
10
10
  export function panel(props: PanelProps): string {
11
- const { width, height, borderColor } = props
12
- const r = tokens.spacing.cornerRadius
13
- return el('rect', {
14
- x: 0.5,
15
- y: 0.5,
16
- width: width - 1,
17
- height: height - 1,
18
- rx: r,
19
- ry: r,
20
- fill: tokens.colors.surface.panel,
21
- stroke: borderColor ?? tokens.colors.surface.panelBorder,
22
- 'stroke-width': 1,
23
- })
11
+ const {width, height, borderColor} = props
12
+ const r = tokens.spacing.cornerRadius
13
+ return el('rect', {
14
+ x: 0.5,
15
+ y: 0.5,
16
+ width: width - 1,
17
+ height: height - 1,
18
+ rx: r,
19
+ ry: r,
20
+ fill: tokens.colors.surface.panel,
21
+ stroke: borderColor ?? tokens.colors.surface.panelBorder,
22
+ 'stroke-width': 1,
23
+ })
24
24
  }
@@ -1,37 +1,37 @@
1
- import { el } from './svg.ts'
2
- import { text } from './text.ts'
3
- import { tokens } from '../tokens/index.ts'
1
+ import {el} from './svg.ts'
2
+ import {text} from './text.ts'
3
+ import {tokens} from '../tokens/index.ts'
4
4
 
5
5
  export interface QuantityBadgeProps {
6
- x: number
7
- y: number
8
- quantity: number
6
+ x: number
7
+ y: number
8
+ quantity: number
9
9
  }
10
10
 
11
- export function quantityBadge({ x, y, quantity }: QuantityBadgeProps): string {
12
- if (quantity <= 1) return ''
13
- const label = `×${quantity}`
14
- const w = label.length * 7 + 12
15
- const h = tokens.spacing.quantityBadgeHeight
16
- return (
17
- el('rect', {
18
- x: x - w,
19
- y,
20
- width: w,
21
- height: h,
22
- rx: h / 2,
23
- ry: h / 2,
24
- fill: tokens.colors.text.accent,
25
- }) +
26
- text({
27
- x: x - w / 2,
28
- y: y + h / 2 + 4,
29
- value: label,
30
- size: tokens.typography.sizes.label,
31
- weight: 700,
32
- family: tokens.typography.mono,
33
- color: tokens.colors.surface.background,
34
- anchor: 'middle',
35
- })
36
- )
11
+ export function quantityBadge({x, y, quantity}: QuantityBadgeProps): string {
12
+ if (quantity <= 1) return ''
13
+ const label = `×${quantity}`
14
+ const w = label.length * 7 + 12
15
+ const h = tokens.spacing.quantityBadgeHeight
16
+ return (
17
+ el('rect', {
18
+ x: x - w,
19
+ y,
20
+ width: w,
21
+ height: h,
22
+ rx: h / 2,
23
+ ry: h / 2,
24
+ fill: tokens.colors.text.accent,
25
+ }) +
26
+ text({
27
+ x: x - w / 2,
28
+ y: y + h / 2 + 4,
29
+ value: label,
30
+ size: tokens.typography.sizes.label,
31
+ weight: 700,
32
+ family: tokens.typography.mono,
33
+ color: tokens.colors.surface.background,
34
+ anchor: 'middle',
35
+ })
36
+ )
37
37
  }
@@ -1,72 +1,70 @@
1
- import { wrapText } from './wrap.ts'
2
- import { escapeXml as escapeAttr } from './svg.ts'
3
- import { tokens } from '../tokens/index.ts'
4
- import type { TextSpan } from '@shipload/sdk'
1
+ import {wrapText} from './wrap.ts'
2
+ import {escapeXml as escapeAttr} from './svg.ts'
3
+ import {tokens} from '../tokens/index.ts'
4
+ import type {TextSpan} from '@shipload/sdk'
5
5
 
6
6
  export interface SpanParagraphProps {
7
- x: number
8
- y: number
9
- spans: TextSpan[]
10
- charsPerLine?: number
11
- lineHeight?: number
12
- bodyColor?: string
13
- highlightColor?: string
14
- fontSize?: number
7
+ x: number
8
+ y: number
9
+ spans: TextSpan[]
10
+ charsPerLine?: number
11
+ lineHeight?: number
12
+ bodyColor?: string
13
+ highlightColor?: string
14
+ fontSize?: number
15
15
  }
16
16
 
17
17
  function escapeXml(s: string): string {
18
- return s
19
- .replace(/&/g, '&amp;')
20
- .replace(/</g, '&lt;')
21
- .replace(/>/g, '&gt;')
22
- .replace(/"/g, '&quot;')
18
+ return s
19
+ .replace(/&/g, '&amp;')
20
+ .replace(/</g, '&lt;')
21
+ .replace(/>/g, '&gt;')
22
+ .replace(/"/g, '&quot;')
23
23
  }
24
24
 
25
25
  function sliceSpans(spans: TextSpan[], start: number, end: number): TextSpan[] {
26
- const out: TextSpan[] = []
27
- let cursor = 0
28
- for (const span of spans) {
29
- const spanStart = cursor
30
- const spanEnd = cursor + span.text.length
31
- cursor = spanEnd
32
- if (spanEnd <= start || spanStart >= end) continue
33
- const sliceStart = Math.max(0, start - spanStart)
34
- const sliceEnd = span.text.length - Math.max(0, spanEnd - end)
35
- const txt = span.text.slice(sliceStart, sliceEnd)
36
- if (txt.length === 0) continue
37
- out.push(span.highlight ? { text: txt, highlight: true } : { text: txt })
38
- }
39
- return out
26
+ const out: TextSpan[] = []
27
+ let cursor = 0
28
+ for (const span of spans) {
29
+ const spanStart = cursor
30
+ const spanEnd = cursor + span.text.length
31
+ cursor = spanEnd
32
+ if (spanEnd <= start || spanStart >= end) continue
33
+ const sliceStart = Math.max(0, start - spanStart)
34
+ const sliceEnd = span.text.length - Math.max(0, spanEnd - end)
35
+ const txt = span.text.slice(sliceStart, sliceEnd)
36
+ if (txt.length === 0) continue
37
+ out.push(span.highlight ? {text: txt, highlight: true} : {text: txt})
38
+ }
39
+ return out
40
40
  }
41
41
 
42
- export function spanParagraph(
43
- props: SpanParagraphProps,
44
- ): { svg: string; lineCount: number } {
45
- const chars = props.charsPerLine ?? 36
46
- const lh = props.lineHeight ?? 14
47
- const bodyColor = props.bodyColor ?? tokens.colors.text.secondary
48
- const highlightColor = props.highlightColor ?? tokens.colors.text.accent
49
- const size = props.fontSize ?? tokens.typography.sizes.body
42
+ export function spanParagraph(props: SpanParagraphProps): {svg: string; lineCount: number} {
43
+ const chars = props.charsPerLine ?? 36
44
+ const lh = props.lineHeight ?? 14
45
+ const bodyColor = props.bodyColor ?? tokens.colors.text.secondary
46
+ const highlightColor = props.highlightColor ?? tokens.colors.text.accent
47
+ const size = props.fontSize ?? tokens.typography.sizes.body
50
48
 
51
- const plain = props.spans.map((s) => s.text).join('')
52
- const lines = wrapText({ value: plain, charsPerLine: chars })
49
+ const plain = props.spans.map((s) => s.text).join('')
50
+ const lines = wrapText({value: plain, charsPerLine: chars})
53
51
 
54
- let charOffset = 0
55
- const out = lines
56
- .map((line, i) => {
57
- const lineStart = charOffset
58
- const lineEnd = lineStart + line.length
59
- charOffset = lineEnd + 1
60
- const lineSpans = sliceSpans(props.spans, lineStart, lineEnd)
61
- const y = props.y + i * lh
62
- const tspans = lineSpans
63
- .map((s) => {
64
- const fill = s.highlight ? highlightColor : bodyColor
65
- return `<tspan fill="${fill}">${escapeXml(s.text)}</tspan>`
52
+ let charOffset = 0
53
+ const out = lines
54
+ .map((line, i) => {
55
+ const lineStart = charOffset
56
+ const lineEnd = lineStart + line.length
57
+ charOffset = lineEnd + 1
58
+ const lineSpans = sliceSpans(props.spans, lineStart, lineEnd)
59
+ const y = props.y + i * lh
60
+ const tspans = lineSpans
61
+ .map((s) => {
62
+ const fill = s.highlight ? highlightColor : bodyColor
63
+ return `<tspan fill="${fill}">${escapeXml(s.text)}</tspan>`
64
+ })
65
+ .join('')
66
+ return `<text x="${props.x}" y="${y}" font-family="${escapeAttr(tokens.typography.sans)}" font-size="${size}">${tspans}</text>`
66
67
  })
67
68
  .join('')
68
- return `<text x="${props.x}" y="${y}" font-family="${escapeAttr(tokens.typography.sans)}" font-size="${size}">${tspans}</text>`
69
- })
70
- .join('')
71
- return { svg: out, lineCount: lines.length }
69
+ return {svg: out, lineCount: lines.length}
72
70
  }
@@ -1,85 +1,85 @@
1
- import { el } from './svg.ts'
2
- import { text } from './text.ts'
3
- import { tokens } from '../tokens/index.ts'
1
+ import {el} from './svg.ts'
2
+ import {text} from './text.ts'
3
+ import {tokens} from '../tokens/index.ts'
4
4
 
5
5
  export interface StatBarProps {
6
- x: number
7
- y: number
8
- width: number
9
- label: string
10
- abbreviation: string
11
- value: number | null // 0..1023, or null for ranges mode (no value text, no fill)
12
- color: string
13
- inverted?: boolean
6
+ x: number
7
+ y: number
8
+ width: number
9
+ label: string
10
+ abbreviation: string
11
+ value: number | null // 0..1023, or null for ranges mode (no value text, no fill)
12
+ color: string
13
+ inverted?: boolean
14
14
  }
15
15
 
16
16
  export function statBar({
17
- x,
18
- y,
19
- width,
20
- label,
21
- abbreviation,
22
- value,
23
- color,
24
- inverted,
25
- }: StatBarProps): string {
26
- const h = tokens.spacing.statBarHeight
27
-
28
- let labelOut =
29
- text({
30
- x,
31
- y: y - 6,
32
- value: abbreviation,
33
- size: tokens.typography.sizes.label,
34
- weight: 700,
35
- family: tokens.typography.mono,
36
- color,
37
- }) +
38
- text({
39
- x: x + 22,
40
- y: y - 6,
41
- value: label,
42
- size: tokens.typography.sizes.stat,
43
- weight: 400,
44
- color: tokens.colors.text.primary,
45
- })
46
-
47
- const track = el('rect', {
48
17
  x,
49
18
  y,
50
19
  width,
51
- height: h,
52
- rx: h / 2,
53
- ry: h / 2,
54
- fill: tokens.colors.surface.panelBorder,
55
- })
20
+ label,
21
+ abbreviation,
22
+ value,
23
+ color,
24
+ inverted,
25
+ }: StatBarProps): string {
26
+ const h = tokens.spacing.statBarHeight
56
27
 
57
- if (value !== null) {
58
- const clamped = Math.max(0, Math.min(1023, value))
59
- const displayFraction = inverted ? 1 - clamped / 1023 : clamped / 1023
60
- const filled = Math.floor(width * displayFraction)
28
+ let labelOut =
29
+ text({
30
+ x,
31
+ y: y - 6,
32
+ value: abbreviation,
33
+ size: tokens.typography.sizes.label,
34
+ weight: 700,
35
+ family: tokens.typography.mono,
36
+ color,
37
+ }) +
38
+ text({
39
+ x: x + 22,
40
+ y: y - 6,
41
+ value: label,
42
+ size: tokens.typography.sizes.stat,
43
+ weight: 400,
44
+ color: tokens.colors.text.primary,
45
+ })
61
46
 
62
- labelOut += text({
63
- x: x + width,
64
- y: y - 6,
65
- value: String(clamped),
66
- size: tokens.typography.sizes.statValue,
67
- weight: 700,
68
- color,
69
- anchor: 'end',
47
+ const track = el('rect', {
48
+ x,
49
+ y,
50
+ width,
51
+ height: h,
52
+ rx: h / 2,
53
+ ry: h / 2,
54
+ fill: tokens.colors.surface.panelBorder,
70
55
  })
71
56
 
72
- const bar = el('rect', {
73
- x,
74
- y,
75
- width: filled,
76
- height: h,
77
- rx: h / 2,
78
- ry: h / 2,
79
- fill: color,
80
- })
81
- return labelOut + track + bar
82
- }
57
+ if (value !== null) {
58
+ const clamped = Math.max(0, Math.min(1023, value))
59
+ const displayFraction = inverted ? 1 - clamped / 1023 : clamped / 1023
60
+ const filled = Math.floor(width * displayFraction)
61
+
62
+ labelOut += text({
63
+ x: x + width,
64
+ y: y - 6,
65
+ value: String(clamped),
66
+ size: tokens.typography.sizes.statValue,
67
+ weight: 700,
68
+ color,
69
+ anchor: 'end',
70
+ })
71
+
72
+ const bar = el('rect', {
73
+ x,
74
+ y,
75
+ width: filled,
76
+ height: h,
77
+ rx: h / 2,
78
+ ry: h / 2,
79
+ fill: color,
80
+ })
81
+ return labelOut + track + bar
82
+ }
83
83
 
84
- return labelOut + track
84
+ return labelOut + track
85
85
  }
@@ -1,25 +1,25 @@
1
1
  export function escapeXml(input: string): string {
2
- return input
3
- .replaceAll('&', '&amp;')
4
- .replaceAll('<', '&lt;')
5
- .replaceAll('>', '&gt;')
6
- .replaceAll('"', '&quot;')
7
- .replaceAll("'", '&apos;')
2
+ return input
3
+ .replaceAll('&', '&amp;')
4
+ .replaceAll('<', '&lt;')
5
+ .replaceAll('>', '&gt;')
6
+ .replaceAll('"', '&quot;')
7
+ .replaceAll("'", '&apos;')
8
8
  }
9
9
 
10
10
  export type AttrValue = string | number | null | undefined
11
11
 
12
12
  export function attr(attrs: Record<string, AttrValue>): string {
13
- let out = ''
14
- for (const [k, v] of Object.entries(attrs)) {
15
- if (v === undefined || v === null) continue
16
- const value = typeof v === 'number' ? String(v) : escapeXml(v)
17
- out += ` ${k}="${value}"`
18
- }
19
- return out
13
+ let out = ''
14
+ for (const [k, v] of Object.entries(attrs)) {
15
+ if (v === undefined || v === null) continue
16
+ const value = typeof v === 'number' ? String(v) : escapeXml(v)
17
+ out += ` ${k}="${value}"`
18
+ }
19
+ return out
20
20
  }
21
21
 
22
22
  export function el(tag: string, attrs: Record<string, AttrValue>, children?: string): string {
23
- if (children === undefined) return `<${tag}${attr(attrs)}/>`
24
- return `<${tag}${attr(attrs)}>${children}</${tag}>`
23
+ if (children === undefined) return `<${tag}${attr(attrs)}/>`
24
+ return `<${tag}${attr(attrs)}>${children}</${tag}>`
25
25
  }
@@ -1,42 +1,42 @@
1
- import { el } from './svg.ts'
2
- import { tokens } from '../tokens/index.ts'
1
+ import {el} from './svg.ts'
2
+ import {tokens} from '../tokens/index.ts'
3
3
 
4
4
  export interface TextProps {
5
- x: number
6
- y: number
7
- value: string
8
- size?: number
9
- weight?: 400 | 600 | 700 | 500
10
- family?: string
11
- color?: string
12
- anchor?: 'start' | 'middle' | 'end'
13
- letterSpacing?: number
14
- dominantBaseline?: 'auto' | 'middle' | 'central' | 'hanging' | 'text-top' | 'text-bottom'
5
+ x: number
6
+ y: number
7
+ value: string
8
+ size?: number
9
+ weight?: 400 | 600 | 700 | 500
10
+ family?: string
11
+ color?: string
12
+ anchor?: 'start' | 'middle' | 'end'
13
+ letterSpacing?: number
14
+ dominantBaseline?: 'auto' | 'middle' | 'central' | 'hanging' | 'text-top' | 'text-bottom'
15
15
  }
16
16
 
17
17
  export function text(props: TextProps): string {
18
- return el(
19
- 'text',
20
- {
21
- x: props.x,
22
- y: props.y,
23
- 'font-family': props.family ?? tokens.typography.sans,
24
- 'font-size': props.size ?? tokens.typography.sizes.body,
25
- 'font-weight': props.weight ?? 400,
26
- fill: props.color ?? tokens.colors.text.primary,
27
- 'text-anchor': props.anchor,
28
- 'letter-spacing': props.letterSpacing,
29
- 'dominant-baseline': props.dominantBaseline,
30
- },
31
- escapeValue(props.value),
32
- )
18
+ return el(
19
+ 'text',
20
+ {
21
+ x: props.x,
22
+ y: props.y,
23
+ 'font-family': props.family ?? tokens.typography.sans,
24
+ 'font-size': props.size ?? tokens.typography.sizes.body,
25
+ 'font-weight': props.weight ?? 400,
26
+ fill: props.color ?? tokens.colors.text.primary,
27
+ 'text-anchor': props.anchor,
28
+ 'letter-spacing': props.letterSpacing,
29
+ 'dominant-baseline': props.dominantBaseline,
30
+ },
31
+ escapeValue(props.value)
32
+ )
33
33
  }
34
34
 
35
35
  function escapeValue(v: string): string {
36
- return v
37
- .replaceAll('&', '&amp;')
38
- .replaceAll('<', '&lt;')
39
- .replaceAll('>', '&gt;')
40
- .replaceAll('"', '&quot;')
41
- .replaceAll("'", '&apos;')
36
+ return v
37
+ .replaceAll('&', '&amp;')
38
+ .replaceAll('<', '&lt;')
39
+ .replaceAll('>', '&gt;')
40
+ .replaceAll('"', '&quot;')
41
+ .replaceAll("'", '&apos;')
42
42
  }
@@ -1,24 +1,24 @@
1
1
  export interface WrapProps {
2
- value: string
3
- charsPerLine: number
2
+ value: string
3
+ charsPerLine: number
4
4
  }
5
5
 
6
- export function wrapText({ value, charsPerLine }: WrapProps): string[] {
7
- const words = value.split(/\s+/).filter((w) => w.length > 0)
8
- const lines: string[] = []
9
- let current = ''
10
- for (const word of words) {
11
- if (current.length === 0) {
12
- current = word
13
- continue
6
+ export function wrapText({value, charsPerLine}: WrapProps): string[] {
7
+ const words = value.split(/\s+/).filter((w) => w.length > 0)
8
+ const lines: string[] = []
9
+ let current = ''
10
+ for (const word of words) {
11
+ if (current.length === 0) {
12
+ current = word
13
+ continue
14
+ }
15
+ if (current.length + 1 + word.length <= charsPerLine) {
16
+ current += ` ${word}`
17
+ } else {
18
+ lines.push(current)
19
+ current = word
20
+ }
14
21
  }
15
- if (current.length + 1 + word.length <= charsPerLine) {
16
- current += ` ${word}`
17
- } else {
18
- lines.push(current)
19
- current = word
20
- }
21
- }
22
- if (current.length > 0) lines.push(current)
23
- return lines
22
+ if (current.length > 0) lines.push(current)
23
+ return lines
24
24
  }
package/src/render.ts CHANGED
@@ -1,33 +1,29 @@
1
- import { resolveItem, type ResolvedItem } from '@shipload/sdk'
2
- import type { CargoItem } from './payload/codec.ts'
3
- import { decodePayload } from './payload/codec.ts'
4
- import { renderByType } from './templates/index.ts'
5
- import { UnknownItemError } from './errors.ts'
1
+ import {resolveItem, type ResolvedItem} from '@shipload/sdk'
2
+ import type {CargoItem} from './payload/codec.ts'
3
+ import {decodePayload} from './payload/codec.ts'
4
+ import {renderByType} from './templates/index.ts'
5
+ import {UnknownItemError} from './errors.ts'
6
6
 
7
7
  export interface RenderOptions {
8
- width?: number
9
- theme?: 'dark' | 'light'
8
+ width?: number
9
+ theme?: 'dark' | 'light'
10
10
  }
11
11
 
12
- export function renderItem(
13
- item: CargoItem,
14
- resolved: ResolvedItem,
15
- _opts?: RenderOptions,
16
- ): string {
17
- return renderByType(item, resolved)
12
+ export function renderItem(item: CargoItem, resolved: ResolvedItem, _opts?: RenderOptions): string {
13
+ return renderByType(item, resolved)
18
14
  }
19
15
 
20
16
  export async function renderFromPayload(
21
- payload: string,
22
- opts?: RenderOptions,
23
- ): Promise<{ svg: string; item: ResolvedItem }> {
24
- const cargoItem = decodePayload(payload)
25
- let resolved: ResolvedItem
26
- try {
27
- resolved = resolveItem(cargoItem.item_id, cargoItem.stats, cargoItem.modules)
28
- } catch {
29
- throw new UnknownItemError(Number(BigInt(cargoItem.item_id.toString())))
30
- }
31
- const svg = renderItem(cargoItem, resolved, opts)
32
- return { svg, item: resolved }
17
+ payload: string,
18
+ opts?: RenderOptions
19
+ ): Promise<{svg: string; item: ResolvedItem}> {
20
+ const cargoItem = decodePayload(payload)
21
+ let resolved: ResolvedItem
22
+ try {
23
+ resolved = resolveItem(cargoItem.item_id, cargoItem.stats, cargoItem.modules)
24
+ } catch {
25
+ throw new UnknownItemError(Number(BigInt(cargoItem.item_id.toString())))
26
+ }
27
+ const svg = renderItem(cargoItem, resolved, opts)
28
+ return {svg, item: resolved}
33
29
  }
@@ -1,15 +1,14 @@
1
- import { tokens } from '../tokens/index.ts'
1
+ import {tokens} from '../tokens/index.ts'
2
2
 
3
3
  export function formatMass(n: number): string {
4
- return n.toLocaleString('en-US')
4
+ return n.toLocaleString('en-US')
5
5
  }
6
6
 
7
7
  export function tierBorder(tier: number): string {
8
- const key = `t${tier}` as keyof typeof tokens.colors.tier
9
- return tokens.colors.tier[key] ?? tokens.colors.surface.panelBorder
8
+ return tokens.colors.tier[tier] ?? tokens.colors.surface.panelBorder
10
9
  }
11
10
 
12
11
  export function shortCode(itemId: number): string {
13
- const str = itemId.toString(10)
14
- return str.slice(-2).padStart(2, '0')
12
+ const str = itemId.toString(10)
13
+ return str.slice(-2).padStart(2, '0')
15
14
  }