@ashley-shrok/viewmodel-shell 1.7.0 → 1.9.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/dist/browser.js +20 -0
- package/dist/index.d.ts +4 -0
- package/package.json +1 -1
- package/styles/default.css +10 -7
package/dist/browser.js
CHANGED
|
@@ -419,6 +419,26 @@ export class BrowserAdapter {
|
|
|
419
419
|
// single-field buttons[]-only form doesn't reload via native submit.
|
|
420
420
|
form.addEventListener("submit", (e) => e.preventDefault());
|
|
421
421
|
}
|
|
422
|
+
// Opt-in chat-composer affordance: bare Enter in a descendant textarea
|
|
423
|
+
// dispatches the submit (a textarea otherwise eats Enter as a newline and
|
|
424
|
+
// never submits). Modifier-Enter falls through to a normal newline, and an
|
|
425
|
+
// IME composition Enter (candidate confirmation) must NOT submit. No-op
|
|
426
|
+
// when submitAction is absent. Same dispatch path as the submit button.
|
|
427
|
+
if (n.submitOnEnter && n.submitAction) {
|
|
428
|
+
const submitAction = n.submitAction;
|
|
429
|
+
form.querySelectorAll("textarea").forEach(ta => {
|
|
430
|
+
ta.addEventListener("keydown", (e) => {
|
|
431
|
+
if (e.key !== "Enter")
|
|
432
|
+
return;
|
|
433
|
+
if (e.isComposing || e.keyCode === 229)
|
|
434
|
+
return; // IME candidate confirm — not a send
|
|
435
|
+
if (e.shiftKey || e.ctrlKey || e.metaKey || e.altKey)
|
|
436
|
+
return; // newline / shortcut
|
|
437
|
+
e.preventDefault();
|
|
438
|
+
dispatchWithFiles(submitAction);
|
|
439
|
+
});
|
|
440
|
+
});
|
|
441
|
+
}
|
|
422
442
|
if (n.buttons && n.buttons.length > 0) {
|
|
423
443
|
const row = document.createElement("div");
|
|
424
444
|
row.className = "vms-form__buttons";
|
package/dist/index.d.ts
CHANGED
|
@@ -189,6 +189,10 @@ export interface FormNode {
|
|
|
189
189
|
* button, same underlying state. A plain ButtonNode placed in `children`
|
|
190
190
|
* has identical dispatch semantics; the buttons[] slot is a layout hint. */
|
|
191
191
|
buttons?: ButtonNode[];
|
|
192
|
+
/** Opt-in: bare Enter inside a descendant <textarea> dispatches submitAction
|
|
193
|
+
* (chat-composer "Enter sends, Shift/Ctrl/Meta/Alt+Enter = newline"). No-op
|
|
194
|
+
* when submitAction is absent or during IME composition. Default false. */
|
|
195
|
+
submitOnEnter?: boolean;
|
|
192
196
|
children: ViewNode[];
|
|
193
197
|
}
|
|
194
198
|
export interface FieldNode {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ashley-shrok/viewmodel-shell",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "A server-driven UI framework where the wire format is structured enough that agents can build full-stack apps without ever opening a browser and all UI tests are pure unit tests with no browser runtime. Server returns a JSON tree of typed nodes; a thin TypeScript adapter renders it to DOM. Backend-agnostic — a .NET reference backend ships with the repo, but any language can produce the JSON contract.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/styles/default.css
CHANGED
|
@@ -323,7 +323,16 @@ body {
|
|
|
323
323
|
variable so themes can flip it. */
|
|
324
324
|
color-scheme: var(--vms-color-scheme);
|
|
325
325
|
}
|
|
326
|
-
|
|
326
|
+
/* Placeholder is derived from the theme text color at reduced strength
|
|
327
|
+
(the UA-default / Bootstrap mechanism, ~50%), NOT from --vms-text-muted.
|
|
328
|
+
Two reasons: (1) it reads as a faint hint rather than committed text, so an
|
|
329
|
+
empty field isn't mistaken for a filled one even with no visible label;
|
|
330
|
+
(2) it decouples from --vms-text-muted (shared by labels/captions/meta),
|
|
331
|
+
which must stay AA-contrast. Opacity (not a fixed gray) so it fades
|
|
332
|
+
correctly across every light/dark theme. This intentionally trades strict
|
|
333
|
+
placeholder AA contrast for clarity — placeholders should never be the sole
|
|
334
|
+
carrier of meaning; pair inputs with a label. */
|
|
335
|
+
.vms-field__input::placeholder { color: var(--vms-text); opacity: 0.5; }
|
|
327
336
|
.vms-field__input:focus {
|
|
328
337
|
border-color: var(--vms-accent);
|
|
329
338
|
box-shadow: 0 0 0 3px var(--vms-accent-glow);
|
|
@@ -508,11 +517,6 @@ select.vms-field__input { cursor: pointer; padding-right: var(--vms-space-xl);
|
|
|
508
517
|
background: var(--vms-accent-glow);
|
|
509
518
|
}
|
|
510
519
|
|
|
511
|
-
@keyframes vms-in {
|
|
512
|
-
from { opacity: 0; transform: translateY(4px); }
|
|
513
|
-
to { opacity: 1; transform: translateY(0); }
|
|
514
|
-
}
|
|
515
|
-
|
|
516
520
|
/* ── Text ── */
|
|
517
521
|
.vms-text { flex: 1; line-height: 1.4; word-break: break-word; }
|
|
518
522
|
.vms-text--heading { font-family: var(--vms-font-head); font-size: var(--vms-text-xl); }
|
|
@@ -560,7 +564,6 @@ select.vms-field__input { cursor: pointer; padding-right: var(--vms-space-xl);
|
|
|
560
564
|
justify-content: center;
|
|
561
565
|
padding: var(--vms-space-md);
|
|
562
566
|
z-index: 1000;
|
|
563
|
-
animation: vms-in 0.15s ease;
|
|
564
567
|
}
|
|
565
568
|
.vms-modal {
|
|
566
569
|
background: var(--vms-surface);
|