@skinhub/viewer 0.2.0 → 0.2.1
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/EMBED.md +8 -6
- package/README.md +47 -26
- package/package.json +1 -1
package/EMBED.md
CHANGED
|
@@ -522,16 +522,18 @@ Honest list, so none of it is discovered late.
|
|
|
522
522
|
and remove it on `ready`.
|
|
523
523
|
- **Click-through.** See §9.
|
|
524
524
|
- **Server-side rendering.** The viewer is WebGL; there is no server-rendered fallback image.
|
|
525
|
-
- **Render to an image.** There is no `capture()
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
525
|
+
- **Render to an image.** There is no `capture()`: the canvas runs without a preserved drawing buffer,
|
|
526
|
+
so a read from outside the render loop comes back blank. A PNG per item wants a render with no
|
|
527
|
+
browser tab open anyway, and that is a separate service and is not built.
|
|
528
|
+
- **Resolve an inspect link on its own.** `?i=` carries the item's *configuration* - float, seed,
|
|
529
|
+
StatTrak, name plate, stickers, charm - but not which weapon, so a raw-iframe integration still has
|
|
529
530
|
to send `?weapon=`/`?paint=` (or `?hash=`) alongside it.
|
|
530
531
|
**This limit is the frame's, not the product's:** `@skinhub/viewer` takes an `inspectLink` prop and
|
|
531
532
|
handles the whole thing, because it decodes host-side and resolves the `defindex` against its own
|
|
532
533
|
weapon table before building the URL. If you are in React, you never see this.
|
|
533
|
-
- **
|
|
534
|
-
|
|
534
|
+
- **Draw two subjects at once.** One frame shows one thing - a weapon or glove, a sticker, a charm, a
|
|
535
|
+
collectible, or an operator (§2). An operator shown as the subject holds no weapon, and the four
|
|
536
|
+
standalone subjects ignore `?view=`, `?i=`, `?float=`, `?slot=` and the sticker slots.
|
|
535
537
|
- **Style anything.** The frame is our document. `gizmocolor` and `gizmoshadow` are the only visual
|
|
536
538
|
hooks.
|
|
537
539
|
- **Two viewers cheaply.** Each frame is its own WebGL context. If you need a grid of them, drop
|
package/README.md
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# @skinhub/viewer
|
|
2
2
|
|
|
3
|
-
The SkinHub CS2 skin viewer as a React component
|
|
4
|
-
with stickers and a charm
|
|
5
|
-
`three` and no assets. React is the only peer
|
|
3
|
+
The SkinHub CS2 skin viewer as a React component - any weapon, knife or glove, at any wear and seed,
|
|
4
|
+
with stickers and a charm, or a sticker, charm, collectible or agent on its own. The 3D runs in an
|
|
5
|
+
iframe on our origin, so installing this pulls in no `three` and no assets. React is the only peer
|
|
6
|
+
dependency.
|
|
6
7
|
|
|
7
8
|
```bash
|
|
8
9
|
bun add @skinhub/viewer @skinhub/cdn
|
|
@@ -21,13 +22,23 @@ export default function Page() {
|
|
|
21
22
|
}
|
|
22
23
|
```
|
|
23
24
|
|
|
24
|
-
Or straight from a Steam inspect link
|
|
25
|
+
Or straight from a Steam inspect link - float, seed, StatTrak, name plate, stickers and charm all come
|
|
25
26
|
out of it:
|
|
26
27
|
|
|
27
28
|
```tsx
|
|
28
29
|
<SkinViewer inspectLink={item.inspectLink} style={{ width: 640, height: 420 }} />
|
|
29
30
|
```
|
|
30
31
|
|
|
32
|
+
A weapon is one of five things the viewer draws, and each of the other four is its own prop. They are
|
|
33
|
+
drawn on their own, with no gun in the picture:
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
<SkinViewer sticker={{ id: 37, wear: 0.2 }} /> // the real holo/foil shader, not an image on a plane
|
|
37
|
+
<SkinViewer charm={{ id: 5 }} />
|
|
38
|
+
<SkinViewer collectible={{ id: 874 }} /> // a pin, coin, medal or trophy
|
|
39
|
+
<SkinViewer operator={{ id: 5036 }} /> // an agent, alone
|
|
40
|
+
```
|
|
41
|
+
|
|
31
42
|
Two things worth knowing up front:
|
|
32
43
|
|
|
33
44
|
- **Give it a size.** It fills its container and has no intrinsic size; at 0 px you see nothing.
|
|
@@ -36,34 +47,44 @@ Two things worth knowing up front:
|
|
|
36
47
|
|
|
37
48
|
## Props
|
|
38
49
|
|
|
39
|
-
|
|
50
|
+
Exactly one subject is required - `item`, `inspectLink`, `sticker`, `charm`, `collectible` or
|
|
51
|
+
`operator`. Passing two, or none, is a type error. Everything else is optional.
|
|
40
52
|
|
|
41
53
|
| prop | type | default | |
|
|
42
54
|
|---|---|---|---|
|
|
43
|
-
| `item` | `{ weapon \| defindex, paintIndex, float?, seed?, statTrak?, nameTag?, legacyModel?, stickers?, charm? }` |
|
|
44
|
-
| `inspectLink` | `string` |
|
|
55
|
+
| `item` | `{ weapon \| defindex, paintIndex, float?, seed?, statTrak?, nameTag?, legacyModel?, stickers?, charm? }` | - | the item. `paintIndex: 0` is vanilla |
|
|
56
|
+
| `inspectLink` | `string` | - | a masked Steam inspect link, instead of `item` |
|
|
57
|
+
| `sticker` | `{ id, wear? }` | - | one sticker on nothing. `wear` is `0` (mint) to `1` |
|
|
58
|
+
| `charm` | `{ id, pattern? }` | - | one charm. `pattern` is its colour template, not a different model |
|
|
59
|
+
| `collectible` | `{ id }` | - | one pin, coin, medal or trophy. It has no other field |
|
|
60
|
+
| `operator` | `{ id, pose? }` | - | one agent alone. `pose` is a main-menu clip name, `null` for their idle |
|
|
45
61
|
| `origin` | `string` | `https://skinhub.gg` | where the embed is served. Read once, on mount |
|
|
46
62
|
| `view` | `'gun' \| 'hands' \| 'agent'` | `'gun'` | item alone, first-person, or held by an operator |
|
|
47
|
-
| `agent` | `{ id, pose? }` | default T | the
|
|
63
|
+
| `agent` | `{ id, pose? }` | default T | who is *holding* the weapon, in `hands` and `agent`. `operator` is the agent shown alone |
|
|
48
64
|
| `gloves` | `{ type, paintIndex, float?, seed? } \| null` | `null` | `null` is the wearer's own pair |
|
|
49
65
|
| `settings` | `{ camera?, quality?, environment?, overlays? }` | see below | shallow-merged per group |
|
|
50
66
|
| `interactions` | `{ orbit?, zoom?, dragStickers?, dragCharm? }` | orbit + zoom on | what the user may do |
|
|
51
67
|
| `editingSlot` | `number` | `-1` | which sticker slot is open; `5` is the charm |
|
|
52
|
-
| `onReady` | `() => void` |
|
|
53
|
-
| `onError` | `(error) => void` |
|
|
54
|
-
| `onChange` | `(item) => void` |
|
|
55
|
-
| `onResize` | `({ width, height, dpr }) => void` |
|
|
56
|
-
| `onEditingSlotChange` | `(slot) => void` |
|
|
57
|
-
| `loading` / `fallback` | `ReactNode` |
|
|
68
|
+
| `onReady` | `() => void` | - | stopped loading - **a level, not an edge**: it fires again after every weapon or view change |
|
|
69
|
+
| `onError` | `(error) => void` | - | `no-item`, `bad-inspect-link`, `unknown-weapon`, `unreachable`, `render-failed`, `protocol-mismatch`, `bad-message` |
|
|
70
|
+
| `onChange` | `(item) => void` | - | the user dragged a sticker or the charm - **carries ids, not names**, see below |
|
|
71
|
+
| `onResize` | `({ width, height, dpr }) => void` | - | the canvas box changed |
|
|
72
|
+
| `onEditingSlotChange` | `(slot) => void` | - | |
|
|
73
|
+
| `loading` / `fallback` | `ReactNode` | - | your skeleton, and your empty state on error |
|
|
58
74
|
| `className` / `style` / `title` | | | on the wrapper |
|
|
59
|
-
| `handle` | `SkinViewerHandle` |
|
|
75
|
+
| `handle` | `SkinViewerHandle` | - | from `useSkinViewer()` |
|
|
60
76
|
|
|
61
77
|
`settings.environment.background` defaults to `'transparent'`, so the canvas composites over your page.
|
|
62
78
|
`quality.bloomSpill` ships at `0` here. `MAP_NAMES` and `WEAPON_IDS` are exported as values for
|
|
63
79
|
building pickers.
|
|
64
80
|
|
|
65
|
-
|
|
66
|
-
|
|
81
|
+
`view`, `agent`, `gloves` and `editingSlot` describe a weapon, and the other four subjects ignore them.
|
|
82
|
+
`settings` and `interactions` apply to all six.
|
|
83
|
+
|
|
84
|
+
Changing `float`, `seed`, `statTrak`, `nameTag`, `stickers` or `charm` updates **in place** - no
|
|
85
|
+
reload, no flicker. Changing the weapon, paint index, legacy variant or view shows a loading cover. On
|
|
86
|
+
the other four subjects `sticker.wear`, `charm.pattern` and `operator.pose` are the in-place ones; the
|
|
87
|
+
id, and switching which subject you pass, reloads.
|
|
67
88
|
|
|
68
89
|
### Copying the item back out
|
|
69
90
|
|
|
@@ -79,7 +100,7 @@ the decoded `@skinhub/cdn` placement if you want to write database rows instead.
|
|
|
79
100
|
### `onChange` gives you ids, not names
|
|
80
101
|
|
|
81
102
|
The embed has never seen the sticker catalogue, so the item it hands back has `stickers: [{ id: 5032,
|
|
82
|
-
… }]` and no name or image. **`setItem(changed)` will therefore blank those out of your own UI**
|
|
103
|
+
… }]` and no name or image. **`setItem(changed)` will therefore blank those out of your own UI** - a
|
|
83
104
|
silent data loss in your state, with nothing to catch. Merge it into what you already hold instead:
|
|
84
105
|
|
|
85
106
|
```tsx
|
|
@@ -104,7 +125,7 @@ instance while developing:
|
|
|
104
125
|
<SkinViewer item={item} origin="http://localhost:3000" />
|
|
105
126
|
```
|
|
106
127
|
|
|
107
|
-
It is read once, at mount
|
|
128
|
+
It is read once, at mount - it is the one prop that could only be applied by reloading the frame.
|
|
108
129
|
|
|
109
130
|
## useSkinViewer()
|
|
110
131
|
|
|
@@ -123,31 +144,31 @@ viewer.reload()
|
|
|
123
144
|
|
|
124
145
|
**A CORS error fetching the catalogue.** `cdn.skinhub.gg` does not send
|
|
125
146
|
`access-control-allow-origin`, so a **browser** `fetch` for `data/skins.json` is blocked by the
|
|
126
|
-
browser before your code sees it
|
|
147
|
+
browser before your code sees it - it surfaces as a CORS failure rather than a status, so you cannot
|
|
127
148
|
even catch it. Call `@skinhub/cdn` from your **server** (a route handler, `getServerSideProps`, an RSC)
|
|
128
149
|
and pass the rows down. The files are 4–6 MB each, so you want them server-side regardless.
|
|
129
150
|
|
|
130
151
|
**Nothing renders.** If the container is 0 px in one dimension you get an empty box; the component
|
|
131
152
|
warns about that in development. Otherwise `onError` reports `unreachable` after 15 seconds, naming the
|
|
132
|
-
URL it could not reach
|
|
153
|
+
URL it could not reach - that means the embed never answered, so check the origin is reachable from
|
|
133
154
|
this browser and that your page's `Content-Security-Policy` allows framing it (`frame-src`).
|
|
134
155
|
|
|
135
156
|
**`onReady` fired but the picture is still covered.** `onReady` means "stopped loading" for the current
|
|
136
157
|
identity, and it fires again after every weapon or view change. Treat it as a level, not an edge.
|
|
137
158
|
|
|
138
159
|
**`protocol-mismatch`.** This package and the embed disagree about the wire. There is no back-compat
|
|
139
|
-
window by design: update `@skinhub/viewer`, or reload the embed if it is the stale one
|
|
160
|
+
window by design: update `@skinhub/viewer`, or reload the embed if it is the stale one - the error
|
|
140
161
|
message says which.
|
|
141
162
|
|
|
142
163
|
## More
|
|
143
164
|
|
|
144
|
-
- **[Quick-start app](https://github.com/SkinHubgg/
|
|
165
|
+
- **[Quick-start app](https://github.com/SkinHubgg/viewer-quick-start)** - a working skin picker built
|
|
145
166
|
on this and `@skinhub/cdn`, ending in an inspect link. The full example.
|
|
146
|
-
- **[EMBED.md](./EMBED.md)**
|
|
167
|
+
- **[EMBED.md](./EMBED.md)** - the raw `<iframe>` URL and `postMessage` contract, for stacks that are
|
|
147
168
|
not React. A PHP, Rails or plain-HTML page can interpolate an item into an `src` and be done; that
|
|
148
169
|
path is first-class, not a fallback.
|
|
149
|
-
- **[@skinhub/cdn](https://github.com/SkinHubgg/skinhub-cdn)**
|
|
170
|
+
- **[@skinhub/cdn](https://github.com/SkinHubgg/skinhub-cdn)** - the CS2 catalogue this pairs with:
|
|
150
171
|
skins, stickers, gloves, agents, and the inspect-link codec.
|
|
151
|
-
- [Development and releasing](./CONTRIBUTING.md)
|
|
172
|
+
- [Development and releasing](./CONTRIBUTING.md) - how the wire is kept in step with the embed.
|
|
152
173
|
|
|
153
174
|
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skinhub/viewer",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "React wrapper for the SkinHub CS2 skin viewer embed — props in, postMessage out. No three.js, no renderer, no asset bundle; React is the only peer.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|