@shipload/item-renderer 0.2.1 → 0.2.3

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 (71) hide show
  1. package/.claude/settings.local.json +6 -0
  2. package/bun.lock +2 -2
  3. package/package.json +8 -4
  4. package/scripts/check-bundle-size.ts +21 -21
  5. package/scripts/copy-fonts.ts +19 -19
  6. package/scripts/preview.ts +13 -15
  7. package/src/assets/stardust-base64.ts +1 -1
  8. package/src/errors.ts +8 -8
  9. package/src/fonts/index.ts +25 -26
  10. package/src/fonts/load-bun.ts +9 -9
  11. package/src/index.ts +21 -21
  12. package/src/links.ts +11 -11
  13. package/src/meta.ts +16 -16
  14. package/src/payload/base64url.ts +16 -16
  15. package/src/payload/codec.ts +13 -13
  16. package/src/primitives/category-icon.ts +69 -48
  17. package/src/primitives/compact-row.ts +13 -13
  18. package/src/primitives/divider.ts +9 -9
  19. package/src/primitives/icon-hex.ts +18 -16
  20. package/src/primitives/module-slot.ts +73 -73
  21. package/src/primitives/panel.ts +10 -10
  22. package/src/primitives/quantity-badge.ts +13 -13
  23. package/src/primitives/span-paragraph.ts +48 -50
  24. package/src/primitives/stat-bar.ts +24 -24
  25. package/src/primitives/svg.ts +13 -13
  26. package/src/primitives/text.ts +25 -25
  27. package/src/primitives/wrap.ts +12 -12
  28. package/src/render.ts +15 -19
  29. package/src/templates/_shared.ts +6 -7
  30. package/src/templates/component.ts +68 -63
  31. package/src/templates/index.ts +17 -17
  32. package/src/templates/item-cell.ts +48 -41
  33. package/src/templates/module.ts +84 -83
  34. package/src/templates/packed-entity.ts +12 -14
  35. package/src/templates/resource.ts +63 -65
  36. package/src/templates/ship-panel.ts +67 -72
  37. package/src/templates/social-card.ts +27 -25
  38. package/src/tokens/colors.ts +29 -29
  39. package/src/tokens/index.ts +6 -6
  40. package/src/tokens/spacing.ts +1 -1
  41. package/src/tokens/typography.ts +1 -1
  42. package/test/__image_snapshots__/component-hull-plates.diff.png +0 -0
  43. package/test/__image_snapshots__/module-engine-t1.diff.png +0 -0
  44. package/test/__image_snapshots__/module-storage-t1.diff.png +0 -0
  45. package/test/__image_snapshots__/packed-entity-ship-t1-only-engine.diff.png +0 -0
  46. package/test/__image_snapshots__/packed-entity-ship-t1-two-modules.diff.png +0 -0
  47. package/test/__image_snapshots__/resource-ore-t1.diff.png +0 -0
  48. package/test/base64url.test.ts +22 -22
  49. package/test/codec.test.ts +26 -35
  50. package/test/errors.test.ts +21 -21
  51. package/test/fixtures/cargo-items.ts +43 -43
  52. package/test/fonts.test.ts +23 -23
  53. package/test/links-meta.test.ts +37 -37
  54. package/test/pixel.test.ts +44 -41
  55. package/test/primitives-category-icon.test.ts +74 -67
  56. package/test/primitives-compact-row.test.ts +29 -29
  57. package/test/primitives-domain.test.ts +61 -50
  58. package/test/primitives-layout.test.ts +47 -47
  59. package/test/primitives-module-slot.test.ts +58 -58
  60. package/test/render.test.ts +38 -35
  61. package/test/sanity.test.ts +5 -5
  62. package/test/sdk-link.test.ts +13 -13
  63. package/test/svg.test.ts +24 -22
  64. package/test/templates-component.test.ts +32 -32
  65. package/test/templates-dispatch.test.ts +29 -29
  66. package/test/templates-item-cell.test.ts +79 -79
  67. package/test/templates-module.test.ts +52 -52
  68. package/test/templates-packed-entity.test.ts +42 -42
  69. package/test/templates-resource.test.ts +61 -61
  70. package/test/templates-ship-panel.test.ts +69 -65
  71. package/test/tokens.test.ts +28 -26
