@ashley-shrok/viewmodel-shell 1.8.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 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.8.0",
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",
@@ -517,11 +517,6 @@ select.vms-field__input { cursor: pointer; padding-right: var(--vms-space-xl);
517
517
  background: var(--vms-accent-glow);
518
518
  }
519
519
 
520
- @keyframes vms-in {
521
- from { opacity: 0; transform: translateY(4px); }
522
- to { opacity: 1; transform: translateY(0); }
523
- }
524
-
525
520
  /* ── Text ── */
526
521
  .vms-text { flex: 1; line-height: 1.4; word-break: break-word; }
527
522
  .vms-text--heading { font-family: var(--vms-font-head); font-size: var(--vms-text-xl); }
@@ -569,7 +564,6 @@ select.vms-field__input { cursor: pointer; padding-right: var(--vms-space-xl);
569
564
  justify-content: center;
570
565
  padding: var(--vms-space-md);
571
566
  z-index: 1000;
572
- animation: vms-in 0.15s ease;
573
567
  }
574
568
  .vms-modal {
575
569
  background: var(--vms-surface);