@shipload/item-renderer 0.2.2 → 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.
- package/.claude/settings.local.json +6 -0
- package/bun.lock +2 -2
- package/package.json +2 -2
- package/scripts/check-bundle-size.ts +21 -21
- package/scripts/copy-fonts.ts +19 -19
- package/scripts/preview.ts +13 -15
- package/src/assets/stardust-base64.ts +1 -1
- package/src/errors.ts +8 -8
- package/src/fonts/index.ts +25 -26
- package/src/fonts/load-bun.ts +9 -9
- package/src/index.ts +21 -21
- package/src/links.ts +11 -11
- package/src/meta.ts +16 -16
- package/src/payload/base64url.ts +16 -16
- package/src/payload/codec.ts +13 -13
- package/src/primitives/category-icon.ts +69 -48
- package/src/primitives/compact-row.ts +13 -13
- package/src/primitives/divider.ts +9 -9
- package/src/primitives/icon-hex.ts +18 -16
- package/src/primitives/module-slot.ts +73 -73
- package/src/primitives/panel.ts +10 -10
- package/src/primitives/quantity-badge.ts +13 -13
- package/src/primitives/span-paragraph.ts +48 -50
- package/src/primitives/stat-bar.ts +24 -24
- package/src/primitives/svg.ts +13 -13
- package/src/primitives/text.ts +25 -25
- package/src/primitives/wrap.ts +12 -12
- package/src/render.ts +15 -19
- package/src/templates/_shared.ts +5 -6
- package/src/templates/component.ts +67 -65
- package/src/templates/index.ts +17 -17
- package/src/templates/item-cell.ts +48 -45
- package/src/templates/module.ts +83 -81
- package/src/templates/packed-entity.ts +12 -14
- package/src/templates/resource.ts +63 -65
- package/src/templates/ship-panel.ts +66 -71
- package/src/templates/social-card.ts +27 -25
- package/src/tokens/colors.ts +29 -29
- package/src/tokens/index.ts +6 -6
- package/src/tokens/spacing.ts +1 -1
- package/src/tokens/typography.ts +1 -1
- package/test/__image_snapshots__/module-storage-t1.diff.png +0 -0
- package/test/__image_snapshots__/packed-entity-ship-t1-two-modules.diff.png +0 -0
- package/test/base64url.test.ts +22 -22
- package/test/codec.test.ts +26 -35
- package/test/errors.test.ts +21 -21
- package/test/fixtures/cargo-items.ts +43 -43
- package/test/fonts.test.ts +23 -23
- package/test/links-meta.test.ts +37 -37
- package/test/pixel.test.ts +44 -41
- package/test/primitives-category-icon.test.ts +74 -67
- package/test/primitives-compact-row.test.ts +29 -29
- package/test/primitives-domain.test.ts +61 -50
- package/test/primitives-layout.test.ts +47 -47
- package/test/primitives-module-slot.test.ts +58 -58
- package/test/render.test.ts +38 -35
- package/test/sanity.test.ts +5 -5
- package/test/sdk-link.test.ts +13 -13
- package/test/svg.test.ts +24 -22
- package/test/templates-component.test.ts +32 -32
- package/test/templates-dispatch.test.ts +29 -29
- package/test/templates-item-cell.test.ts +79 -79
- package/test/templates-module.test.ts +52 -52
- package/test/templates-packed-entity.test.ts +42 -42
- package/test/templates-resource.test.ts +61 -61
- package/test/templates-ship-panel.test.ts +65 -61
- package/test/tokens.test.ts +28 -26
package/bun.lock
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"": {
|
|
6
6
|
"name": "@shipload/item-renderer",
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"@shipload/sdk": "2.0.0-
|
|
8
|
+
"@shipload/sdk": "^2.0.0-rc25",
|
|
9
9
|
"@wharfkit/antelope": "^1.0.0",
|
|
10
10
|
},
|
|
11
11
|
"devDependencies": {
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
|
|
73
73
|
"@resvg/resvg-js-win32-x64-msvc": ["@resvg/resvg-js-win32-x64-msvc@2.6.2", "", { "os": "win32", "cpu": "x64" }, "sha512-ZXtYhtUr5SSaBrUDq7DiyjOFJqBVL/dOBN7N/qmi/pO0IgiWW/f/ue3nbvu9joWE5aAKDoIzy/CxsY0suwGosQ=="],
|
|
74
74
|
|
|
75
|
-
"@shipload/sdk": ["@shipload/sdk@2.0.0-
|
|
75
|
+
"@shipload/sdk": ["@shipload/sdk@2.0.0-rc25", "", { "dependencies": { "@wharfkit/antelope": "1.2.0", "@wharfkit/contract": "^1.2.1", "@wharfkit/session": "^1.3.1", "tslib": "^2.1.0" } }, "sha512-Kjr8YH/hiGWaRm9gTtgus/H/YHJfLBfn9KtbU82iTRARS+ZIMZIYeMmvshTlOcOvGL9nIynbfM4mVz4jDQCq4g=="],
|
|
76
76
|
|
|
77
77
|
"@types/bun": ["@types/bun@1.3.12", "", { "dependencies": { "bun-types": "1.3.12" } }, "sha512-DBv81elK+/VSwXHDlnH3Qduw+KxkTIWi7TXkAeh24zpi5l0B2kUg9Ga3tb4nJaPcOFswflgi/yAvMVBPrxMB+A=="],
|
|
78
78
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shipload/item-renderer",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Deterministic SVG rendering for Shipload items",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"fonts:copy": "bun run scripts/copy-fonts.ts"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@shipload/sdk": "2.0.0-
|
|
40
|
+
"@shipload/sdk": "^2.0.0-rc25",
|
|
41
41
|
"@wharfkit/antelope": "^1.0.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
import { gzipSync } from
|
|
1
|
+
import { gzipSync } from "node:zlib";
|
|
2
2
|
|
|
3
|
-
const CORE_LIMIT_BYTES = 50 * 1024
|
|
3
|
+
const CORE_LIMIT_BYTES = 50 * 1024;
|
|
4
4
|
|
|
5
5
|
async function main() {
|
|
6
6
|
const result = await Bun.build({
|
|
7
|
-
entrypoints: [
|
|
8
|
-
target:
|
|
9
|
-
format:
|
|
7
|
+
entrypoints: ["./src/index.ts"],
|
|
8
|
+
target: "browser",
|
|
9
|
+
format: "esm",
|
|
10
10
|
minify: true,
|
|
11
|
-
external: [
|
|
12
|
-
})
|
|
11
|
+
external: ["@shipload/sdk", "@wharfkit/antelope"],
|
|
12
|
+
});
|
|
13
13
|
|
|
14
14
|
if (!result.success) {
|
|
15
|
-
for (const msg of result.logs) console.error(msg)
|
|
16
|
-
process.exit(1)
|
|
15
|
+
for (const msg of result.logs) console.error(msg);
|
|
16
|
+
process.exit(1);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
let totalBytes = 0
|
|
20
|
-
let totalGzipped = 0
|
|
19
|
+
let totalBytes = 0;
|
|
20
|
+
let totalGzipped = 0;
|
|
21
21
|
for (const out of result.outputs) {
|
|
22
|
-
const text = await out.text()
|
|
23
|
-
const bytes = new TextEncoder().encode(text).length
|
|
24
|
-
const gz = gzipSync(new TextEncoder().encode(text)).length
|
|
25
|
-
console.log(`${out.path}: ${bytes} bytes (${gz} gzipped)`)
|
|
26
|
-
totalBytes += bytes
|
|
27
|
-
totalGzipped += gz
|
|
22
|
+
const text = await out.text();
|
|
23
|
+
const bytes = new TextEncoder().encode(text).length;
|
|
24
|
+
const gz = gzipSync(new TextEncoder().encode(text)).length;
|
|
25
|
+
console.log(`${out.path}: ${bytes} bytes (${gz} gzipped)`);
|
|
26
|
+
totalBytes += bytes;
|
|
27
|
+
totalGzipped += gz;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
console.log(`core bundle total: ${totalBytes} bytes, ${totalGzipped} gzipped`)
|
|
30
|
+
console.log(`core bundle total: ${totalBytes} bytes, ${totalGzipped} gzipped`);
|
|
31
31
|
if (totalGzipped > CORE_LIMIT_BYTES) {
|
|
32
|
-
console.error(`FAIL: core bundle is ${totalGzipped} gzipped, limit is ${CORE_LIMIT_BYTES}`)
|
|
33
|
-
process.exit(1)
|
|
32
|
+
console.error(`FAIL: core bundle is ${totalGzipped} gzipped, limit is ${CORE_LIMIT_BYTES}`);
|
|
33
|
+
process.exit(1);
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
main()
|
|
37
|
+
main();
|
package/scripts/copy-fonts.ts
CHANGED
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
import { copyFile, mkdir } from
|
|
2
|
-
import { dirname, resolve } from
|
|
3
|
-
import { fileURLToPath } from
|
|
1
|
+
import { copyFile, mkdir } from "node:fs/promises";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
4
|
|
|
5
|
-
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
6
|
-
const OUT_DIR = resolve(__dirname,
|
|
7
|
-
const NM = resolve(__dirname,
|
|
5
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
const OUT_DIR = resolve(__dirname, "../src/fonts");
|
|
7
|
+
const NM = resolve(__dirname, "../node_modules");
|
|
8
8
|
|
|
9
9
|
const FACES = [
|
|
10
10
|
{
|
|
11
11
|
src: `${NM}/@fontsource/orbitron/files/orbitron-latin-700-normal.woff2`,
|
|
12
|
-
out:
|
|
12
|
+
out: "orbitron-700.woff2",
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
src: `${NM}/@fontsource/inter/files/inter-latin-400-normal.woff2`,
|
|
16
|
-
out:
|
|
16
|
+
out: "inter-400.woff2",
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
src: `${NM}/@fontsource/inter/files/inter-latin-600-normal.woff2`,
|
|
20
|
-
out:
|
|
20
|
+
out: "inter-600.woff2",
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
23
|
src: `${NM}/@fontsource/jetbrains-mono/files/jetbrains-mono-latin-500-normal.woff2`,
|
|
24
|
-
out:
|
|
24
|
+
out: "jetbrains-500.woff2",
|
|
25
25
|
},
|
|
26
|
-
]
|
|
26
|
+
];
|
|
27
27
|
|
|
28
28
|
async function main() {
|
|
29
|
-
await mkdir(OUT_DIR, { recursive: true })
|
|
29
|
+
await mkdir(OUT_DIR, { recursive: true });
|
|
30
30
|
for (const face of FACES) {
|
|
31
|
-
const dest = resolve(OUT_DIR, face.out)
|
|
32
|
-
await copyFile(face.src, dest)
|
|
33
|
-
const size = (await Bun.file(dest).arrayBuffer()).byteLength
|
|
34
|
-
console.log(`wrote ${face.out} (${size} bytes)`)
|
|
31
|
+
const dest = resolve(OUT_DIR, face.out);
|
|
32
|
+
await copyFile(face.src, dest);
|
|
33
|
+
const size = (await Bun.file(dest).arrayBuffer()).byteLength;
|
|
34
|
+
console.log(`wrote ${face.out} (${size} bytes)`);
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
main().catch((e) => {
|
|
39
|
-
console.error(e)
|
|
40
|
-
process.exit(1)
|
|
41
|
-
})
|
|
39
|
+
console.error(e);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
});
|
package/scripts/preview.ts
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
import { resolveItem } from
|
|
2
|
-
import { renderItem } from
|
|
3
|
-
import { FIXTURES } from
|
|
1
|
+
import { resolveItem } from "@shipload/sdk";
|
|
2
|
+
import { renderItem } from "../src/render.ts";
|
|
3
|
+
import { FIXTURES } from "../test/fixtures/cargo-items.ts";
|
|
4
4
|
|
|
5
5
|
function page(): string {
|
|
6
|
-
const sections: string[] = []
|
|
6
|
+
const sections: string[] = [];
|
|
7
7
|
for (const [name, item] of Object.entries(FIXTURES)) {
|
|
8
|
-
const resolved = resolveItem(item.item_id, item.stats, item.modules)
|
|
9
|
-
const svg = renderItem(item, resolved)
|
|
10
|
-
sections.push(
|
|
11
|
-
`<section><h3>${name}</h3><div class="wrap">${svg}</div></section>`,
|
|
12
|
-
)
|
|
8
|
+
const resolved = resolveItem(item.item_id, item.stats, item.modules);
|
|
9
|
+
const svg = renderItem(item, resolved);
|
|
10
|
+
sections.push(`<section><h3>${name}</h3><div class="wrap">${svg}</div></section>`);
|
|
13
11
|
}
|
|
14
12
|
return `
|
|
15
13
|
<!doctype html>
|
|
@@ -27,17 +25,17 @@ function page(): string {
|
|
|
27
25
|
</head>
|
|
28
26
|
<body>
|
|
29
27
|
<h1>Fixtures</h1>
|
|
30
|
-
<main>${sections.join(
|
|
28
|
+
<main>${sections.join("")}</main>
|
|
31
29
|
</body>
|
|
32
30
|
</html>
|
|
33
|
-
|
|
31
|
+
`;
|
|
34
32
|
}
|
|
35
33
|
|
|
36
|
-
const port = Number(process.env.PORT ?? 5173)
|
|
34
|
+
const port = Number(process.env.PORT ?? 5173);
|
|
37
35
|
|
|
38
36
|
Bun.serve({
|
|
39
37
|
port,
|
|
40
|
-
fetch: () => new Response(page(), { headers: {
|
|
41
|
-
})
|
|
38
|
+
fetch: () => new Response(page(), { headers: { "content-type": "text/html" } }),
|
|
39
|
+
});
|
|
42
40
|
|
|
43
|
-
console.log(`preview running at http://localhost:${port}`)
|
|
41
|
+
console.log(`preview running at http://localhost:${port}`);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export const STARDUST_BASE64 =
|
|
2
|
-
'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'
|
|
2
|
+
"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";
|
package/src/errors.ts
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
export class InvalidPayloadError extends Error {
|
|
2
|
-
override readonly name =
|
|
2
|
+
override readonly name = "InvalidPayloadError";
|
|
3
3
|
constructor(message: string) {
|
|
4
|
-
super(message)
|
|
4
|
+
super(message);
|
|
5
5
|
}
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export class UnknownItemError extends Error {
|
|
9
|
-
override readonly name =
|
|
10
|
-
readonly itemId: number
|
|
9
|
+
override readonly name = "UnknownItemError";
|
|
10
|
+
readonly itemId: number;
|
|
11
11
|
constructor(itemId: number) {
|
|
12
|
-
super(`unknown item id: ${itemId}`)
|
|
13
|
-
this.itemId = itemId
|
|
12
|
+
super(`unknown item id: ${itemId}`);
|
|
13
|
+
this.itemId = itemId;
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export class RenderError extends Error {
|
|
18
|
-
override readonly name =
|
|
18
|
+
override readonly name = "RenderError";
|
|
19
19
|
constructor(message: string, options?: { cause?: unknown }) {
|
|
20
|
-
super(message, options)
|
|
20
|
+
super(message, options);
|
|
21
21
|
}
|
|
22
22
|
}
|
package/src/fonts/index.ts
CHANGED
|
@@ -1,36 +1,35 @@
|
|
|
1
|
-
export type FontKey =
|
|
1
|
+
export type FontKey = "orbitron-700" | "inter-400" | "inter-600" | "jetbrains-500";
|
|
2
2
|
|
|
3
3
|
export interface FontMeta {
|
|
4
|
-
family: string
|
|
5
|
-
weight: number
|
|
6
|
-
fileName: string
|
|
4
|
+
family: string;
|
|
5
|
+
weight: number;
|
|
6
|
+
fileName: string;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export const FONT_MANIFEST: Record<FontKey, FontMeta> = {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
10
|
+
"orbitron-700": { family: "Orbitron", weight: 700, fileName: "orbitron-700.woff2" },
|
|
11
|
+
"inter-400": { family: "Inter", weight: 400, fileName: "inter-400.woff2" },
|
|
12
|
+
"inter-600": { family: "Inter", weight: 600, fileName: "inter-600.woff2" },
|
|
13
|
+
"jetbrains-500": { family: "JetBrains Mono", weight: 500, fileName: "jetbrains-500.woff2" },
|
|
14
|
+
};
|
|
15
15
|
|
|
16
16
|
function bytesToBase64(bytes: Uint8Array): string {
|
|
17
|
-
let binary =
|
|
18
|
-
for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]!)
|
|
19
|
-
return btoa(binary)
|
|
17
|
+
let binary = "";
|
|
18
|
+
for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]!);
|
|
19
|
+
return btoa(binary);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
export function embedFontsInSvg(
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return svg.replace('>', `>${style}`)
|
|
22
|
+
export function embedFontsInSvg(svg: string, fontData: Record<FontKey, Uint8Array>): string {
|
|
23
|
+
const faceBlocks = (Object.keys(fontData) as FontKey[])
|
|
24
|
+
.map((key) => {
|
|
25
|
+
const meta = FONT_MANIFEST[key];
|
|
26
|
+
const b64 = bytesToBase64(fontData[key]);
|
|
27
|
+
return (
|
|
28
|
+
`@font-face { font-family: "${meta.family}"; font-weight: ${meta.weight}; ` +
|
|
29
|
+
`font-style: normal; src: url(data:font/woff2;base64,${b64}) format("woff2"); }`
|
|
30
|
+
);
|
|
31
|
+
})
|
|
32
|
+
.join("\n");
|
|
33
|
+
const style = `<defs><style type="text/css"><![CDATA[\n${faceBlocks}\n]]></style></defs>`;
|
|
34
|
+
return svg.replace(">", `>${style}`);
|
|
36
35
|
}
|
package/src/fonts/load-bun.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { readFile } from
|
|
2
|
-
import { dirname, join } from
|
|
3
|
-
import { fileURLToPath } from
|
|
4
|
-
import { FONT_MANIFEST, type FontKey } from
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { FONT_MANIFEST, type FontKey } from "./index.ts";
|
|
5
5
|
|
|
6
|
-
const HERE = dirname(fileURLToPath(import.meta.url))
|
|
6
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
7
7
|
|
|
8
8
|
export async function loadFontData(): Promise<Record<FontKey, Uint8Array>> {
|
|
9
9
|
const entries = await Promise.all(
|
|
10
10
|
(Object.keys(FONT_MANIFEST) as FontKey[]).map(async (key) => {
|
|
11
|
-
const buf = await readFile(join(HERE, FONT_MANIFEST[key].fileName))
|
|
12
|
-
return [key, new Uint8Array(buf)] as const
|
|
11
|
+
const buf = await readFile(join(HERE, FONT_MANIFEST[key].fileName));
|
|
12
|
+
return [key, new Uint8Array(buf)] as const;
|
|
13
13
|
}),
|
|
14
|
-
)
|
|
15
|
-
return Object.fromEntries(entries) as Record<FontKey, Uint8Array
|
|
14
|
+
);
|
|
15
|
+
return Object.fromEntries(entries) as Record<FontKey, Uint8Array>;
|
|
16
16
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,45 +1,45 @@
|
|
|
1
1
|
// Version
|
|
2
|
-
export const VERSION =
|
|
2
|
+
export const VERSION = "0.1.0";
|
|
3
3
|
|
|
4
4
|
// Errors
|
|
5
|
-
export { InvalidPayloadError, UnknownItemError, RenderError } from
|
|
5
|
+
export { InvalidPayloadError, UnknownItemError, RenderError } from "./errors.ts";
|
|
6
6
|
|
|
7
7
|
// Payload
|
|
8
|
-
export { encodePayload, decodePayload } from
|
|
9
|
-
export type { CargoItem, CargoItemLike } from
|
|
8
|
+
export { encodePayload, decodePayload } from "./payload/codec.ts";
|
|
9
|
+
export type { CargoItem, CargoItemLike } from "./payload/codec.ts";
|
|
10
10
|
|
|
11
11
|
// Rendering
|
|
12
|
-
export { renderItem, renderFromPayload, type RenderOptions } from
|
|
13
|
-
export { renderByType, type RenderByTypeOpts } from
|
|
12
|
+
export { renderItem, renderFromPayload, type RenderOptions } from "./render.ts";
|
|
13
|
+
export { renderByType, type RenderByTypeOpts } from "./templates/index.ts";
|
|
14
14
|
|
|
15
15
|
// Links + meta
|
|
16
|
-
export { linkToItemPage, linkToItemImage, linkToItemSocial } from
|
|
17
|
-
export { itemPageMeta, svgDimensions } from
|
|
18
|
-
export type { ItemPageMeta, ItemPageMetaOptions } from
|
|
16
|
+
export { linkToItemPage, linkToItemImage, linkToItemSocial } from "./links.ts";
|
|
17
|
+
export { itemPageMeta, svgDimensions } from "./meta.ts";
|
|
18
|
+
export type { ItemPageMeta, ItemPageMetaOptions } from "./meta.ts";
|
|
19
19
|
|
|
20
20
|
// Tokens (consumed by testmap tailwind.config)
|
|
21
|
-
export { tokens } from
|
|
22
|
-
export type { Tokens } from
|
|
23
|
-
export type { CategoryColorKey, TierColorKey } from
|
|
21
|
+
export { tokens } from "./tokens/index.ts";
|
|
22
|
+
export type { Tokens } from "./tokens/index.ts";
|
|
23
|
+
export type { CategoryColorKey, TierColorKey } from "./tokens/colors.ts";
|
|
24
24
|
|
|
25
25
|
// Category icon primitive
|
|
26
|
-
export { categoryIconSvg, categoryIconPath } from
|
|
27
|
-
export type { CategoryIconPathOpts, CategoryIconSvgOpts } from
|
|
26
|
+
export { categoryIconSvg, categoryIconPath } from "./primitives/category-icon.ts";
|
|
27
|
+
export type { CategoryIconPathOpts, CategoryIconSvgOpts } from "./primitives/category-icon.ts";
|
|
28
28
|
|
|
29
29
|
// Item cell templates
|
|
30
|
-
export { renderItemCell, itemCellGroup } from
|
|
31
|
-
export type { ItemCellProps, ItemCellGroupProps } from
|
|
30
|
+
export { renderItemCell, itemCellGroup } from "./templates/item-cell.ts";
|
|
31
|
+
export type { ItemCellProps, ItemCellGroupProps } from "./templates/item-cell.ts";
|
|
32
32
|
|
|
33
33
|
// Social card template (1200x630 OG image)
|
|
34
34
|
export {
|
|
35
35
|
socialCardSvg,
|
|
36
36
|
SOCIAL_CARD_WIDTH,
|
|
37
37
|
SOCIAL_CARD_HEIGHT,
|
|
38
|
-
} from
|
|
38
|
+
} from "./templates/social-card.ts";
|
|
39
39
|
|
|
40
40
|
// Ship panel template
|
|
41
|
-
export { renderShipPanel } from
|
|
42
|
-
export type { ShipPanelProps, ShipPanelSlot } from
|
|
41
|
+
export { renderShipPanel } from "./templates/ship-panel.ts";
|
|
42
|
+
export type { ShipPanelProps, ShipPanelSlot } from "./templates/ship-panel.ts";
|
|
43
43
|
|
|
44
44
|
// Re-exports from sdkv2 so consumers only need one import boundary
|
|
45
45
|
export {
|
|
@@ -50,5 +50,5 @@ export {
|
|
|
50
50
|
type ResolvedItemType,
|
|
51
51
|
type ResolvedModuleSlot,
|
|
52
52
|
type ResolvedAttributeGroup,
|
|
53
|
-
} from
|
|
54
|
-
export type { CategoryIconShape } from
|
|
53
|
+
} from "@shipload/sdk";
|
|
54
|
+
export type { CategoryIconShape } from "@shipload/sdk";
|
package/src/links.ts
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import type { CargoItem } from
|
|
2
|
-
import { encodePayload } from
|
|
1
|
+
import type { CargoItem } from "./payload/codec.ts";
|
|
2
|
+
import { encodePayload } from "./payload/codec.ts";
|
|
3
3
|
|
|
4
|
-
const DEFAULT_WEBSITE_BASE =
|
|
5
|
-
const DEFAULT_IMAGE_BASE =
|
|
4
|
+
const DEFAULT_WEBSITE_BASE = "https://shiploadgame.com";
|
|
5
|
+
const DEFAULT_IMAGE_BASE = "https://item.shiploadgame.com";
|
|
6
6
|
|
|
7
7
|
export function linkToItemPage(item: CargoItem, baseUrl = DEFAULT_WEBSITE_BASE): string {
|
|
8
|
-
const payload = encodePayload(item)
|
|
9
|
-
return `${baseUrl}/guide/item/${payload}
|
|
8
|
+
const payload = encodePayload(item);
|
|
9
|
+
return `${baseUrl}/guide/item/${payload}`;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export function linkToItemImage(
|
|
13
13
|
item: CargoItem,
|
|
14
|
-
ext:
|
|
14
|
+
ext: "png" | "svg",
|
|
15
15
|
baseUrl = DEFAULT_IMAGE_BASE,
|
|
16
16
|
): string {
|
|
17
|
-
const payload = encodePayload(item)
|
|
18
|
-
return `${baseUrl}/item/${payload}.${ext}
|
|
17
|
+
const payload = encodePayload(item);
|
|
18
|
+
return `${baseUrl}/item/${payload}.${ext}`;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export function linkToItemSocial(item: CargoItem, baseUrl = DEFAULT_IMAGE_BASE): string {
|
|
22
|
-
const payload = encodePayload(item)
|
|
23
|
-
return `${baseUrl}/social/${payload}.png
|
|
22
|
+
const payload = encodePayload(item);
|
|
23
|
+
return `${baseUrl}/social/${payload}.png`;
|
|
24
24
|
}
|
package/src/meta.ts
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import type { ResolvedItem } from
|
|
2
|
-
import { describeItem, displayName } from
|
|
3
|
-
import type { CargoItem } from
|
|
4
|
-
import { linkToItemSocial } from
|
|
5
|
-
import { SOCIAL_CARD_WIDTH, SOCIAL_CARD_HEIGHT } from
|
|
1
|
+
import type { ResolvedItem } from "@shipload/sdk";
|
|
2
|
+
import { describeItem, displayName } from "@shipload/sdk";
|
|
3
|
+
import type { CargoItem } from "./payload/codec.ts";
|
|
4
|
+
import { linkToItemSocial } from "./links.ts";
|
|
5
|
+
import { SOCIAL_CARD_WIDTH, SOCIAL_CARD_HEIGHT } from "./templates/social-card.ts";
|
|
6
6
|
|
|
7
|
-
const DIMS_RE = /<svg[^>]*?\bwidth="(\d+)"[^>]*?\bheight="(\d+)"
|
|
7
|
+
const DIMS_RE = /<svg[^>]*?\bwidth="(\d+)"[^>]*?\bheight="(\d+)"/;
|
|
8
8
|
|
|
9
9
|
export function svgDimensions(svg: string): { width: number; height: number } {
|
|
10
|
-
const m = DIMS_RE.exec(svg)
|
|
11
|
-
if (!m) throw new Error(
|
|
12
|
-
return { width: Number(m[1]), height: Number(m[2]) }
|
|
10
|
+
const m = DIMS_RE.exec(svg);
|
|
11
|
+
if (!m) throw new Error("svgDimensions: could not locate width/height on root <svg>");
|
|
12
|
+
return { width: Number(m[1]), height: Number(m[2]) };
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export interface ItemPageMeta {
|
|
16
|
-
title: string
|
|
17
|
-
description: string
|
|
18
|
-
ogImage: string
|
|
19
|
-
ogImageWidth: number
|
|
20
|
-
ogImageHeight: number
|
|
16
|
+
title: string;
|
|
17
|
+
description: string;
|
|
18
|
+
ogImage: string;
|
|
19
|
+
ogImageWidth: number;
|
|
20
|
+
ogImageHeight: number;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export interface ItemPageMetaOptions {
|
|
24
|
-
imageBaseUrl?: string
|
|
24
|
+
imageBaseUrl?: string;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export function itemPageMeta(
|
|
@@ -35,5 +35,5 @@ export function itemPageMeta(
|
|
|
35
35
|
ogImage: linkToItemSocial(item, opts?.imageBaseUrl),
|
|
36
36
|
ogImageWidth: SOCIAL_CARD_WIDTH,
|
|
37
37
|
ogImageHeight: SOCIAL_CARD_HEIGHT,
|
|
38
|
-
}
|
|
38
|
+
};
|
|
39
39
|
}
|
package/src/payload/base64url.ts
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import { InvalidPayloadError } from
|
|
1
|
+
import { InvalidPayloadError } from "../errors.ts";
|
|
2
2
|
|
|
3
3
|
export function bytesToBase64Url(bytes: Uint8Array): string {
|
|
4
|
-
let binary =
|
|
5
|
-
for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i]!)
|
|
6
|
-
const b64 = btoa(binary)
|
|
7
|
-
return b64.replaceAll(
|
|
4
|
+
let binary = "";
|
|
5
|
+
for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i]!);
|
|
6
|
+
const b64 = btoa(binary);
|
|
7
|
+
return b64.replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "");
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export function base64UrlToBytes(input: string): Uint8Array {
|
|
11
11
|
if (!/^[A-Za-z0-9_-]*$/.test(input)) {
|
|
12
|
-
throw new InvalidPayloadError(
|
|
12
|
+
throw new InvalidPayloadError("payload contains non-base64url characters");
|
|
13
13
|
}
|
|
14
|
-
const padded = input
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
let binary: string
|
|
14
|
+
const padded = input
|
|
15
|
+
.replaceAll("-", "+")
|
|
16
|
+
.replaceAll("_", "/")
|
|
17
|
+
.padEnd(Math.ceil(input.length / 4) * 4, "=");
|
|
18
|
+
let binary: string;
|
|
19
19
|
try {
|
|
20
|
-
binary = atob(padded)
|
|
20
|
+
binary = atob(padded);
|
|
21
21
|
} catch (e) {
|
|
22
|
-
throw new InvalidPayloadError(`base64 decode failed: ${(e as Error).message}`)
|
|
22
|
+
throw new InvalidPayloadError(`base64 decode failed: ${(e as Error).message}`);
|
|
23
23
|
}
|
|
24
|
-
const bytes = new Uint8Array(binary.length)
|
|
25
|
-
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i)
|
|
26
|
-
return bytes
|
|
24
|
+
const bytes = new Uint8Array(binary.length);
|
|
25
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
26
|
+
return bytes;
|
|
27
27
|
}
|