@@ -1,90 +1,88 @@
1
- import type { ResolvedItem } from '@shipload/sdk'
2
- import { getStatDefinitions, categoryColors, displayName } from '@shipload/sdk'
3
- import type { CargoItem } from '../payload/codec.ts'
4
- import { panel } from '../primitives/panel.ts'
5
- import { iconHex } from '../primitives/icon-hex.ts'
6
- import { text } from '../primitives/text.ts'
7
- import { divider } from '../primitives/divider.ts'
8
- import { statBar } from '../primitives/stat-bar.ts'
9
- import { quantityBadge } from '../primitives/quantity-badge.ts'
10
- import { tokens } from '../tokens/index.ts'
11
- import { shortCode, formatMass, tierBorder } from './_shared.ts'
1
+ import type { ResolvedItem } from "@shipload/sdk";
2
+ import { getStatDefinitions, categoryColors, displayName } from "@shipload/sdk";
3
+ import type { CargoItem } from "../payload/codec.ts";
4
+ import { panel } from "../primitives/panel.ts";
5
+ import { iconHex } from "../primitives/icon-hex.ts";
6
+ import { text } from "../primitives/text.ts";
7
+ import { divider } from "../primitives/divider.ts";
8
+ import { statBar } from "../primitives/stat-bar.ts";
9
+ import { quantityBadge } from "../primitives/quantity-badge.ts";
10
+ import { tokens } from "../tokens/index.ts";
11
+ import { shortCode, formatMass, tierBorder } from "./_shared.ts";
12
12
 
13
13
  const CATEGORY_LABELS: Record<string, string> = {
14
- ore: 'Ore',
15
- crystal: 'Crystal',
16
- gas: 'Gas',
17
- regolith: 'Regolith',
18
- biomass: 'Biomass',
19
- }
14
+ ore: "Ore",
15
+ crystal: "Crystal",
16
+ gas: "Gas",
17
+ regolith: "Regolith",
18
+ biomass: "Biomass",
19
+ };
20
20
 
21
21
  function categoryColor(category?: string): string {
22
- if (!category) return tokens.colors.text.muted
23
- const key = category as keyof typeof tokens.colors.category
24
- return tokens.colors.category[key] ?? tokens.colors.text.muted
22
+ if (!category) return tokens.colors.text.muted;
23
+ const key = category as keyof typeof tokens.colors.category;
24
+ return tokens.colors.category[key] ?? tokens.colors.text.muted;
25
25
  }
26
26
 
27
27
  export interface RenderResourceOpts {
28
- mode?: 'values' | 'ranges'
28
+ mode?: "values" | "ranges";
29
29
  }
30
30
 
31
31
  type StatRow = {
32
- label: string
33
- abbreviation: string
34
- value: number | null
35
- color: string
36
- inverted?: boolean
37
- }
32
+ label: string;
33
+ abbreviation: string;
34
+ value: number | null;
35
+ color: string;
36
+ inverted?: boolean;
37
+ };
38
38
 
