polish: refine structured composer
This commit is contained in:
@@ -583,9 +583,11 @@ describe("structured connection: turn/composer gating", () => {
|
||||
ws.emit({ type: "reduced_state", state: { turn: { responding: true, thinking: false, active_tool_calls: [] } } });
|
||||
expect(input.disabled).toBe(true);
|
||||
expect(cancel.hidden).toBe(false);
|
||||
expect((conn.el.querySelector(".structured-send") as HTMLButtonElement).hidden).toBe(true);
|
||||
ws.emit({ type: "reduced_state", state: idleState });
|
||||
expect(input.disabled).toBe(false);
|
||||
expect(cancel.hidden).toBe(true);
|
||||
expect((conn.el.querySelector(".structured-send") as HTMLButtonElement).hidden).toBe(false);
|
||||
conn.dispose();
|
||||
});
|
||||
|
||||
|
||||
@@ -104,7 +104,9 @@ export function createStructuredConnection(
|
||||
</button>
|
||||
<div class="structured-config-menu" role="group" aria-label="Session settings" hidden></div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary structured-send">Send</button>
|
||||
<button type="submit" class="btn btn-primary structured-send"><span>Send</span>
|
||||
<svg viewBox="0 0 20 20" aria-hidden="true"><path d="m7 5 5 5-5 5M12 10H3"/></svg>
|
||||
</button>
|
||||
<button type="button" class="btn structured-cancel" hidden>Cancel turn</button>
|
||||
</form>
|
||||
</div>
|
||||
@@ -478,9 +480,11 @@ export function createStructuredConnection(
|
||||
|
||||
function renderComposerState(): void {
|
||||
const active = state.turn.responding || state.turn.thinking || awaitingBeginGeneration !== null;
|
||||
const projectedActive = state.turn.responding || state.turn.thinking;
|
||||
input.disabled = active;
|
||||
sendBtn.disabled = active;
|
||||
cancelBtn.hidden = !state.turn.responding && !state.turn.thinking;
|
||||
sendBtn.hidden = projectedActive;
|
||||
cancelBtn.hidden = !projectedActive;
|
||||
if (active) closeCommandMenu();
|
||||
renderActivity();
|
||||
}
|
||||
@@ -696,7 +700,9 @@ export function createStructuredConnection(
|
||||
function autoResize(): void {
|
||||
updateTranscriptLayout(() => {
|
||||
input.style.height = "auto";
|
||||
input.style.overflowY = "hidden";
|
||||
input.style.height = `${Math.min(input.scrollHeight, 160)}px`;
|
||||
if (input.scrollHeight > 160) input.style.overflowY = "auto";
|
||||
});
|
||||
}
|
||||
input.addEventListener("input", () => {
|
||||
|
||||
+90
-17
@@ -2175,19 +2175,19 @@ input.leaf-rename:focus {
|
||||
|
||||
.structured-config {
|
||||
position: relative;
|
||||
flex: 0 0 var(--control-h);
|
||||
flex: 0 0 40px;
|
||||
}
|
||||
|
||||
.structured-config-trigger {
|
||||
width: var(--control-h);
|
||||
height: var(--control-h);
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0;
|
||||
border: 1px solid var(--border);
|
||||
border: 0;
|
||||
border-radius: var(--r-2);
|
||||
background: var(--surface-2);
|
||||
background: transparent;
|
||||
color: var(--muted);
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -2204,7 +2204,6 @@ input.leaf-rename:focus {
|
||||
.structured-config-trigger:hover,
|
||||
.structured-config-trigger[aria-expanded="true"] {
|
||||
color: var(--ink);
|
||||
border-color: var(--border-strong);
|
||||
background: var(--surface-3);
|
||||
}
|
||||
|
||||
@@ -2829,16 +2828,28 @@ input.leaf-rename:focus {
|
||||
stays reachable at the pane edge without changing transcript scroll math. */
|
||||
.structured-composer {
|
||||
flex: 0 0 auto;
|
||||
padding: var(--sp-3);
|
||||
border-top: 1px solid var(--border);
|
||||
margin: var(--sp-2) var(--sp-3) max(var(--sp-2), env(safe-area-inset-bottom));
|
||||
padding: var(--sp-1);
|
||||
border: 1px solid var(--border-strong);
|
||||
border-radius: var(--r-3);
|
||||
background: var(--surface);
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: var(--sp-2);
|
||||
gap: var(--sp-1);
|
||||
box-shadow:
|
||||
inset 0 1px 0 color-mix(in oklab, var(--ink) 6%, transparent),
|
||||
var(--shadow-1);
|
||||
transition:
|
||||
border-color var(--dur-fast) var(--ease-out),
|
||||
box-shadow var(--dur-fast) var(--ease-out);
|
||||
}
|
||||
|
||||
.structured-composer {
|
||||
padding-bottom: max(var(--sp-3), env(safe-area-inset-bottom));
|
||||
.structured-composer:focus-within {
|
||||
border-color: var(--accent);
|
||||
box-shadow:
|
||||
inset 0 1px 0 color-mix(in oklab, var(--ink) 8%, transparent),
|
||||
0 0 0 3px var(--accent-tint),
|
||||
var(--shadow-1);
|
||||
}
|
||||
|
||||
.structured-input-wrap {
|
||||
@@ -2852,13 +2863,72 @@ input.leaf-rename:focus {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
max-height: 160px;
|
||||
min-height: 40px;
|
||||
resize: none;
|
||||
padding: var(--sp-2) var(--sp-3);
|
||||
border: 1px solid var(--border-strong);
|
||||
overflow-y: hidden;
|
||||
padding: 10px var(--sp-2);
|
||||
border: 0;
|
||||
border-radius: var(--r-2);
|
||||
background: var(--bg);
|
||||
background: transparent;
|
||||
color: var(--ink);
|
||||
font-size: var(--fs-13);
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.structured-composer .structured-send,
|
||||
.structured-composer .structured-cancel {
|
||||
height: 40px;
|
||||
min-height: 40px;
|
||||
border-radius: var(--r-2);
|
||||
font-family: var(--font-sans);
|
||||
}
|
||||
|
||||
.structured-composer .structured-send {
|
||||
gap: 6px;
|
||||
padding: 0 var(--sp-3);
|
||||
border-color: var(--accent);
|
||||
background: var(--accent);
|
||||
color: var(--accent-ink);
|
||||
font-weight: 650;
|
||||
box-shadow: inset 0 1px 0 color-mix(in oklab, white 24%, transparent);
|
||||
}
|
||||
|
||||
.structured-composer .structured-send::before,
|
||||
.structured-composer .structured-send::after,
|
||||
.structured-composer .structured-cancel::before,
|
||||
.structured-composer .structured-cancel::after {
|
||||
content: none;
|
||||
}
|
||||
|
||||
.structured-composer .structured-send svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
fill: none;
|
||||
stroke: currentColor;
|
||||
stroke-width: 1.7;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
transition: transform var(--dur-fast) var(--ease-out);
|
||||
}
|
||||
|
||||
.structured-composer .structured-send:hover {
|
||||
background: var(--accent-strong);
|
||||
border-color: var(--accent-strong);
|
||||
color: var(--accent-ink);
|
||||
}
|
||||
|
||||
.structured-composer .structured-send:hover svg {
|
||||
transform: translateX(2px);
|
||||
}
|
||||
|
||||
.structured-composer .structured-cancel {
|
||||
padding: 0 var(--sp-3);
|
||||
border-color: color-mix(in oklab, var(--danger) 35%, var(--border));
|
||||
color: var(--danger);
|
||||
}
|
||||
|
||||
.structured-composer .structured-cancel:hover {
|
||||
background: var(--danger-tint);
|
||||
}
|
||||
|
||||
@media (max-width: 520px) {
|
||||
@@ -2933,13 +3003,12 @@ input.leaf-rename:focus {
|
||||
|
||||
.structured-input:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent);
|
||||
box-shadow: 0 0 0 3px var(--accent-tint);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.structured-input:disabled {
|
||||
color: var(--faint);
|
||||
background: var(--surface-2);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* `.btn` sets `display: inline-flex`, which outranks the UA sheet's
|
||||
@@ -2948,6 +3017,10 @@ input.leaf-rename:focus {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.structured-send[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.structured-error {
|
||||
margin: 0 var(--sp-3);
|
||||
padding: var(--sp-2) var(--sp-3);
|
||||
|
||||
Reference in New Issue
Block a user