dock: the practice fold morphs like teaching — every docked variant carries its fold state
Owner report 2026-10-06: practice sessions 'open and close instantly'
while teaching morphs. Live instrumented run (Element.prototype.animate
wrapped, three transitions recorded) proved the pose/conclude morphs
fire identically in practice (54px↔327.5px, 120ms, same easing) — the
asymmetry was the FOLD: the shell's height-morph key read
`mode:${pending ? expanded : "-"}` and pending (the question's tool
call) is null in practice, so a practice dock's bar↔form toggle never
changed the key — no height morph, instant jump, only the 160ms content
fade. The ternary predates practice sessions (aeef28d, August); the
remembered Oct-5 fix (ec8315b) added the fade only. Round 16's pause
flow raised fold exposure until the seam surfaced.
Fix: dockStateKey(mode, docked, expanded) — components/dock-state-key.ts
— widened to !!pending || !!practice (the fold machine's own ownership
rule). Typing growth keeps the key (never animates); the fold rides the
exact branch teaching has used for months. Pinned in
tests/dock-state-key.test.ts.
This commit is contained in:
+6
-1
@@ -24,6 +24,7 @@ import { artifactRenderers } from "@/components/artifacts";
|
|||||||
import { SubagentSteerCard } from "@/components/artifacts/steer-card";
|
import { SubagentSteerCard } from "@/components/artifacts/steer-card";
|
||||||
import { Composer } from "@/components/composer";
|
import { Composer } from "@/components/composer";
|
||||||
import { useEnterSubmits } from "@/components/enter-submits";
|
import { useEnterSubmits } from "@/components/enter-submits";
|
||||||
|
import { dockStateKey } from "@/components/dock-state-key";
|
||||||
import { QueuedSteersBody, type QueuedSteerLine } from "@/components/queued-steers";
|
import { QueuedSteersBody, type QueuedSteerLine } from "@/components/queued-steers";
|
||||||
import { DictationBar, joinDictation, useDictation } from "@/components/dictation";
|
import { DictationBar, joinDictation, useDictation } from "@/components/dictation";
|
||||||
import { JobsContext } from "@/components/jobs-context";
|
import { JobsContext } from "@/components/jobs-context";
|
||||||
@@ -2108,7 +2109,11 @@ function DockOverlay({
|
|||||||
{ duration: 160, easing: "cubic-bezier(0.2, 0.8, 0.2, 1)" },
|
{ duration: 160, easing: "cubic-bezier(0.2, 0.8, 0.2, 1)" },
|
||||||
);
|
);
|
||||||
}, [expanded]);
|
}, [expanded]);
|
||||||
const stateKey = `${mode}:${pending ? expanded : "-"}`;
|
// The height-morph key (components/dock-state-key.ts): the expanded
|
||||||
|
// term (the fold) belongs to EVERY docked variant — question AND
|
||||||
|
// practice (owner fix, 2026-10-06). Typing growth keeps the key and
|
||||||
|
// never animates; the fold/unfold morphs like teaching always has.
|
||||||
|
const stateKey = dockStateKey(mode, !!pending || !!practice, expanded);
|
||||||
const prevRef = useRef<{ key: string; height: number } | null>(null);
|
const prevRef = useRef<{ key: string; height: number } | null>(null);
|
||||||
useIsoLayoutEffect(() => {
|
useIsoLayoutEffect(() => {
|
||||||
const el = shellRef.current;
|
const el = shellRef.current;
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* The dock shell's variant key (the WAAPI height-morph trigger,
|
||||||
|
* components/chat.tsx DockOverlay). Whenever this string CHANGES between
|
||||||
|
* renders, the shell morphs its height (120ms) and re-reserves the
|
||||||
|
* transcript tail; when it stays the same, height changes are measured
|
||||||
|
* only (typing growth never animates).
|
||||||
|
*
|
||||||
|
* The expanded term (the fold: full form ↔ slim bar) belongs to EVERY
|
||||||
|
* docked variant — question AND practice. For two months it keyed on
|
||||||
|
* `pending` (the question's tool call) alone, which is null in practice
|
||||||
|
* sessions: the practice dock folded and re-expanded with an INSTANT
|
||||||
|
* height jump while teaching morphed (owner report 2026-10-06, "opens
|
||||||
|
* and closes instantly"). The fold machine itself already treats
|
||||||
|
* practice as a dock owner; this key now agrees.
|
||||||
|
*/
|
||||||
|
export function dockStateKey(mode: string, docked: boolean, expanded: boolean): string {
|
||||||
|
return docked ? `${mode}:${expanded}` : `${mode}:-`;
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* The dock shell's variant key — the WAAPI height-morph trigger. Pinned
|
||||||
|
* after the practice-fold fix (owner, 2026-10-06): the expanded term
|
||||||
|
* belongs to every docked variant, so a practice dock's fold/unfold
|
||||||
|
* morphs exactly like a teaching question's. Before the fix the key
|
||||||
|
* read `pending ? expanded : "-"` — pending is null in practice, the
|
||||||
|
* key never changed on the fold, and the practice dock's height jumped
|
||||||
|
* instantly between form and bar while teaching morphed.
|
||||||
|
*/
|
||||||
|
import assert from "node:assert/strict";
|
||||||
|
import { describe, it } from "node:test";
|
||||||
|
|
||||||
|
import { dockStateKey } from "../components/dock-state-key.ts";
|
||||||
|
|
||||||
|
describe("dockStateKey (the height-morph trigger)", () => {
|
||||||
|
it("a docked variant carries its fold state — practice included", () => {
|
||||||
|
assert.equal(dockStateKey("practice:call_p1", true, true), "practice:call_p1:true");
|
||||||
|
assert.equal(dockStateKey("practice:call_p1", true, false), "practice:call_p1:false");
|
||||||
|
assert.equal(dockStateKey("question:call_q1", true, true), "question:call_q1:true");
|
||||||
|
assert.equal(dockStateKey("question:call_q1", true, false), "question:call_q1:false");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("the fold toggling the key is what animates: same variant + same fold = same key (typing growth never morphs)", () => {
|
||||||
|
assert.equal(
|
||||||
|
dockStateKey("practice:call_p1", true, true),
|
||||||
|
dockStateKey("practice:call_p1", true, true),
|
||||||
|
);
|
||||||
|
assert.notEqual(
|
||||||
|
dockStateKey("practice:call_p1", true, true),
|
||||||
|
dockStateKey("practice:call_p1", true, false),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("an undocked band (the composer) has no fold term", () => {
|
||||||
|
assert.equal(dockStateKey("chat", false, true), "chat:-");
|
||||||
|
assert.equal(dockStateKey("chat", false, false), "chat:-");
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user