39
39
  export function renderResource(
40
40
  item: CargoItem,
41
41
  resolved: ResolvedItem,
42
42
  opts?: RenderResourceOpts,
43
43
  ): string {
44
- const mode = opts?.mode ?? 'values'
45
- const w = tokens.spacing.panelWidth
46
- const pad = tokens.spacing.panelPadding
47
- const innerW = w - pad * 2
48
-
49
- let rows: StatRow[]
50
- if (mode === 'values') {
51
- rows = (resolved.stats ?? []).map(s => ({
44
+ const mode = opts?.mode ?? "values";
45
+ const w = tokens.spacing.panelWidth;
46
+ const pad = tokens.spacing.panelPadding;
47
+ const innerW = w - pad * 2;
48
+
49
+ let rows: StatRow[];
50
+ if (mode === "values") {
51
+ rows = (resolved.stats ?? []).map((s) => ({
52
52
  label: s.label,
53
53
  abbreviation: s.abbreviation,
54
54
  value: s.value,
55
55
  color: s.color,
56
56
  inverted: s.inverted,
57
- }))
57
+ }));
58
58
  } else {
59
- const defs = resolved.category ? getStatDefinitions(resolved.category) : []
60
- const color = resolved.category
61
- ? categoryColors[resolved.category]
62
- : tokens.colors.text.muted
63
- rows = defs.map(d => ({
59
+ const defs = resolved.category ? getStatDefinitions(resolved.category) : [];
60
+ const color = resolved.category ? categoryColors[resolved.category] : tokens.colors.text.muted;
61
+ rows = defs.map((d) => ({
64
62
  label: d.label,
65
63
  abbreviation: d.abbreviation,
66
64
  value: null,
67
65
  color,
68
66
  inverted: d.inverted,
69
- }))
67
+ }));
70
68
  }
71
69
 
72
- const headerH = 48
73
- const metaRowH = 28
74
- const statsH = rows.length * 26 + 8
75
- const height = headerH + metaRowH + 14 + statsH + pad
70
+ const headerH = 48;
71
+ const metaRowH = 28;
72
+ const statsH = rows.length * 26 + 8;
73
+ const height = headerH + metaRowH + 14 + statsH + pad;
76
74
 
77
- const chrome = panel({ width: w, height, borderColor: tierBorder(resolved.tier) })
75
+ const chrome = panel({ width: w, height, borderColor: tierBorder(resolved.tier) });
78
76
 
79
- const quantity = Number(BigInt(item.quantity.toString()))
80
- const badge = quantityBadge({ x: w - pad, y: pad, quantity })
77
+ const quantity = Number(BigInt(item.quantity.toString()));
78
+ const badge = quantityBadge({ x: w - pad, y: pad, quantity });
81
79
 
82
80
  const icon = iconHex({
83
81
  x: pad,
84
82
  y: pad + 4,
85
83
  color: categoryColor(resolved.category),
86
84
  code: shortCode(resolved.itemId),
87
- })
85
+ });
88
86
 
89
87
  const name = text({
90
88
  x: pad + 34,
@@ -93,42 +91,42 @@ export function renderResource(
93
91
  size: tokens.typography.sizes.title,
94
92
  weight: 700,
95
93
  family: tokens.typography.display,
96
- })
94
+ });
97
95
 
98
- const catLabel = resolved.category ? (CATEGORY_LABELS[resolved.category] ?? 'Item') : 'Item'
96
+ const catLabel = resolved.category ? (CATEGORY_LABELS[resolved.category] ?? "Item") : "Item";
99
97
  const catText = text({
100
98
  x: pad,
101
99
  y: pad + headerH + 4,
102
- value: 'Category',
100
+ value: "Category",
103
101
  size: tokens.typography.sizes.body,
104
102
  color: tokens.colors.text.secondary,
105
- })
103
+ });
106
104
  const catValue = text({
107
105
  x: w - pad,
108
106
  y: pad + headerH + 4,
109
107
  value: catLabel,
110
108
  size: tokens.typography.sizes.body,
111
109
  weight: 600,
112
- anchor: 'end',
113
- })
110
+ anchor: "end",
111
+ });
114
112
  const massLabel = text({
115
113
  x: pad,
116
114
  y: pad + headerH + metaRowH - 8,
117
- value: 'Mass',
115
+ value: "Mass",
118
116
  size: tokens.typography.sizes.body,
119
117
  color: tokens.colors.text.secondary,
120
- })
118
+ });
121
119
  const massValue = text({
122
120
  x: w - pad,
123
121
  y: pad + headerH + metaRowH - 8,
124
122
  value: formatMass(resolved.mass),
125
123
  size: tokens.typography.sizes.body,
126
124
  weight: 600,
127
- anchor: 'end',
128
- })
125
+ anchor: "end",
126
+ });
129
127
 
