@pieai/swimmer-ui-kit 1.3.1 → 1.4.0
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 +69 -6
- package/README.md +23 -4
- package/bin/swimmer-ui-assets.mjs +83 -0
- package/dist/index.d.ts +20 -3
- package/dist/index.js +515 -498
- package/dist/styles.css +1 -1
- package/package.json +25 -22
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,72 @@
|
|
|
3
3
|
All notable changes to `@pieai/swimmer-ui-kit`.
|
|
4
4
|
Format: [Keep a Changelog](https://keepachangelog.com); versioning: semver.
|
|
5
5
|
|
|
6
|
+
## 1.4.0 — 2026-08-22
|
|
7
|
+
|
|
8
|
+
The kit shipped an icon set that no consumer could see. Nothing was broken and
|
|
9
|
+
nothing errored, which was the problem: with no setup the kit draws lettered
|
|
10
|
+
placeholder squares, and a placeholder that looks deliberate gets shipped. A
|
|
11
|
+
product could put eight identical coloured squares in its navigation and never
|
|
12
|
+
file a bug, because it reads as a design choice rather than a missing step.
|
|
13
|
+
|
|
14
|
+
The sculpted PNGs were never missing — 350 files travel inside the package, in
|
|
15
|
+
`dist/assets`. What was missing was any route from there to a path the host
|
|
16
|
+
actually serves. `CLAY_ASSET_BASE_PATH` names an absolute URL on the
|
|
17
|
+
*consumer's* origin, and nothing copied, exported or explained it. The kit's own
|
|
18
|
+
Storybook worked throughout, because its assets sit in `public/`.
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- `swimmer-ui-assets [dir] [--base=…] [--force]` — a `bin` that copies the
|
|
23
|
+
sculpted set into a host's static root. Bundler-agnostic and explicit; no
|
|
24
|
+
postinstall, because a package that writes into your repo on install is worse
|
|
25
|
+
than the problem it solves.
|
|
26
|
+
- `"./assets/*"` export, so bundler-based consumers can import an individual
|
|
27
|
+
file and get a content-hashed URL instead of copying the whole tree.
|
|
28
|
+
- `setClayAssetBasePath()` / `getClayAssetBasePath()` for a CDN or a sub-path
|
|
29
|
+
deploy. Resolution rebases on the way out, so the variant table stays a
|
|
30
|
+
single source of truth.
|
|
31
|
+
- A one-time `console.warn` the first time a placeholder is actually drawn,
|
|
32
|
+
naming the two commands that fix it, and `acknowledgeClayPlaceholders()` for
|
|
33
|
+
anyone using them on purpose.
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- README states plainly that the default is placeholders rather than the icon
|
|
38
|
+
set, and records that the sculpted family is 96px art which cannot take a
|
|
39
|
+
`currentColor` tint — so it is the wrong family for a dense nav rail or
|
|
40
|
+
toolbar however correctly it is served.
|
|
41
|
+
|
|
42
|
+
### Compatibility
|
|
43
|
+
|
|
44
|
+
Additive only. Defaults are unchanged, so every existing consumer renders
|
|
45
|
+
exactly what it rendered before and gains one console line telling it why.
|
|
46
|
+
|
|
47
|
+
## 1.3.2 — 2026-08-17
|
|
48
|
+
|
|
49
|
+
Follows 1.3.1 with the contrast work that release only half did. The new off
|
|
50
|
+
state paired a muted track with a `--game-ui-panel-strong` bead, which is a
|
|
51
|
+
light colour on the light theme and a dark one on night — so on night the bead
|
|
52
|
+
measured 1.8:1 against its own track and all but disappeared in exactly the
|
|
53
|
+
state it had just been added to show.
|
|
54
|
+
|
|
55
|
+
### Fixed
|
|
56
|
+
|
|
57
|
+
- The off track is now an inset well (`--game-ui-surface` plus
|
|
58
|
+
`--game-ui-shadow-inset`) rather than a muted fill, so off and on differ in
|
|
59
|
+
form — recessed versus filled — and not only in hue. That difference is the
|
|
60
|
+
kit's own existing vocabulary; `.game-ui-progress-track` is drawn the same
|
|
61
|
+
way.
|
|
62
|
+
- The bead is `--game-ui-text`, the one token guaranteed to read against every
|
|
63
|
+
surface its own theme defines, replacing `--game-ui-panel-strong`, which
|
|
64
|
+
tracks the surface and therefore inverts between themes. Measured on night:
|
|
65
|
+
13.1:1 bead against the off track, up from 1.8:1, and 6.1:1 between the two
|
|
66
|
+
track colours.
|
|
67
|
+
- The regression test now asserts the bead's inset offset flips sign between
|
|
68
|
+
the two states. Position is the half of the state that survives both palettes
|
|
69
|
+
and both kinds of colour vision, and it is also the half a future edit is
|
|
70
|
+
most likely to drop.
|
|
71
|
+
|
|
6
72
|
## 1.3.1 — 2026-08-17
|
|
7
73
|
|
|
8
74
|
Gives `<GameToggle>` a visible off state. Since the component shipped, its
|
|
@@ -17,12 +83,9 @@ readers the truth and told everyone else nothing.
|
|
|
17
83
|
|
|
18
84
|
- `.game-ui-toggle-track` now has two states. Off is a muted track with the
|
|
19
85
|
bead inset on the left; on fills the track with `--game-ui-secondary` and
|
|
20
|
-
moves the bead to the right
|
|
21
|
-
|
|
22
|
-
- The bead is
|
|
23
|
-
It is the mark that says which side the switch is sitting on, so it has to
|
|
24
|
-
clear the 3:1 non-text contrast floor against the track in both states, and a
|
|
25
|
-
translucent bead over an unknown track colour cannot promise that.
|
|
86
|
+
moves the bead to the right, so the state is carried by position as well as
|
|
87
|
+
by colour.
|
|
88
|
+
- The bead is opaque rather than a 62% mix of the panel colour.
|
|
26
89
|
- `.game-ui-toggle-track` joins the `prefers-reduced-motion: reduce` list, so
|
|
27
90
|
the state change is instant for readers who asked for that.
|
|
28
91
|
|
package/README.md
CHANGED
|
@@ -71,10 +71,29 @@ import '@pieai/swimmer-ui-kit/tailwind.css';
|
|
|
71
71
|
- **Official themes**: light (default) and `night`
|
|
72
72
|
(`<html data-game-ui-theme="night">`). Downstream theming = overriding
|
|
73
73
|
semantic tokens; see the design system guide.
|
|
74
|
-
- **Clay assets**:
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
74
|
+
- **Clay assets**: two lines of setup, and **skipping them is not a no-op**.
|
|
75
|
+
Out of the box the kit draws *placeholders* — one rounded square per icon
|
|
76
|
+
with a letter in it — not the icon set. They exist so a fresh install
|
|
77
|
+
renders something instead of a broken image, and they are not shippable.
|
|
78
|
+
The real sculpted PNGs travel inside the package and need serving:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
npx swimmer-ui-assets public # copies dist/assets into your static root
|
|
82
|
+
```
|
|
83
|
+
```ts
|
|
84
|
+
import { setClayAssetMode } from '@pieai/swimmer-ui-kit';
|
|
85
|
+
setClayAssetMode('source'); // once, at your entry
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Serving them somewhere else — a CDN, a sub-path deploy — is
|
|
89
|
+
`setClayAssetBasePath('/my/path')`, and `swimmer-ui-assets public --base=/my/path`
|
|
90
|
+
mirrors the layout to match. If you *want* placeholders, say so with
|
|
91
|
+
`acknowledgeClayPlaceholders()` and the console notice goes quiet.
|
|
92
|
+
|
|
93
|
+
Note on sizing: the sculpted family is 96px art. Below roughly 24px it turns
|
|
94
|
+
to mud, and being PNG it cannot take a `currentColor` tint, so it is the
|
|
95
|
+
wrong family for a dense navigation rail or toolbar. Use the `line` style
|
|
96
|
+
there, or your own glyphs.
|
|
78
97
|
- **Audio helper**: `playGameInteractionSound` (SSR-safe, opt-in via the
|
|
79
98
|
`sound` prop on `GameButton`).
|
|
80
99
|
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Copy the sculpted clay asset set out of the package and into a directory the
|
|
3
|
+
// consuming app actually serves.
|
|
4
|
+
//
|
|
5
|
+
// The kit resolves sculpted icons to absolute URLs under CLAY_ASSET_BASE_PATH
|
|
6
|
+
// ('/assets/game/ui/clay/…'), which is a path on the *host's* origin. The files
|
|
7
|
+
// themselves ship inside the package, in dist/assets. Nothing was closing that
|
|
8
|
+
// gap, so every consumer had to discover it, and the default placeholder mode
|
|
9
|
+
// meant the failure looked like a design choice rather than a missing step.
|
|
10
|
+
//
|
|
11
|
+
// Usage: swimmer-ui-assets [targetDir] [--base=/assets/game/ui/clay/...] [--force]
|
|
12
|
+
// targetDir where the host serves static files from. Default "public".
|
|
13
|
+
// --base the URL path to mirror under targetDir. Defaults to the kit's
|
|
14
|
+
// CLAY_ASSET_BASE_PATH, which is what setClayAssetMode('source')
|
|
15
|
+
// expects with no further configuration.
|
|
16
|
+
// --force overwrite files that already exist (default: skip them).
|
|
17
|
+
//
|
|
18
|
+
// After running this, call setClayAssetMode('source') once at your app entry.
|
|
19
|
+
import { cpSync, existsSync, mkdirSync, readdirSync, statSync } from 'node:fs';
|
|
20
|
+
import { dirname, join, resolve } from 'node:path';
|
|
21
|
+
import { fileURLToPath } from 'node:url';
|
|
22
|
+
|
|
23
|
+
const DEFAULT_BASE = '/assets/game/ui/clay/phase03-clay-kit';
|
|
24
|
+
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
25
|
+
|
|
26
|
+
const args = process.argv.slice(2);
|
|
27
|
+
const flags = new Set(args.filter((a) => a.startsWith('--')));
|
|
28
|
+
const positional = args.filter((a) => !a.startsWith('--'));
|
|
29
|
+
|
|
30
|
+
// Two different paths that are easy to conflate: where the files sit inside
|
|
31
|
+
// this package (fixed, mirrors the default URL under dist/) and where the host
|
|
32
|
+
// will serve them from (--base, defaults to the same thing so the common case
|
|
33
|
+
// needs no configuration).
|
|
34
|
+
const trim = (p) => p.replace(/^\/+|\/+$/g, '');
|
|
35
|
+
const baseFlag = args.find((a) => a.startsWith('--base='));
|
|
36
|
+
const servedBase = trim(baseFlag ? baseFlag.slice('--base='.length) : DEFAULT_BASE);
|
|
37
|
+
const targetRoot = resolve(process.cwd(), positional[0] ?? 'public');
|
|
38
|
+
const force = flags.has('--force');
|
|
39
|
+
|
|
40
|
+
const source = join(packageRoot, 'dist', trim(DEFAULT_BASE));
|
|
41
|
+
if (!existsSync(source)) {
|
|
42
|
+
console.error(`swimmer-ui-assets: nothing to copy — ${source} does not exist.`);
|
|
43
|
+
console.error('This package may have been installed without its dist/assets tree.');
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const destination = join(targetRoot, servedBase);
|
|
48
|
+
|
|
49
|
+
function count(dir) {
|
|
50
|
+
let files = 0;
|
|
51
|
+
let bytes = 0;
|
|
52
|
+
for (const entry of readdirSync(dir)) {
|
|
53
|
+
const full = join(dir, entry);
|
|
54
|
+
const stat = statSync(full);
|
|
55
|
+
if (stat.isDirectory()) {
|
|
56
|
+
const inner = count(full);
|
|
57
|
+
files += inner.files;
|
|
58
|
+
bytes += inner.bytes;
|
|
59
|
+
} else {
|
|
60
|
+
files += 1;
|
|
61
|
+
bytes += stat.size;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return { files, bytes };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const { files, bytes } = count(source);
|
|
68
|
+
|
|
69
|
+
mkdirSync(dirname(destination), { recursive: true });
|
|
70
|
+
cpSync(source, destination, { recursive: true, force, errorOnExist: false });
|
|
71
|
+
|
|
72
|
+
const megabytes = (bytes / 1024 / 1024).toFixed(1);
|
|
73
|
+
console.log(`swimmer-ui-assets: copied ${files} files (${megabytes} MB)`);
|
|
74
|
+
console.log(` from ${source}`);
|
|
75
|
+
console.log(` to ${destination}`);
|
|
76
|
+
console.log('');
|
|
77
|
+
console.log('Now call this once at your app entry, or the kit keeps drawing placeholders:');
|
|
78
|
+
console.log(" import { setClayAssetMode } from '@pieai/swimmer-ui-kit';");
|
|
79
|
+
console.log(" setClayAssetMode('source');");
|
|
80
|
+
if (!force) {
|
|
81
|
+
console.log('');
|
|
82
|
+
console.log('Existing files were left alone. Re-run with --force to overwrite.');
|
|
83
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,9 @@ import { ReactNode } from 'react';
|
|
|
6
6
|
import { RefAttributes } from 'react';
|
|
7
7
|
import { TextareaHTMLAttributes } from 'react';
|
|
8
8
|
|
|
9
|
+
/** Opt in to placeholders on purpose, and stop being told about it. */
|
|
10
|
+
export declare function acknowledgeClayPlaceholders(): void;
|
|
11
|
+
|
|
9
12
|
export declare const CLAY_ASSET_BASE_PATH: "/assets/game/ui/clay/phase03-clay-kit";
|
|
10
13
|
|
|
11
14
|
export declare const CLAY_ASSET_SIZE_TOKENS: {
|
|
@@ -1852,6 +1855,8 @@ export declare interface GameWindowPanelProps extends Omit<HTMLAttributes<HTMLEl
|
|
|
1852
1855
|
|
|
1853
1856
|
export declare type GameWindowState = 'normal' | 'minimized' | 'maximized';
|
|
1854
1857
|
|
|
1858
|
+
export declare function getClayAssetBasePath(): string;
|
|
1859
|
+
|
|
1855
1860
|
export declare function getClayAssetMode(): ClayAssetMode;
|
|
1856
1861
|
|
|
1857
1862
|
export declare function getClayCatalogPaths(): typeof CLAY_ASSETS.catalog;
|
|
@@ -1867,11 +1872,23 @@ export declare function playGameInteractionSound(options?: GameInteractionSoundO
|
|
|
1867
1872
|
|
|
1868
1873
|
export declare function playGameInteractionSoundForContext(audioContext: GameInteractionAudioContext, options?: GameInteractionSoundOptions): boolean;
|
|
1869
1874
|
|
|
1875
|
+
/**
|
|
1876
|
+
* Point the sculpted set at somewhere other than the default path — a CDN, a
|
|
1877
|
+
* sub-path deploy, a host whose static root is not `/`. Pass no trailing slash.
|
|
1878
|
+
*
|
|
1879
|
+
* The copy command mirrors the default layout, so a consumer that runs
|
|
1880
|
+
* `swimmer-ui-assets public` never needs this.
|
|
1881
|
+
*/
|
|
1882
|
+
export declare function setClayAssetBasePath(basePath: string): void;
|
|
1883
|
+
|
|
1870
1884
|
/**
|
|
1871
1885
|
* Switch how clay icons resolve globally.
|
|
1872
|
-
* - 'inline' (default):
|
|
1873
|
-
*
|
|
1874
|
-
*
|
|
1886
|
+
* - 'inline' (default): lettered placeholders. They are not the icon set —
|
|
1887
|
+
* every one is the same rounded square with a different glyph and colour.
|
|
1888
|
+
* They exist so a fresh install renders *something* rather than a broken
|
|
1889
|
+
* image, and shipping them is a bug.
|
|
1890
|
+
* - 'source': the real sculpted PNGs under the base path. Get the files there
|
|
1891
|
+
* with `npx swimmer-ui-assets <publicDir>`.
|
|
1875
1892
|
*/
|
|
1876
1893
|
export declare function setClayAssetMode(mode: ClayAssetMode): void;
|
|
1877
1894
|
|