From 429445126ef5e2d3b435f87ebb276b833c511b0f Mon Sep 17 00:00:00 2001 From: digital-garbage <133010426+digital-garbage@users.noreply.github.com> Date: Tue, 6 Oct 2026 23:47:03 +0300 Subject: [PATCH] fix: variable values take Enter as a new line; the box is one line until clicked, then shows all of it Co-Authored-By: Claude Opus 5.5 --- app/ui/composer/elements/input.css | 2 ++ app/ui/composer/elements/input.js | 14 ++++++++++---- app/ui/features/composer/storytools.js | 2 +- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/app/ui/composer/elements/input.css b/app/ui/composer/elements/input.css index 7eadfe4d..8f01d543 100644 --- a/app/ui/composer/elements/input.css +++ b/app/ui/composer/elements/input.css @@ -31,6 +31,8 @@ resize: vertical; min-height: var(--ctl-h-xl); } .cx-autogrow { resize: none; overflow: hidden; } +.cx-fold { min-height: var(--ctl-h-md); height: var(--ctl-h-md); padding: var(--sp-2) var(--ctl-pad-x-sm); font-size: var(--fs-5); } +.cx-fold:not(:focus) { padding-block: 0; line-height: calc(var(--ctl-h-md) - 2px); } /* folded: the first line centred, the rest fully out of sight */ .cx-search { appearance: none; } .cx-search::-webkit-search-cancel-button { filter: grayscale(1) opacity(.6); } diff --git a/app/ui/composer/elements/input.js b/app/ui/composer/elements/input.js index f02716d6..6796f563 100644 --- a/app/ui/composer/elements/input.js +++ b/app/ui/composer/elements/input.js @@ -138,21 +138,27 @@ define("stepper", "md", ({ value = 0, min, max, step = 1, onChange, label } = {} return { node, get value() { return current; }, setValue: set, destroy: () => node.remove() }; }); -define("textarea", "md", ({ value = "", rows = 4, autoGrow = false, placeholder, onInput, onCommit, label } = {}) => { +// `fold`: one line like an input until clicked, then as tall as its text (Enter is a new line); one line again on leaving. +define("textarea", "md", ({ value = "", rows = 4, autoGrow = false, fold = false, placeholder, onInput, onCommit, label } = {}) => { const node = el("textarea", { - cls: ["cx-textarea", "cx-focusable", autoGrow ? "cx-autogrow" : null], - attrs: { rows, placeholder, "aria-label": label }, + cls: ["cx-textarea", "cx-focusable", autoGrow || fold ? "cx-autogrow" : null, fold ? "cx-fold" : null], + attrs: { rows: fold ? 1 : rows, placeholder, "aria-label": label }, }); node.value = value; const grow = () => { node.style.height = "auto"; node.style.height = `${node.scrollHeight}px`; }; if (autoGrow) { node.addEventListener("input", grow); queueMicrotask(grow); } + if (fold) { + node.addEventListener("focus", grow); + node.addEventListener("input", grow); + node.addEventListener("blur", () => { node.style.height = ""; node.scrollTop = 0; }); + } wire(node, { onInput, onCommit }); return { node, get value() { return node.value; }, - setValue(v) { node.value = v ?? ""; if (autoGrow) grow(); }, + setValue(v) { node.value = v ?? ""; if (autoGrow || (fold && document.activeElement === node)) grow(); }, destroy: () => node.remove(), }; }); diff --git a/app/ui/features/composer/storytools.js b/app/ui/features/composer/storytools.js index 9a45c41c..92a59993 100644 --- a/app/ui/features/composer/storytools.js +++ b/app/ui/features/composer/storytools.js @@ -71,7 +71,7 @@ export function variablesPanel(app, own) { const draw = () => { shown = JSON.stringify([p.project && p.project.id, rows()]); page.set([ ...rows().map((v, i) => c.toolbar.default({ items: [c.input.sm({ label: "Name", value: v.name, placeholder: "name", onCommit: (x) => { const l = clone(rows()); l[i].name = x; write(l); } }), - c.input.sm({ label: "Value", value: v.value, placeholder: "text", onCommit: (x) => { const l = clone(rows()); l[i].value = x; write(l); } })], + c.textarea.md({ fold: true, label: "Value", value: v.value, placeholder: "text", onCommit: (x) => { const l = clone(rows()); l[i].value = x; write(l); } })], trailing: [c.button.sm({ label: "✕", tone: "ghost", title: "Remove", onClick: () => { write(rows().filter((_, k) => k !== i)); draw(); } })] })), c.button.sm({ label: "+ Add variable", tone: "ghost", disabled: !p.project, onClick: () => { write([...rows(), { name: "name", value: "" }]); draw(); } }), c.hint.default({ text: "Write $name in a prompt; it becomes the variable's text when you generate." }),