130
- const sepY = pad + headerH + metaRowH + 6
131
- const sep = divider({ x: pad, y: sepY, width: innerW })
128
+ const sepY = pad + headerH + metaRowH + 6;
129
+ const sep = divider({ x: pad, y: sepY, width: innerW });
132
130
 
133
131
  const statsSvg = rows
134
132
  .map((row, i) =>
@@ -143,9 +141,9 @@ export function renderResource(
143
141
  inverted: row.inverted,
144
142
  }),
145
143
  )
146
- .join('')
144
+ .join("");
147
145
 
148
- const inner = `${chrome}${icon}${name}${badge}${catText}${catValue}${massLabel}${massValue}${sep}${statsSvg}`
146
+ const inner = `${chrome}${icon}${name}${badge}${catText}${catValue}${massLabel}${massValue}${sep}${statsSvg}`;
149
147
 
150
- return `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${height}" viewBox="0 0 ${w} ${height}">${inner}</svg>`
148
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${height}" viewBox="0 0 ${w} ${height}">${inner}</svg>`;
151
149
  }
@@ -1,84 +1,74 @@
1
- import type { ResourceTier, TextSpan } from '@shipload/sdk'
2
- import { panel } from '../primitives/panel.ts'
3
- import { iconHex } from '../primitives/icon-hex.ts'
4
- import { text } from '../primitives/text.ts'
5
- import { divider } from '../primitives/divider.ts'
6
- import { moduleSlot } from '../primitives/module-slot.ts'
7
- import { quantityBadge } from '../primitives/quantity-badge.ts'
8
- import { wrapText } from '../primitives/wrap.ts'
9
- import { tokens } from '../tokens/index.ts'
1
+ import type { TextSpan } from "@shipload/sdk";
2
+ import { panel } from "../primitives/panel.ts";
3
+ import { iconHex } from "../primitives/icon-hex.ts";
4
+ import { text } from "../primitives/text.ts";
5
+ import { divider } from "../primitives/divider.ts";
6
+ import { moduleSlot } from "../primitives/module-slot.ts";
7
+ import { quantityBadge } from "../primitives/quantity-badge.ts";
8
+ import { wrapText } from "../primitives/wrap.ts";
9
+ import { tokens } from "../tokens/index.ts";
10
10
 
11
11
  export interface ShipPanelSlot {
12
- name?: string
13
- installed: boolean
14
- description?: string | TextSpan[]
12
+ name?: string;
13
+ installed: boolean;
14
+ description?: string | TextSpan[];
15
15
  }
16
16
 
17
17
  export interface ShipPanelProps {
18
- name: string
19
- tier: ResourceTier
20
- quantity?: number
21
- attributes: { capability: string; attributes: { label: string; value: number }[] }[]
22
- slots: ShipPanelSlot[]
18
+ name: string;
19
+ tier: number;
20
+ quantity?: number;
21
+ attributes: { capability: string; attributes: { label: string; value: number }[] }[];
22
+ slots: ShipPanelSlot[];
23
23
  }
24
24
 
25
25
  function formatNumber(n: number): string {
26
- return n.toLocaleString('en-US')
26
+ return n.toLocaleString("en-US");
27
27
  }
28
28
 
