@souldi/try-on 0.4.5 → 0.5.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/CHANGELOG.md +21 -0
- package/README.md +43 -56
- package/dist/widget.mjs +1 -1
- package/dist/widget.umd.js +1 -1
- package/package.json +3 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
**Breaking.** Refine and the size screen leave the widget (ADR-0095).
|
|
6
|
+
|
|
7
|
+
- **One screen.** The result dialog is one **Try-on** screen. Before a try-on it shows the garment, the customer's size, the cart button and **Try on**; while the try-on is made the garment fades under a progress line, "Creating your try-on…", and the dialog stays open until the result lands. The size on the button opens it, before and after a try-on.
|
|
8
|
+
- **Removed: the size screen.** "How size X sits on you" and its 3D mannequin are gone from the dialog, and the size in the dialog is no longer a button. The mannequin's standalone build is still published, for older bundles that load it.
|
|
9
|
+
- **Removed: Refine.** The dialog has no Refine control, and the button handle's `refine()` is gone. `onError` no longer reports `operation: 'refine'`, and `onGenerationSuccess` / `onResultShown` no longer carry `refinable`. Refine stays in the Souldi API; the widget never sends a `refinement_prompt`.
|
|
10
|
+
- `openSize()` on the button handle still opens the dialog on the customer's size. Before a try-on it now shows the garment and **Try on** there.
|
|
11
|
+
|
|
12
|
+
## 0.5.0
|
|
13
|
+
|
|
14
|
+
**Breaking.** `init({ appearance })` replaces the `theme` option.
|
|
15
|
+
|
|
16
|
+
- **New: `appearance`.** `init({ appearance: { ink, fill, corners, font } })` styles the try-on button, the result dialog and the sign-in and upload screens. It is the same closed set of values a Shopify merchant sets on the Souldi block. Missing or unknown values fall back to the defaults, one by one, and never throw. See [Appearance](README.md#appearance).
|
|
17
|
+
- **Removed: `theme`.** Its properties (`mode`, `accentColor`, `accentTextColor`, `borderRadius`, `fontFamily`, `headingFontFamily`, `buttonOutlineColor`) are gone, not deprecated. A `theme` that is still passed is ignored and logs one console warning.
|
|
18
|
+
- **Removed: dark mode.** The widget is light-only, like its result dialog. An embedder who passed `theme: { mode: 'dark' }` now gets the light sign-in and upload screens.
|
|
19
|
+
- **Contrast is reported, never corrected.** The `ink` is used as given. If it is below 4.5:1 against white, the widget logs one console warning.
|
|
20
|
+
|
|
21
|
+
**CDN embedders get this on the day it is published.** The README's script tag, `https://unpkg.com/@souldi/try-on/dist/widget.umd.js`, names no version, so it serves the newest release. To stay on 0.4 while you move from `theme` to `appearance`, pin the version in the URL (`https://unpkg.com/@souldi/try-on@0.4/dist/widget.umd.js`) or in your `package.json`.
|
package/README.md
CHANGED
|
@@ -61,19 +61,20 @@ The result surface is the widget's and only the widget's: an accessible
|
|
|
61
61
|
`<dialog>` inside an isolated Shadow DOM, appended to `<body>`. It reads none of
|
|
62
62
|
your CSS selectors and never touches your product images, so a theme change
|
|
63
63
|
cannot break it — and there is nothing to point it at. It is also where the
|
|
64
|
-
customer decides. It
|
|
65
|
-
|
|
66
|
-
- **
|
|
67
|
-
size chart), an **Add size X to
|
|
68
|
-
[`onAddToCart`](#onaddtocart))
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
64
|
+
customer decides. It is one screen, **Try-on**, in three states:
|
|
65
|
+
|
|
66
|
+
- **Before a try-on** — the garment (the button's `garmentUrl`) beside the
|
|
67
|
+
customer's size (when the garment has a size chart), an **Add size X to
|
|
68
|
+
cart** button (when you supply [`onAddToCart`](#onaddtocart)) and **Try on**.
|
|
69
|
+
- **While it is being made** — the garment fades under a progress line,
|
|
70
|
+
"Creating your try-on…". The dialog stays open and shows the result when it
|
|
71
|
+
lands; closed meanwhile, it reopens with the result.
|
|
72
|
+
- **With a result** — the render, the same size and cart button, and the
|
|
73
|
+
garment's back view when there is one.
|
|
74
|
+
|
|
75
|
+
The dialog can open before any try-on has been generated — the size comes
|
|
76
|
+
from the customer's measurements, not from a render — so the size shown on
|
|
77
|
+
the try-on button opens it, before and after a try-on. The ruler
|
|
77
78
|
shown on the button before a size exists is a way into sizing: it signs the
|
|
78
79
|
customer in and takes them to their size, or to their profile when no size is
|
|
79
80
|
ready yet. Neither ever starts a try-on. **Your profile** (retake the photo,
|
|
@@ -97,7 +98,7 @@ Call once per page load to configure the widget.
|
|
|
97
98
|
| Option | Type | Required | Description |
|
|
98
99
|
|--------|------|----------|-------------|
|
|
99
100
|
| `tenantApiKey` | `string` | Yes | Your publishable API key (from your Souldi dashboard) |
|
|
100
|
-
| `
|
|
101
|
+
| `appearance` | `object` | No | How the widget looks: ink, fill, corners and font ([see Appearance](#appearance)) |
|
|
101
102
|
| `fontsUrl` | `string` | No | Where you serve the package's `dist/fonts` directory, to load the fonts from your own origin instead of unpkg ([see Fonts](#fonts)) |
|
|
102
103
|
| `declareFonts` | `boolean` | No | Default `true`: the widget declares its fonts in your page ([see Fonts](#fonts)). Pass `false` only if your page already declares them |
|
|
103
104
|
| `onGenerationStart` | `function(info)` | No | Called when AI generation begins |
|
|
@@ -117,7 +118,7 @@ Callbacks about a specific try-on receive a trailing `info` object:
|
|
|
117
118
|
| Field | Type | Present on | Description |
|
|
118
119
|
|-------|------|-----------|-------------|
|
|
119
120
|
| `buttonId` | `string` | most | The `containerId` of the button this callback is about |
|
|
120
|
-
| `operation` | `'generate' \| '
|
|
121
|
+
| `operation` | `'generate' \| 'view'` | `onError`, `onResultShown` | Which operation this is about |
|
|
121
122
|
| `shownInResult` | `boolean` | `onError` | `true` when the widget has already said this inside its open dialog, under the result it failed to change — so you need not say it again |
|
|
122
123
|
| `view` | `string \| null` | `onGenerationSuccess`, `onResultShown` | The Render View this result is (e.g. `'front'`), when the garment offers more than one |
|
|
123
124
|
| `how` | `'verified' \| 'shared' \| 'restored'` | `onSignedIn` | How this tab's session began. Only `'verified'` is a *new* sign-in — `'shared'` and `'restored'` are this tab catching up with a session that began elsewhere |
|
|
@@ -131,12 +132,9 @@ placed by guessing. Read it whenever you render results yourself.
|
|
|
131
132
|
`info` is passed after the existing arguments, so `onGenerationSuccess(url)`
|
|
132
133
|
and `onError(message)` keep working unchanged.
|
|
133
134
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
The previous result is untouched either way. A failed `refine` or `view` with
|
|
138
|
-
the dialog open is already shown in it (`shownInResult: true`); with the dialog
|
|
139
|
-
closed, it is yours to show if you want the customer to see it.
|
|
135
|
+
A failed `view` leaves the previous result untouched. With the dialog open it
|
|
136
|
+
is already shown in it (`shownInResult: true`); with the dialog closed, it is
|
|
137
|
+
yours to show if you want the customer to see it.
|
|
140
138
|
|
|
141
139
|
`onResultShown` receives the same `info` `onGenerationSuccess` did for that
|
|
142
140
|
result, minus the image URL — it is about the same render, just reported once
|
|
@@ -213,7 +211,7 @@ It returns a handle for that button. Among its methods:
|
|
|
213
211
|
| Method | Description |
|
|
214
212
|
|--------|-------------|
|
|
215
213
|
| `setGarment(garmentUrl)` | Point the button at another garment — a colour switch. The button's previous result is forgotten; a try-on still running for the previous garment is shown when it lands. |
|
|
216
|
-
| `openSize()` | Open the dialog on the customer's size, exactly as the size on the button does — for a "check your size" control of your own. Does nothing while the button has no size to show. |
|
|
214
|
+
| `openSize()` | Open the dialog on the customer's size, exactly as the size on the button does — for a "check your size" control of your own. Before a try-on it shows the garment, the size and **Try on**. Does nothing while the button has no size to show. |
|
|
217
215
|
|
|
218
216
|
> **Removed in 0.2.0:** `targetImageId`. It named an `<img>` on your page for the
|
|
219
217
|
> widget to overwrite with the result, and it is gone — the widget shows every
|
|
@@ -222,51 +220,40 @@ It returns a handle for that button. Among its methods:
|
|
|
222
220
|
|
|
223
221
|
---
|
|
224
222
|
|
|
225
|
-
##
|
|
223
|
+
## Appearance
|
|
226
224
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
### Theme Modes
|
|
225
|
+
One **appearance** styles the whole widget: the try-on button, the result dialog, and the sign-in and upload screens. It is the same closed set of choices a Shopify merchant makes on the Souldi block, so every embed has one appearance contract.
|
|
230
226
|
|
|
231
227
|
```javascript
|
|
232
228
|
VirtualTryOn.init({
|
|
233
229
|
tenantApiKey: 'your_key',
|
|
234
|
-
|
|
235
|
-
|
|
230
|
+
appearance: {
|
|
231
|
+
ink: '#7A1F5C', // your brand colour, as six hex digits
|
|
232
|
+
fill: 'filled', // 'outlined' (default) or 'filled'
|
|
233
|
+
corners: 'rounded', // 'square' (default), 'rounded' or 'pill'
|
|
234
|
+
font: 'theme', // 'souldi' (default) or 'theme'
|
|
236
235
|
},
|
|
237
236
|
});
|
|
238
237
|
```
|
|
239
238
|
|
|
240
|
-
|
|
239
|
+
| Property | Values | Default | What it does |
|
|
240
|
+
|----------|--------|---------|--------------|
|
|
241
|
+
| `ink` | `#rrggbb` | `#1F201F` | Colours the accents only: the primary action, selected states, the size stamp and focus rings. Never body text. |
|
|
242
|
+
| `fill` | `'outlined'` \| `'filled'` | `'outlined'` | The primary action is the ink on white (outlined) or white on the ink (filled). Secondary actions stay outlined. |
|
|
243
|
+
| `corners` | `'square'` \| `'rounded'` \| `'pill'` | `'square'` | Buttons take the shape as chosen. Cards and sheets cap it (0, 12px and 20px), so a dialog never turns into an oval. |
|
|
244
|
+
| `font` | `'souldi'` \| `'theme'` | `'souldi'` | The widget's own faces ([see Fonts](#fonts)), or your page's font, inherited everywhere, headings included. |
|
|
241
245
|
|
|
242
|
-
|
|
246
|
+
Each value is checked on its own. A missing or unknown value falls back to its default and never throws, so a typo can't break your page. An `ink` that isn't six hex digits (`#FFF`, `red`) falls back to the default ink.
|
|
243
247
|
|
|
244
|
-
|
|
245
|
-
VirtualTryOn.init({
|
|
246
|
-
tenantApiKey: 'your_key',
|
|
247
|
-
theme: {
|
|
248
|
-
mode: 'dark',
|
|
249
|
-
accentColor: '#7C3AED', // Primary button & accent color
|
|
250
|
-
accentTextColor: '#FFFFFF', // Text color on accent buttons
|
|
251
|
-
borderRadius: '8px', // Border radius for cards & modals
|
|
252
|
-
fontFamily: '"Inter", sans-serif',
|
|
253
|
-
headingFontFamily: '"Playfair Display", serif',
|
|
254
|
-
buttonOutlineColor: '#7C3AED', // Outline on the split button
|
|
255
|
-
},
|
|
256
|
-
});
|
|
257
|
-
```
|
|
248
|
+
**Pick an ink that reads on white.** The ink is used exactly as you give it. If it is below 4.5:1 contrast against white (the WCAG AA minimum for text), the widget logs one console warning and still uses it. It doesn't darken the colour for you.
|
|
258
249
|
|
|
259
|
-
|
|
250
|
+
There is no `size` option. One that is passed is ignored.
|
|
260
251
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
| `borderRadius` | CSS value | `0` (square) | Corner rounding for buttons; cards take about 1.7× it |
|
|
267
|
-
| `fontFamily` | CSS font stack | `'Souldi Manrope', system-ui, sans-serif` | Body text font |
|
|
268
|
-
| `headingFontFamily` | CSS font stack | `'Souldi Newsreader', serif` | Heading font |
|
|
269
|
-
| `buttonOutlineColor` | CSS color | `transparent` | Border color on the try-on button |
|
|
252
|
+
> **Removed in 0.5.0:** `theme` (`mode`, `accentColor`, `accentTextColor`,
|
|
253
|
+
> `borderRadius`, `fontFamily`, `headingFontFamily`, `buttonOutlineColor`) and
|
|
254
|
+
> the dark mode it selected. The widget is light-only. A `theme` that is still
|
|
255
|
+
> passed is ignored and logs one console warning. Move to `appearance`. See the
|
|
256
|
+
> [changelog](CHANGELOG.md).
|
|
270
257
|
|
|
271
258
|
All UI is rendered inside a **Shadow DOM**, so your site's CSS will never leak into the widget and the widget will never break your styles.
|
|
272
259
|
|
|
@@ -309,7 +296,7 @@ The widget then contacts no one but you for its fonts, and a `font-src 'self'` p
|
|
|
309
296
|
<script>
|
|
310
297
|
VirtualTryOn.init({
|
|
311
298
|
tenantApiKey: 'pk_live_abc123',
|
|
312
|
-
|
|
299
|
+
appearance: { ink: '#2563EB', corners: 'rounded' },
|
|
313
300
|
onGenerationSuccess: (url) => {
|
|
314
301
|
console.log('Try-on result:', url);
|
|
315
302
|
},
|
|
@@ -338,7 +325,7 @@ The widget then contacts no one but you for its fonts, and a `font-src 'self'` p
|
|
|
338
325
|
|
|
339
326
|
Your API keys are available in your **Souldi Dashboard**. The publishable key is scoped to the domains you whitelist — it cannot be used from unauthorized origins.
|
|
340
327
|
|
|
341
|
-
> **Security note:** Authentication and AI generation go through your Souldi API endpoint and are handled server-side. The browser also reaches Souldi's own storage, through short-lived signed URLs the API issues: a photo upload goes straight to it, and result images come from Souldi's image CDN. Beyond Souldi, the browser contacts unpkg (the package's CDN) for
|
|
328
|
+
> **Security note:** Authentication and AI generation go through your Souldi API endpoint and are handled server-side. The browser also reaches Souldi's own storage, through short-lived signed URLs the API issues: a photo upload goes straight to it, and result images come from Souldi's image CDN. Beyond Souldi, the browser contacts unpkg (the package's CDN) for its fonts, unless you set [`fontsUrl`](#fonts). No other third-party service is contacted.
|
|
342
329
|
|
|
343
330
|
---
|
|
344
331
|
|