Mercurial
annotate mrjunejune/src/public/mjj-composer.js @ 272:41a49c29a28f
polish JRPG conversation experience
Integrate desktop conversations into the utility panel, simplify the mobile frame, add modal destinations and a reusable Zenbu composer lab, and preserve explicit conversation resume behavior.
Co-authored-by: Copilot <[email protected]>
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Fri, 07 Aug 2026 16:05:29 -0700 |
| parents | |
| children | e02e2036ef84 |
| rev | line source |
|---|---|
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
1 /** |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2 * Coordinates a native composer form built from Zenbu controls. |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
3 * |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
4 * @extends {HTMLElement} |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
5 */ |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
6 export class MjjComposer extends HTMLElement { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
7 connectedCallback() { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
8 this._form = this.querySelector(":scope > form"); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
9 this._textarea = this._form?.querySelector("textarea"); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
10 this._submit = this._form?.querySelector('button[type="submit"]'); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
11 this._cancel = this._form?.querySelector("[data-composer-cancel]"); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
12 this._cancelOwner = this._form?.querySelector("[data-composer-cancel-owner]"); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
13 this._onSubmit = event => { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
14 event.preventDefault(); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
15 const message = this._textarea?.value.trim(); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
16 if (!message || this._submit?.disabled) return; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
17 this.dispatchEvent(new CustomEvent("mjj-composer-submit", { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
18 bubbles: true, |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
19 detail: { message }, |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
20 })); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
21 this._form.reset(); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
22 }; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
23 this._onKeydown = event => { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
24 if (event.key !== "Enter" || event.shiftKey || event.isComposing) return; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
25 event.preventDefault(); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
26 this._form?.requestSubmit(); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
27 }; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
28 this._onCancel = () => { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
29 this.dispatchEvent(new CustomEvent("mjj-composer-cancel", { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
30 bubbles: true, |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
31 })); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
32 }; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
33 this._form?.addEventListener("submit", this._onSubmit); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
34 this._textarea?.addEventListener("keydown", this._onKeydown); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
35 this._cancel?.addEventListener("click", this._onCancel); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
36 } |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
37 |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
38 disconnectedCallback() { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
39 this._form?.removeEventListener("submit", this._onSubmit); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
40 this._textarea?.removeEventListener("keydown", this._onKeydown); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
41 this._cancel?.removeEventListener("click", this._onCancel); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
42 } |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
43 |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
44 /** |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
45 * Sets the active request state. |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
46 * |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
47 * @param {boolean} busy |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
48 */ |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
49 setBusy(busy) { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
50 if (this._textarea) this._textarea.disabled = busy; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
51 if (this._submit) { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
52 this._submit.disabled = busy; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
53 this._submit.setAttribute("aria-busy", String(busy)); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
54 } |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
55 if (this._cancelOwner) this._cancelOwner.hidden = !busy; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
56 } |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
57 |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
58 /** |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
59 * Shows or hides the cancellation action. |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
60 * |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
61 * @param {boolean} cancellable |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
62 */ |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
63 setCancellable(cancellable) { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
64 if (this._cancelOwner) this._cancelOwner.hidden = !cancellable; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
65 } |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
66 |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
67 /** |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
68 * Enables or disables composer input. |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
69 * |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
70 * @param {boolean} disabled |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
71 */ |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
72 setDisabled(disabled) { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
73 if (this._textarea) this._textarea.disabled = disabled; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
74 if (this._submit) this._submit.disabled = disabled; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
75 } |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
76 } |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
77 |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
78 if (!customElements.get("mjj-composer")) { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
79 customElements.define("mjj-composer", MjjComposer); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
80 } |