29
- function tierBorder(tier: string): string {
30
- const key = tier.toLowerCase() as keyof typeof tokens.colors.tier
31
- return tokens.colors.tier[key] ?? tokens.colors.surface.panelBorder
29
+ function tierBorder(tier: number): string {
30
+ return tokens.colors.tier[tier] ?? tokens.colors.surface.panelBorder;
32
31
  }
33
32
 
34
- const MODULE_LABEL_PREFIX = (capability: string) => `${capability}: `
33
+ const MODULE_LABEL_PREFIX = (capability: string) => `${capability}: `;
35
34
 
36
35
  function rowHeightFor(slot: ShipPanelSlot): number {
37
- if (!slot.installed) return 24
38
- const desc = slot.description
36
+ if (!slot.installed) return 24;
37
+ const desc = slot.description;
39
38
  const plain =
40
- typeof desc === 'string'
41
- ? desc
42
- : Array.isArray(desc)
43
- ? desc.map((s) => s.text).join('')
44
- : ''
45
- if (plain.length === 0) return 24
46
- const combined = MODULE_LABEL_PREFIX(slot.name ?? 'Module') + plain
47
- const lineCount = Math.max(1, wrapText({ value: combined, charsPerLine: 36 }).length)
48
- return 10 + lineCount * 14
39
+ typeof desc === "string" ? desc : Array.isArray(desc) ? desc.map((s) => s.text).join("") : "";
40
+ if (plain.length === 0) return 24;
41
+ const combined = MODULE_LABEL_PREFIX(slot.name ?? "Module") + plain;
42
+ const lineCount = Math.max(1, wrapText({ value: combined, charsPerLine: 36 }).length);
43
+ return 10 + lineCount * 14;
49
44
  }
50
45
 
51
46
  export function renderShipPanel(props: ShipPanelProps): string {
52
- const w = tokens.spacing.panelWidth
53
- const pad = tokens.spacing.panelPadding
54
- const innerW = w - pad * 2
55
- const quantity = props.quantity ?? 0
56
-
57
- const hullGroup = props.attributes?.find((g) => g.capability.toLowerCase() === 'hull')
58
- const hullRows = hullGroup?.attributes ?? []
59
-
60
- const headerH = 48
61
- const hullHeaderH = 20
62
- const hullRowH = 22
63
- const sectionGap = 12
64
- const rowHeights = props.slots.map(rowHeightFor)
65
- const modulesHeight = rowHeights.reduce((a, b) => a + b, 0)
47
+ const w = tokens.spacing.panelWidth;
48
+ const pad = tokens.spacing.panelPadding;
49
+ const innerW = w - pad * 2;
50
+ const quantity = props.quantity ?? 0;
51
+
52
+ const hullGroup = props.attributes?.find((g) => g.capability.toLowerCase() === "hull");
53
+ const hullRows = hullGroup?.attributes ?? [];
54
+
55
+ const headerH = 48;
56
+ const hullHeaderH = 20;
57
+ const hullRowH = 22;
58
+ const sectionGap = 12;
59
+ const rowHeights = props.slots.map(rowHeightFor);
60
+ const modulesHeight = rowHeights.reduce((a, b) => a + b, 0);
66
61
  const height =
67
- headerH +
68
- hullHeaderH +
69
- hullRows.length * hullRowH +
70
- sectionGap +
71
- modulesHeight +
72
- pad
62
+ headerH + hullHeaderH + hullRows.length * hullRowH + sectionGap + modulesHeight + pad;
73
63
 
74
- const chrome = panel({ width: w, height, borderColor: tierBorder(props.tier) })
64
+ const chrome = panel({ width: w, height, borderColor: tierBorder(props.tier) });
75
65
 
76
66
  const icon = iconHex({
77
67
  x: pad,
78
68
  y: pad + 4,
79
69
  color: tokens.colors.text.accent,
80
- code: 'SH',
81
- })
70
+ code: "SH",
71
+ });
82
72
 
83
73
  const name = text({
84
74
  x: pad + 34,
@@ -87,22 +77,22 @@ export function renderShipPanel(props: ShipPanelProps): string {
87
77
  size: tokens.typography.sizes.title,
88
78
  weight: 700,
89
79
  family: tokens.typography.display,
90
- })
80
+ });
91
81
 
92
- const badge = quantityBadge({ x: w - pad, y: pad, quantity })
82
+ const badge = quantityBadge({ x: w - pad, y: pad, quantity });
93
83
 
