@nadicodeai/design-system 0.10.0 → 0.10.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/AGENTS.md +2 -1
- package/dist/css/chat-autoplay.js +45 -43
- package/dist/css/motion.css +40 -2
- package/package.json +4 -1
package/AGENTS.md
CHANGED
|
@@ -76,6 +76,7 @@ Generated-artifact and runtime-JS prohibitions live in the Forbidden Patterns ta
|
|
|
76
76
|
| Command | When |
|
|
77
77
|
| --- | --- |
|
|
78
78
|
| `npm run lint:design` | After any `DESIGN.md` edit |
|
|
79
|
+
| `npm run lint` | After editing JavaScript or TypeScript tooling, tests, or the chat enhancer |
|
|
79
80
|
| `npm run build` | After any `DESIGN.md` edit; regenerates `dist/tokens/nadicode.dtcg.json` and `dist/tailwind/nadicode.tailwind.json` |
|
|
80
81
|
| `npm run generate:icons` | After bumping `lucide-static`; rebakes `dist/icons/` (also runs inside `npm run build`) |
|
|
81
82
|
| `npm run test` | Before handoff. Browser-free by design — safe for agents and CI |
|
|
@@ -86,7 +87,7 @@ Generated-artifact and runtime-JS prohibitions live in the Forbidden Patterns ta
|
|
|
86
87
|
Before claiming work complete (from this directory, or with `-w design-system` from the repo root):
|
|
87
88
|
|
|
88
89
|
```bash
|
|
89
|
-
npm run lint:design && npm run build && npm run test
|
|
90
|
+
npm run lint:design && npm run lint && npm run build && npm run test
|
|
90
91
|
```
|
|
91
92
|
|
|
92
93
|
`npm run test` MUST stay browser-free: browser-driven tests live in `tests/visual/*.visual.ts` (outside the default `*.test.ts` glob) and run only via `npm run test:visual`. Never add a browser test to the default suite — parallel headless sessions inside the gate are what orphaned Chrome and hung it; that is the real constraint, not `agent-browser` itself.
|
|
@@ -176,10 +176,10 @@
|
|
|
176
176
|
};
|
|
177
177
|
|
|
178
178
|
ChatThreadPlayer.prototype._sleep = function (ms) {
|
|
179
|
-
var
|
|
179
|
+
var timers = this.timers;
|
|
180
180
|
return new Promise(function (resolve) {
|
|
181
181
|
var t = setTimeout(resolve, ms);
|
|
182
|
-
|
|
182
|
+
timers.push(t);
|
|
183
183
|
});
|
|
184
184
|
};
|
|
185
185
|
|
|
@@ -272,34 +272,33 @@
|
|
|
272
272
|
// continuation can detect that the player was stopped-and-re-armed underneath
|
|
273
273
|
// it (see the `generation` note in the constructor).
|
|
274
274
|
ChatThreadPlayer.prototype._runLoop = function () {
|
|
275
|
-
var self = this;
|
|
276
275
|
var myGen = this.generation;
|
|
277
|
-
|
|
278
|
-
return
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
if (stale()) return Promise.resolve();
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
return
|
|
276
|
+
function stale(player) {
|
|
277
|
+
return player.cancelled || player.generation !== myGen;
|
|
278
|
+
}
|
|
279
|
+
function loop(player) {
|
|
280
|
+
if (stale(player)) return Promise.resolve();
|
|
281
|
+
player._hideAllMessages();
|
|
282
|
+
player._hideTyping();
|
|
283
|
+
player._setMeter(0);
|
|
284
|
+
return player
|
|
286
285
|
._sleep(START_DELAY)
|
|
287
286
|
.then(function () {
|
|
288
287
|
var chain = Promise.resolve();
|
|
289
|
-
|
|
288
|
+
player.messages.forEach(function (msg, i) {
|
|
290
289
|
chain = chain.then(function () {
|
|
291
|
-
if (stale()) return;
|
|
290
|
+
if (stale(player)) return;
|
|
292
291
|
var role = roleOf(msg);
|
|
293
292
|
var t = defaultTiming(role, (msg.textContent || "").trim().length);
|
|
294
293
|
var step = function () {
|
|
295
|
-
if (stale()) return;
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
return
|
|
294
|
+
if (stale(player)) return;
|
|
295
|
+
player._hideTyping();
|
|
296
|
+
player._reveal(msg, i);
|
|
297
|
+
return player._sleep(t.holdMs);
|
|
299
298
|
};
|
|
300
299
|
if (t.typingMs > 0) {
|
|
301
|
-
|
|
302
|
-
return
|
|
300
|
+
player._showTyping();
|
|
301
|
+
return player._sleep(t.typingMs).then(step);
|
|
303
302
|
}
|
|
304
303
|
return step();
|
|
305
304
|
});
|
|
@@ -307,17 +306,18 @@
|
|
|
307
306
|
return chain;
|
|
308
307
|
})
|
|
309
308
|
.then(function () {
|
|
310
|
-
if (stale()) return;
|
|
309
|
+
if (stale(player)) return;
|
|
311
310
|
// Mark completion before the hold so the observer entry branch can
|
|
312
311
|
// show the final state on re-entry without replaying from blank.
|
|
313
|
-
|
|
314
|
-
return
|
|
312
|
+
player.completedOnce = true;
|
|
313
|
+
return player._sleep(LOOP_HOLD);
|
|
315
314
|
})
|
|
316
315
|
.then(function () {
|
|
317
|
-
if (stale()) return;
|
|
318
|
-
return loop();
|
|
316
|
+
if (stale(player)) return;
|
|
317
|
+
return loop(player);
|
|
319
318
|
});
|
|
320
|
-
}
|
|
319
|
+
}
|
|
320
|
+
return loop(this);
|
|
321
321
|
};
|
|
322
322
|
|
|
323
323
|
ChatThreadPlayer.prototype.play = function () {
|
|
@@ -363,7 +363,6 @@
|
|
|
363
363
|
// Never called in reduced-motion mode (contract (b): no button for a path
|
|
364
364
|
// that plays nothing).
|
|
365
365
|
ChatThreadPlayer.prototype._injectPauseButton = function () {
|
|
366
|
-
var self = this;
|
|
367
366
|
var thread = this.thread;
|
|
368
367
|
var subject = thread.getAttribute("data-nc-autoplay-name") || "demonstration";
|
|
369
368
|
var btn = document.createElement("button");
|
|
@@ -371,22 +370,25 @@
|
|
|
371
370
|
btn.className = "nc-chat-pause";
|
|
372
371
|
btn.setAttribute("aria-pressed", "false");
|
|
373
372
|
btn.textContent = "Pause " + subject;
|
|
374
|
-
btn.addEventListener(
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
373
|
+
btn.addEventListener(
|
|
374
|
+
"click",
|
|
375
|
+
function () {
|
|
376
|
+
if (this.userPaused) {
|
|
377
|
+
// Resume: clear the latch, rearm and play.
|
|
378
|
+
this.userPaused = false;
|
|
379
|
+
btn.setAttribute("aria-pressed", "false");
|
|
380
|
+
btn.textContent = "Pause " + subject;
|
|
381
|
+
this.rearm();
|
|
382
|
+
this.play();
|
|
383
|
+
} else {
|
|
384
|
+
// Pause: set the latch, stop and reveal full content.
|
|
385
|
+
this.userPaused = true;
|
|
386
|
+
btn.setAttribute("aria-pressed", "true");
|
|
387
|
+
btn.textContent = "Resume " + subject;
|
|
388
|
+
this.stop();
|
|
389
|
+
}
|
|
390
|
+
}.bind(this),
|
|
391
|
+
);
|
|
390
392
|
// Insert before the body so the button sits above the scrolling content.
|
|
391
393
|
var body = thread.querySelector(".nc-chat-thread-body");
|
|
392
394
|
if (body) {
|
package/dist/css/motion.css
CHANGED
|
@@ -23,8 +23,26 @@
|
|
|
23
23
|
* built-in where a brand curve exists.
|
|
24
24
|
* - State feedback completes in 150–200 ms; press <= 160 ms; only the
|
|
25
25
|
* demonstration (Type) may run longer.
|
|
26
|
+
* - DURATION TIERS: UI feedback stays under 300 ms. Medium-frequency
|
|
27
|
+
* product motion (list reflow, rolling numbers) uses --nc-duration-reflow
|
|
28
|
+
* (250 ms); --nc-duration-msg-in (360 ms) is reserved for the one-time
|
|
29
|
+
* Settle entrance, never for motion the operator meets on every refetch.
|
|
30
|
+
* - ASYMMETRIC TIMING: a deliberate action opens slower than it dismisses.
|
|
31
|
+
* Popovers/menus/dialogs open at --nc-duration-open (220 ms) and close at
|
|
32
|
+
* --nc-duration-close (140 ms); the open invites, the close gets out of the
|
|
33
|
+
* way.
|
|
34
|
+
* - ORIGIN-AWARE: a surface that springs from a trigger scales FROM that
|
|
35
|
+
* trigger (transform-origin at the anchor), not from its own centre — a
|
|
36
|
+
* popover grows out of the button that opened it.
|
|
37
|
+
* - INTERRUPTIBLE: gesture- or toast-driven motion retargets from its
|
|
38
|
+
* current position; it is never restarted from a fixed keyframe mid-flight.
|
|
26
39
|
* - Nothing enters from scale(0) or from nothing.
|
|
27
|
-
* -
|
|
40
|
+
* - PROPERTY DISCIPLINE: movement is compositor-only (opacity/transform).
|
|
41
|
+
* The Confirm move may additionally transition background-color,
|
|
42
|
+
* border-color, and color at --nc-duration-confirm — colour is the state
|
|
43
|
+
* change itself, and a paint-only transition triggers no reflow. Never
|
|
44
|
+
* animate a property that shifts surrounding layout; no will-change;
|
|
45
|
+
* zero CLS.
|
|
28
46
|
* - FREQUENCY RULE: motion a user meets repeatedly (product UI, keyboard-
|
|
29
47
|
* triggered actions) does not animate. Delight is reserved for rarely-
|
|
30
48
|
* seen surfaces (marketing pages, first runs). Product surfaces that
|
|
@@ -32,7 +50,9 @@
|
|
|
32
50
|
*
|
|
33
51
|
* BANNED: parallax, scroll-jacking, floating/bobbing idle loops, gradient
|
|
34
52
|
* shimmer, staggered letter reveals, anything that moves a seam or hairline
|
|
35
|
-
* after it settles, more than one theatrical moment per page
|
|
53
|
+
* after it settles, more than one theatrical moment per page, and
|
|
54
|
+
* `transition: all` / `transition-property: all` (name the properties you
|
|
55
|
+
* animate — a blanket transition drags unintended props off the compositor).
|
|
36
56
|
*
|
|
37
57
|
* REDUCED MOTION: prefers-reduced-motion removes movement entirely — Settle
|
|
38
58
|
* and Type loops gone, Confirm instant, press still. The page must read
|
|
@@ -68,6 +88,24 @@
|
|
|
68
88
|
--nc-duration-pulse: 1.6s;
|
|
69
89
|
--nc-duration-confirm: 180ms;
|
|
70
90
|
|
|
91
|
+
/* Frequency-tiered UI cadence (craft rule, not a prototype extraction).
|
|
92
|
+
--nc-duration-reflow : 250ms ← medium-frequency product motion (list
|
|
93
|
+
reflow, rolling numbers) — kept under the
|
|
94
|
+
300ms UI-feedback ceiling so it never feels
|
|
95
|
+
sluggish on a surface met on every refetch.
|
|
96
|
+
360ms (--nc-duration-msg-in) stays reserved
|
|
97
|
+
for the one-time Settle entrance. */
|
|
98
|
+
--nc-duration-reflow: 250ms;
|
|
99
|
+
|
|
100
|
+
/* Asymmetric open/close cadence (craft-rule tokens for the ASYMMETRIC TIMING
|
|
101
|
+
rule above — a deliberate action opens slower than it dismisses).
|
|
102
|
+
--nc-duration-open : 220ms drives a popover/menu/dialog opening; the open
|
|
103
|
+
invites.
|
|
104
|
+
--nc-duration-close: 140ms drives its dismissal; the close gets out of the
|
|
105
|
+
way. Both stay under the 300ms UI-feedback ceiling. */
|
|
106
|
+
--nc-duration-open: 220ms;
|
|
107
|
+
--nc-duration-close: 140ms;
|
|
108
|
+
|
|
71
109
|
/* Easings — extracted from chat-anim.jsx.
|
|
72
110
|
--nc-ease-emphasized : cubic-bezier(.3,.7,.2,1) ← footer width transition;
|
|
73
111
|
the brand's settle curve.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nadicodeai/design-system",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"generate:icons": "tsx scripts/generate-icons.ts",
|
|
47
47
|
"generate:favicons": "tsx scripts/generate-favicons.ts",
|
|
48
48
|
"lint:design": "design.md lint DESIGN.md",
|
|
49
|
+
"lint": "eslint --max-warnings=0",
|
|
49
50
|
"test": "vitest run",
|
|
50
51
|
"test:visual": "vitest run --config vitest.visual.config.ts",
|
|
51
52
|
"prepublishOnly": "npm run build"
|
|
@@ -54,10 +55,12 @@
|
|
|
54
55
|
"@google/design.md": "0.1.1",
|
|
55
56
|
"@types/node": "^22",
|
|
56
57
|
"agent-browser": "^0.31.1",
|
|
58
|
+
"eslint": "^9.39.4",
|
|
57
59
|
"lucide-static": "^1.17.0",
|
|
58
60
|
"style-dictionary": "^5.4.3",
|
|
59
61
|
"tsx": "^4.21.0",
|
|
60
62
|
"typescript": "^5",
|
|
63
|
+
"typescript-eslint": "^8.46.0",
|
|
61
64
|
"vitest": "^4.1.6"
|
|
62
65
|
}
|
|
63
66
|
}
|