94
84
  const hullHeader = text({
95
85
  x: pad,
96
86
  y: pad + headerH,
97
- value: 'HULL',
87
+ value: "HULL",
98
88
  size: tokens.typography.sizes.subtitle,
99
89
  weight: 700,
100
90
  color: tokens.colors.text.secondary,
101
91
  letterSpacing: 1,
102
- })
92
+ });
103
93
 
104
- let y = pad + headerH + 6
105
- let hullSvg = ''
94
+ let y = pad + headerH + 6;
95
+ let hullSvg = "";
106
96
  for (const row of hullRows) {
107
97
  hullSvg +=
108
98
  text({
@@ -118,16 +108,21 @@ export function renderShipPanel(props: ShipPanelProps): string {
118
108
  value: formatNumber(row.value),
119
109
  size: tokens.typography.sizes.body,
120
110
  weight: 700,
121
- anchor: 'end',
111
+ anchor: "end",
122
112
  }) +
123
- divider({ x: pad, y: y + hullRowH - 4, width: innerW, color: tokens.colors.surface.panelBorderBright })
124
- y += hullRowH
113
+ divider({
114
+ x: pad,
115
+ y: y + hullRowH - 4,
116
+ width: innerW,
117
+ color: tokens.colors.surface.panelBorderBright,
118
+ });
119
+ y += hullRowH;
125
120
  }
126
121
 
127
- y += sectionGap
128
- let modulesSvg = ''
122
+ y += sectionGap;
123
+ let modulesSvg = "";
129
124
  for (let i = 0; i < props.slots.length; i++) {
130
- const slot = props.slots[i]!
125
+ const slot = props.slots[i]!;
131
126
  modulesSvg += moduleSlot({
132
127
  x: pad,
133
128
  y,
@@ -136,10 +131,10 @@ export function renderShipPanel(props: ShipPanelProps): string {
136
131
  capability: slot.name,
137
132
  description: slot.description,
138
133
  accentColor: tokens.colors.brand.teal,
139
- })
140
- y += rowHeights[i]!
134
+ });
135
+ y += rowHeights[i]!;
141
136
  }
142
137
 
143
- const inner = `${chrome}${icon}${name}${badge}${hullHeader}${hullSvg}${modulesSvg}`
144
- return `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${height}" viewBox="0 0 ${w} ${height}">${inner}</svg>`
138
+ const inner = `${chrome}${icon}${name}${badge}${hullHeader}${hullSvg}${modulesSvg}`;
139
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${height}" viewBox="0 0 ${w} ${height}">${inner}</svg>`;
145
140
  }
@@ -1,42 +1,44 @@
1
- import type { ResolvedItem } from '@shipload/sdk'
2
- import type { CargoItem } from '../payload/codec.ts'
3
- import { renderByType } from './index.ts'
4
- import { STARDUST_BASE64 } from '../assets/stardust-base64.ts'
5
- import { svgDimensions } from '../meta.ts'
1
+ import type { ResolvedItem } from "@shipload/sdk";
2
+ import type { CargoItem } from "../payload/codec.ts";
3
+ import { renderByType } from "./index.ts";
4
+ import { STARDUST_BASE64 } from "../assets/stardust-base64.ts";
5
+ import { svgDimensions } from "../meta.ts";
6
6
 
7
- export const SOCIAL_CARD_WIDTH = 1200
8
- export const SOCIAL_CARD_HEIGHT = 630
7
+ export const SOCIAL_CARD_WIDTH = 1200;
8
+ export const SOCIAL_CARD_HEIGHT = 630;
9
9
 
10
- const SPACE_DEEP = '#050c24'
11
- const STARDUST_TILE = 512
12
- const ITEM_MAX_WIDTH_RATIO = 0.35
13
- const ITEM_MAX_HEIGHT_RATIO = 0.82
14
- const DOMAIN_LABEL = 'shiploadgame.com'
10
+ const SPACE_DEEP = "#050c24";
11
+ const STARDUST_TILE = 512;
12
+ const ITEM_MAX_WIDTH_RATIO = 0.35;
13
+ const ITEM_MAX_HEIGHT_RATIO = 0.82;
14
+ const DOMAIN_LABEL = "shiploadgame.com";
15
15
 
16
16
  export function socialCardSvg(item: CargoItem, resolved: ResolvedItem): string {
17
- const itemSvg = renderByType(item, resolved)
18
- const { width: itemW, height: itemH } = svgDimensions(itemSvg)
17
+ const itemSvg = renderByType(item, resolved);
18
+ const { width: itemW, height: itemH } = svgDimensions(itemSvg);
19
19
 
20
20
  const scale = Math.min(
21
21
  (SOCIAL_CARD_WIDTH * ITEM_MAX_WIDTH_RATIO) / itemW,
22
22
  (SOCIAL_CARD_HEIGHT * ITEM_MAX_HEIGHT_RATIO) / itemH,
23
- )
24
- const scaledW = itemW * scale
25
- const scaledH = itemH * scale
26
- const tx = (SOCIAL_CARD_WIDTH - scaledW) / 2
27
- const ty = (SOCIAL_CARD_HEIGHT - scaledH) / 2
23
+ );
24
+ const scaledW = itemW * scale;
25
+ const scaledH = itemH * scale;
26
+ const tx = (SOCIAL_CARD_WIDTH - scaledW) / 2;
27
+ const ty = (SOCIAL_CARD_HEIGHT - scaledH) / 2;
28
28
 
29
- const itemInner = itemSvg.replace(/^<svg[^>]*>/, '').replace(/<\/svg>\s*$/, '')
29
+ const itemInner = itemSvg.replace(/^<svg[^>]*>/, "").replace(/<\/svg>\s*$/, "");
30
30
 
31
- return `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="${SOCIAL_CARD_WIDTH}" height="${SOCIAL_CARD_HEIGHT}" viewBox="0 0 ${SOCIAL_CARD_WIDTH} ${SOCIAL_CARD_HEIGHT}">` +
31
+ return (
32
+ `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="${SOCIAL_CARD_WIDTH}" height="${SOCIAL_CARD_HEIGHT}" viewBox="0 0 ${SOCIAL_CARD_WIDTH} ${SOCIAL_CARD_HEIGHT}">` +
32
33
  `<defs>` +
33
- `<pattern id="sc-stars" width="${STARDUST_TILE}" height="${STARDUST_TILE}" patternUnits="userSpaceOnUse">` +
34
- `<image xlink:href="data:image/png;base64,${STARDUST_BASE64}" width="${STARDUST_TILE}" height="${STARDUST_TILE}"/>` +
35
- `</pattern>` +
34
+ `<pattern id="sc-stars" width="${STARDUST_TILE}" height="${STARDUST_TILE}" patternUnits="userSpaceOnUse">` +
35
+ `<image xlink:href="data:image/png;base64,${STARDUST_BASE64}" width="${STARDUST_TILE}" height="${STARDUST_TILE}"/>` +
36
+ `</pattern>` +
36
37
  `</defs>` +
37
38
  `<rect width="${SOCIAL_CARD_WIDTH}" height="${SOCIAL_CARD_HEIGHT}" fill="${SPACE_DEEP}"/>` +
38
39
  `<rect width="${SOCIAL_CARD_WIDTH}" height="${SOCIAL_CARD_HEIGHT}" fill="url(#sc-stars)" opacity="0.75"/>` +
39
40
  `<g transform="translate(${tx.toFixed(2)} ${ty.toFixed(2)}) scale(${scale.toFixed(4)})">${itemInner}</g>` +
40
41
  `<text x="${SOCIAL_CARD_WIDTH - 40}" y="${SOCIAL_CARD_HEIGHT - 36}" text-anchor="end" fill="#e6e8ec" opacity="0.55" font-size="22" font-family="Inter, sans-serif" letter-spacing="0.04em">${DOMAIN_LABEL}</text>` +
41
- `</svg>`
42
+ `</svg>`
43
+ );
42
44
  }
@@ -1,45 +1,45 @@
1
- import { tierColors } from '@shipload/sdk'
1
+ import { tierColors } from "@shipload/sdk";
2
2
 
3
3
  export const colors = {
4
4
  surface: {
5
- background: '#0a0a0c',
6
- panel: '#11141a',
7
- panelBorder: '#1e242e',
8
- panelBorderBright: '#2a3340',
5
+ background: "#0a0a0c",
6
+ panel: "#11141a",
7
+ panelBorder: "#1e242e",
8
+ panelBorderBright: "#2a3340",
9
9
  },
10
10
  text: {
11
- primary: '#e6e8ec',
12
- secondary: '#8f98a8',
13
- muted: '#5b6373',
14
- accent: '#f4c96b',
11
+ primary: "#e6e8ec",
12
+ secondary: "#8f98a8",
13
+ muted: "#5b6373",
14
+ accent: "#f4c96b",
15
15
  },
16
16
  brand: {
17
- pink: '#ff4f9a',
18
- teal: '#2fd6d1',
19
- cyan: '#6cb9ff',
17
+ pink: "#ff4f9a",
18
+ teal: "#2fd6d1",
19
+ cyan: "#6cb9ff",
20
20
  },
21
21
  category: {
22
- ore: '#C26D3F',
23
- crystal: '#4ADBFF',
24
- gas: '#B8E4A0',
25
- regolith: '#C4A57B',
26
- biomass: '#5A8B3E',
22
+ ore: "#C26D3F",
23
+ crystal: "#4ADBFF",
24
+ gas: "#B8E4A0",
25
+ regolith: "#C4A57B",
26
+ biomass: "#5A8B3E",
27
27
  },
28
28
  tier: tierColors,
29
29
  accent: {
30
- component: '#8f98a8',
30
+ component: "#8f98a8",
31
31
  },
32
32
  capability: {
33
- engine: '#4a8abf',
34
- generator: '#22c55e',
35
- gatherer: '#f59e0b',
36
- loader: '#eab308',
37
- manufacturing: '#a855f7',
38
- storage: '#14b8a6',
39
- hauler: '#f97316',
33
+ engine: "#4a8abf",
34
+ generator: "#22c55e",
35
+ gatherer: "#f59e0b",
36
+ loader: "#eab308",
37
+ manufacturing: "#a855f7",
38
+ storage: "#14b8a6",
39
+ hauler: "#f97316",
40
40
  },
41
- } as const
41
+ } as const;
42
42
 
43
- export type CategoryColorKey = keyof typeof colors.category
44
- export type TierColorKey = keyof typeof colors.tier
45
- export type CapabilityColorKey = keyof typeof colors.capability
43
+ export type CategoryColorKey = keyof typeof colors.category;
44
+ export type TierColorKey = keyof typeof colors.tier;
45
+ export type CapabilityColorKey = keyof typeof colors.capability;
@@ -1,7 +1,7 @@
1
- import { colors } from './colors.ts'
2
- import { spacing } from './spacing.ts'
3
- import { typography } from './typography.ts'
1
+ import { colors } from "./colors.ts";
2
+ import { spacing } from "./spacing.ts";
3
+ import { typography } from "./typography.ts";
4
4
 
5
- export const tokens = { colors, spacing, typography } as const
6
- export type Tokens = typeof tokens
7
- export { colors, spacing, typography }
5
+ export const tokens = { colors, spacing, typography } as const;
6
+ export type Tokens = typeof tokens;
7
+ export { colors, spacing, typography };
@@ -7,4 +7,4 @@ export const spacing = {
7
7
  statBarHeight: 4,
8
8
  iconHexSize: 22,
9
9
  quantityBadgeHeight: 18,
10
- } as const
10
+ } as const;
@@ -16,4 +16,4 @@ export const typography = {
16
16
  semibold: 600,
17
17
  bold: 700,
18
18
  },
19
- } as const
19
+ } as const;