@memberjunction/ng-composer 6.2.0-edge.4 → 6.2.0-edge.5
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.
|
@@ -11,7 +11,7 @@ function MessageInputBoxComponent_Conditional_5_Conditional_4_Template(rf, ctx)
|
|
|
11
11
|
} if (rf & 2) {
|
|
12
12
|
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
13
13
|
i0.ɵɵadvance();
|
|
14
|
-
i0.ɵɵ
|
|
14
|
+
i0.ɵɵtextInterpolate(ctx_r1.StatusElapsed);
|
|
15
15
|
} }
|
|
16
16
|
function MessageInputBoxComponent_Conditional_5_Template(rf, ctx) { if (rf & 1) {
|
|
17
17
|
i0.ɵɵelementStart(0, "div", 10);
|
|
@@ -489,11 +489,11 @@ export class MessageInputBoxComponent {
|
|
|
489
489
|
i0.ɵɵattribute("aria-label", ctx.SendSlotLabel)("aria-busy", ctx.SendSlotMode === "stopping" ? true : null);
|
|
490
490
|
i0.ɵɵadvance();
|
|
491
491
|
i0.ɵɵconditional((tmp_22_0 = ctx.SendSlotMode) === "stop" ? 8 : tmp_22_0 === "stopping" ? 9 : 10);
|
|
492
|
-
} }, dependencies: [i1.NgControlStatus, i1.NgModel, i2.MentionEditorComponent], styles: [".message-input-box-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface-card);\n border: 2px solid var(--mj-border-default);\n border-radius: 12px;\n box-shadow: var(--mj-shadow-sm);\n transition: all 0.2s ease;\n overflow: visible;\n position: relative;\n cursor: text;\n}\n.message-input-box-container[_ngcontent-%COMP%]:focus-within {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 4px 16px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n.input-wrapper[_ngcontent-%COMP%] {\n position: relative;\n display: flex;\n flex-direction: column;\n padding: 5px;\n}\n.input-wrapper[_ngcontent-%COMP%] mj-mention-editor[_ngcontent-%COMP%] {\n display: block;\n}\n\n\n\n\n.input-wrapper[_ngcontent-%COMP%] .mention-editor {\n min-height: calc(100px - 43px);\n max-height: calc(300px - 43px);\n padding-right: 12px;\n}\n.composer-actions[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 2px;\n padding: 0 7px 7px;\n}\n\n.message-input-box-textarea[_ngcontent-%COMP%] {\n flex: 1;\n min-height: 100px;\n padding: 1rem;\n padding-right: 3.5rem;\n border: 0 !important;\n outline: 0 !important;\n box-shadow: none !important;\n -webkit-appearance: none !important;\n -moz-appearance: none !important;\n appearance: none !important;\n font-family: inherit;\n font-size: 1rem;\n line-height: 1.5;\n resize: vertical;\n background: transparent;\n color: var(--mj-text-primary);\n border: 0 !important;\n}\n.message-input-box-textarea[_ngcontent-%COMP%]::placeholder {\n color: var(--mj-text-muted);\n}\n.message-input-box-textarea[_ngcontent-%COMP%]:disabled {\n cursor: not-allowed;\n opacity: 0.6;\n background: var(--mj-bg-surface-sunken);\n}\n.message-input-box-textarea[_ngcontent-%COMP%]:focus, .message-input-box-textarea[_ngcontent-%COMP%]:active, .message-input-box-textarea[_ngcontent-%COMP%]:focus-visible {\n outline: 0 !important;\n box-shadow: none !important;\n}\n\n.send-button-icon[_ngcontent-%COMP%] {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border: none;\n border-radius: 8px;\n cursor: pointer;\n transition: all 0.2s ease;\n flex-shrink: 0;\n}\n.send-button-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 1rem;\n}\n.send-button-icon[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n transform: scale(1.05);\n}\n.send-button-icon[_ngcontent-%COMP%]:active:not(:disabled) {\n transform: scale(0.95);\n}\n.send-button-icon[_ngcontent-%COMP%]:disabled {\n background: var(--mj-border-default);\n color: var(--mj-text-muted);\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n\n.send-button-icon--stop[_ngcontent-%COMP%] {\n background: var(--mj-status-error);\n color: var(--mj-brand-on-primary);\n}\n.send-button-icon--stop[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: color-mix(in srgb, var(--mj-status-error) 85%, var(--mj-text-primary));\n}\n\n.send-button-icon--stop-all[_ngcontent-%COMP%] {\n width: auto;\n padding: 0 12px;\n gap: 8px;\n font-size: var(--mj-text-xs);\n font-weight: 600;\n white-space: nowrap;\n}\n.send-slot-count[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-on-primary) 22%, transparent);\n border-radius: 999px;\n padding: 0 7px;\n font-size: var(--mj-text-xs);\n}\n\n.send-button-icon--stopping[_ngcontent-%COMP%]:disabled {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-secondary);\n cursor: default;\n opacity: 1;\n}\n\n\n.send-slot-status[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n margin-right: auto;\n padding-left: 5px;\n font-size: var(--mj-text-xs);\n color: var(--mj-text-secondary);\n}\n.send-slot-status--stopping[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n}\n.send-slot-status-text[_ngcontent-%COMP%]
|
|
492
|
+
} }, dependencies: [i1.NgControlStatus, i1.NgModel, i2.MentionEditorComponent], styles: [".message-input-box-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface-card);\n border: 2px solid var(--mj-border-default);\n border-radius: 12px;\n box-shadow: var(--mj-shadow-sm);\n transition: all 0.2s ease;\n overflow: visible;\n position: relative;\n cursor: text;\n}\n.message-input-box-container[_ngcontent-%COMP%]:focus-within {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 4px 16px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n.input-wrapper[_ngcontent-%COMP%] {\n position: relative;\n display: flex;\n flex-direction: column;\n padding: 5px;\n}\n.input-wrapper[_ngcontent-%COMP%] mj-mention-editor[_ngcontent-%COMP%] {\n display: block;\n}\n\n\n\n\n.input-wrapper[_ngcontent-%COMP%] .mention-editor {\n min-height: calc(100px - 43px);\n max-height: calc(300px - 43px);\n padding-right: 12px;\n}\n.composer-actions[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 2px;\n padding: 0 7px 7px;\n}\n\n.message-input-box-textarea[_ngcontent-%COMP%] {\n flex: 1;\n min-height: 100px;\n padding: 1rem;\n padding-right: 3.5rem;\n border: 0 !important;\n outline: 0 !important;\n box-shadow: none !important;\n -webkit-appearance: none !important;\n -moz-appearance: none !important;\n appearance: none !important;\n font-family: inherit;\n font-size: 1rem;\n line-height: 1.5;\n resize: vertical;\n background: transparent;\n color: var(--mj-text-primary);\n border: 0 !important;\n}\n.message-input-box-textarea[_ngcontent-%COMP%]::placeholder {\n color: var(--mj-text-muted);\n}\n.message-input-box-textarea[_ngcontent-%COMP%]:disabled {\n cursor: not-allowed;\n opacity: 0.6;\n background: var(--mj-bg-surface-sunken);\n}\n.message-input-box-textarea[_ngcontent-%COMP%]:focus, .message-input-box-textarea[_ngcontent-%COMP%]:active, .message-input-box-textarea[_ngcontent-%COMP%]:focus-visible {\n outline: 0 !important;\n box-shadow: none !important;\n}\n\n.send-button-icon[_ngcontent-%COMP%] {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border: none;\n border-radius: 8px;\n cursor: pointer;\n transition: all 0.2s ease;\n flex-shrink: 0;\n}\n.send-button-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 1rem;\n}\n.send-button-icon[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n transform: scale(1.05);\n}\n.send-button-icon[_ngcontent-%COMP%]:active:not(:disabled) {\n transform: scale(0.95);\n}\n.send-button-icon[_ngcontent-%COMP%]:disabled {\n background: var(--mj-border-default);\n color: var(--mj-text-muted);\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n\n.send-button-icon--stop[_ngcontent-%COMP%] {\n background: var(--mj-status-error);\n color: var(--mj-brand-on-primary);\n}\n.send-button-icon--stop[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: color-mix(in srgb, var(--mj-status-error) 85%, var(--mj-text-primary));\n}\n\n.send-button-icon--stop-all[_ngcontent-%COMP%] {\n width: auto;\n padding: 0 12px;\n gap: 8px;\n font-size: var(--mj-text-xs);\n font-weight: 600;\n white-space: nowrap;\n}\n.send-slot-count[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-on-primary) 22%, transparent);\n border-radius: 999px;\n padding: 0 7px;\n font-size: var(--mj-text-xs);\n}\n\n.send-button-icon--stopping[_ngcontent-%COMP%]:disabled {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-secondary);\n cursor: default;\n opacity: 1;\n}\n\n\n.send-slot-status[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n margin-right: auto;\n padding-left: 5px;\n font-size: var(--mj-text-xs);\n color: var(--mj-text-secondary);\n}\n.send-slot-status--stopping[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n}\n.send-slot-status-text[_ngcontent-%COMP%] {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n\n.send-slot-status-elapsed[_ngcontent-%COMP%] {\n flex-shrink: 0;\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n line-height: 1.4;\n white-space: nowrap;\n font-variant-numeric: tabular-nums;\n background: var(--mj-status-info-bg);\n color: var(--mj-status-info-text);\n}\n\n\n.attach-buttons[_ngcontent-%COMP%] {\n display: flex;\n gap: 2px;\n}\n.attach-button-icon[_ngcontent-%COMP%] {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n color: var(--mj-text-secondary);\n border: none;\n border-radius: 8px;\n cursor: pointer;\n transition: all 0.2s ease;\n flex-shrink: 0;\n}\n.attach-button-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 1rem;\n}\n.attach-button-icon[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: color-mix(in srgb, var(--mj-text-primary) 5%, transparent);\n color: var(--mj-brand-primary);\n transform: scale(1.05);\n}\n.attach-button-icon[_ngcontent-%COMP%]:active:not(:disabled) {\n transform: scale(0.95);\n}\n.attach-button-icon[_ngcontent-%COMP%]:disabled {\n color: var(--mj-text-muted);\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n\n\n.plan-mode-button-icon--active[_ngcontent-%COMP%]:not(:disabled) {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n.plan-mode-button-icon--active[_ngcontent-%COMP%]:not(:disabled):hover {\n background: var(--mj-brand-primary-hover, var(--mj-brand-primary));\n color: var(--mj-text-inverse);\n}\n\n\n\n.skills-button-icon--active[_ngcontent-%COMP%]:not(:disabled) {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n.skills-button-icon--active[_ngcontent-%COMP%]:not(:disabled):hover {\n background: var(--mj-brand-primary-hover, var(--mj-brand-primary));\n color: var(--mj-text-inverse);\n}\n\n\n@media (max-width: 768px) {\n .message-input-box-container[_ngcontent-%COMP%] {\n border-radius: 10px;\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-text-primary) 6%, transparent);\n }\n .message-input-box-textarea[_ngcontent-%COMP%] {\n min-height: 80px;\n padding: 0.75rem;\n padding-right: 3rem;\n font-size: 16px;\n line-height: 1.4;\n }\n \n .input-wrapper[_ngcontent-%COMP%] .mention-editor {\n min-height: calc(100px - 37px);\n max-height: calc(300px - 37px);\n padding-right: 5px;\n }\n .composer-actions[_ngcontent-%COMP%] {\n padding: 0 5px 5px;\n }\n \n .send-slot-status-elapsed[_ngcontent-%COMP%] {\n display: none;\n }\n .send-button-icon[_ngcontent-%COMP%] {\n width: 32px;\n height: 32px;\n border-radius: 6px;\n }\n .send-button-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 0.875rem;\n }\n .attach-button-icon[_ngcontent-%COMP%] {\n width: 32px;\n height: 32px;\n }\n .attach-button-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 0.875rem;\n }\n}\n\n@media (max-width: 480px) {\n .message-input-box-container[_ngcontent-%COMP%] {\n border-radius: 8px;\n border-width: 1.5px;\n }\n .message-input-box-textarea[_ngcontent-%COMP%] {\n min-height: 60px;\n padding: 0.625rem;\n padding-right: 2.75rem;\n font-size: 16px;\n line-height: 1.3;\n }\n \n .input-wrapper[_ngcontent-%COMP%] .mention-editor {\n min-height: calc(100px - 39px);\n max-height: calc(300px - 39px);\n }\n .composer-actions[_ngcontent-%COMP%] {\n padding: 0 3px 3px;\n }\n .send-button-icon[_ngcontent-%COMP%] {\n width: 36px;\n height: 36px;\n border-radius: 6px;\n }\n .send-button-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 0.875rem;\n }\n .attach-button-icon[_ngcontent-%COMP%] {\n width: 32px;\n height: 32px;\n }\n .attach-button-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 0.875rem;\n }\n}\n\n\n\n\n\n.voice-button-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n.voice-button-icon[_ngcontent-%COMP%]:hover:not(:disabled) i[_ngcontent-%COMP%] {\n filter: drop-shadow(0 0 4px color-mix(in srgb, var(--mj-brand-primary) 55%, transparent));\n}\n.voice-button-icon[_ngcontent-%COMP%]:disabled i[_ngcontent-%COMP%] {\n color: var(--mj-text-disabled);\n}\n.voice-button-icon--active[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, transparent);\n}\n.voice-button-icon--active[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: color-mix(in srgb, var(--mj-brand-primary) 18%, transparent);\n}\n\n\n\n\n.voice-options-button-icon[_ngcontent-%COMP%] {\n width: 20px;\n margin-left: -4px;\n}\n.voice-options-button-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n}\n.voice-options-button-icon[_ngcontent-%COMP%]:hover:not(:disabled) i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n.voice-options-button-icon[_ngcontent-%COMP%]:disabled i[_ngcontent-%COMP%] {\n color: var(--mj-text-disabled);\n}"] });
|
|
493
493
|
}
|
|
494
494
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MessageInputBoxComponent, [{
|
|
495
495
|
type: Component,
|
|
496
|
-
args: [{ standalone: false, selector: 'mj-message-input-box', template: "<div class=\"message-input-box-container\" (click)=\"OnContainerClick($event)\">\n <div class=\"input-wrapper\">\n <mj-mention-editor\n #mentionEditor\n [placeholder]=\"Placeholder\"\n [disabled]=\"Disabled\"\n [currentUser]=\"CurrentUser\"\n [enableMentions]=\"EnableMentions\"\n [TriggerProviders]=\"TriggerProviders\"\n [ExcludedTriggerKeys]=\"ExcludedTriggerKeys\"\n [Provider]=\"Provider\"\n [enableAttachments]=\"EnableAttachments\"\n [maxAttachments]=\"MaxAttachments\"\n [maxAttachmentSizeBytes]=\"MaxAttachmentSizeBytes\"\n [acceptedFileTypes]=\"AcceptedFileTypes\"\n [(ngModel)]=\"Value\"\n (valueChange)=\"OnValueChange($event)\"\n (enterPressed)=\"OnEnterPressed($event)\"\n (mentionSelected)=\"OnMentionSelected($event)\"\n (attachmentsChanged)=\"OnAttachmentsChanged($event)\"\n (attachmentError)=\"OnAttachmentError($event)\"\n (attachmentClicked)=\"OnAttachmentClicked($event)\"\n (editorBlurred)=\"Blurred.emit()\">\n </mj-mention-editor>\n\n <!-- Action row: its own strip below the editor (not floating over it), so text never runs behind a button -->\n <div class=\"composer-actions\">\n <!-- Status line, left of the action row: what the host is waiting on (\"Sage is working\"). A polite\n live region; the elapsed time sits in an aria-hidden child so its ticking is never announced. -->\n @if (StatusText) {\n <div class=\"send-slot-status\" [class.send-slot-status--stopping]=\"SendSlotMode === 'stopping'\" role=\"status\" aria-live=\"polite\">\n <i class=\"fa-solid fa-circle-notch fa-spin\" aria-hidden=\"true\"></i>\n <span class=\"send-slot-status-text\">{{ StatusText }}</span>\n @if (StatusElapsed) {\n <span class=\"send-slot-status-elapsed\" aria-hidden=\"true\">, {{ StatusElapsed }}</span>\n }\n </div>\n }\n\n @if (EnableAttachments || EnableRealtime || EnablePlanMode || EnableSkills) {\n <div class=\"attach-buttons\">\n @if (EnableSkills) {\n <button\n class=\"attach-button-icon skills-button-icon\"\n [class.skills-button-icon--active]=\"SkillsActive\"\n [disabled]=\"Disabled\"\n [attr.aria-expanded]=\"SkillsActive\"\n aria-haspopup=\"true\"\n (mousedown)=\"$event.preventDefault(); $event.stopPropagation()\"\n (click)=\"OnSkillsClick($event); $event.stopPropagation()\"\n title=\"Skills, or type / in the message box\"\n aria-label=\"Show skills\"\n >\n <i class=\"fa-solid fa-wand-magic-sparkles\" aria-hidden=\"true\"></i>\n </button>\n }\n @if (EnablePlanMode) {\n <button\n class=\"attach-button-icon plan-mode-button-icon\"\n [class.plan-mode-button-icon--active]=\"PlanModeActive\"\n [disabled]=\"Disabled\"\n [attr.aria-pressed]=\"PlanModeActive\"\n (click)=\"PlanModeToggle.emit(); $event.stopPropagation()\"\n [title]=\"PlanModeActive ? 'Plan Mode ON \u2014 the agent presents a plan for your approval before it acts' : 'Plan Mode: have the agent present a plan for your approval before it acts'\"\n aria-label=\"Toggle Plan Mode\"\n >\n <i class=\"fa-solid fa-route\" aria-hidden=\"true\"></i>\n </button>\n }\n @if (EnableAttachments) {\n <button\n class=\"attach-button-icon\"\n [disabled]=\"Disabled\"\n (click)=\"OpenFilePicker(); $event.stopPropagation()\"\n title=\"Attach file\"\n >\n <i class=\"fa-solid fa-paperclip\"></i>\n </button>\n }\n @if (EnableRealtime) {\n <button\n class=\"attach-button-icon voice-button-icon\"\n [class.voice-button-icon--active]=\"VoiceActive\"\n [disabled]=\"!CanStartRealtime\"\n (click)=\"OnRealtimeClick(); $event.stopPropagation()\"\n [title]=\"VoiceActive ? 'Voice call active' : 'Start a voice call'\"\n aria-label=\"Start a voice call\"\n >\n <i class=\"fa-solid fa-phone\" aria-hidden=\"true\"></i>\n </button>\n <!-- Caret beside the phone: opens the agent/model picker so call OPTIONS stay one\n click away while the plain phone click keeps its instant-start behavior. -->\n <button\n class=\"attach-button-icon voice-options-button-icon\"\n [disabled]=\"!CanStartRealtime\"\n (click)=\"OnRealtimeOptionsClick(); $event.stopPropagation()\"\n title=\"Voice call options (choose agent and voice model)\"\n aria-label=\"Voice call options\"\n >\n <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i>\n </button>\n }\n </div>\n }\n\n <!-- The send slot. ONE button in every mode (send / stop / stopping) so focus is kept across the\n change; see SendSlotMode. -->\n <button\n class=\"send-button-icon\"\n [class.send-button-icon--stop]=\"SendSlotMode === 'stop'\"\n [class.send-button-icon--stop-all]=\"IsStopAll\"\n [class.send-button-icon--stopping]=\"SendSlotMode === 'stopping'\"\n [disabled]=\"SendSlotDisabled\"\n (click)=\"OnSendSlotClick()\"\n [title]=\"SendSlotLabel\"\n [attr.aria-label]=\"SendSlotLabel\"\n [attr.aria-busy]=\"SendSlotMode === 'stopping' ? true : null\"\n >\n @switch (SendSlotMode) {\n @case ('stop') {\n <i class=\"fa-solid fa-stop\" aria-hidden=\"true\"></i>\n @if (IsStopAll) {\n <span class=\"send-slot-label\">Stop all</span>\n <span class=\"send-slot-count\">{{ StopCount }}</span>\n }\n }\n @case ('stopping') {\n <i class=\"fa-solid fa-circle-notch fa-spin\" aria-hidden=\"true\"></i>\n }\n @default {\n <i class=\"fa-solid fa-paper-plane\"></i>\n }\n }\n </button>\n </div>\n </div>\n</div>\n", styles: [".message-input-box-container {\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface-card);\n border: 2px solid var(--mj-border-default);\n border-radius: 12px;\n box-shadow: var(--mj-shadow-sm);\n transition: all 0.2s ease;\n overflow: visible;\n position: relative;\n cursor: text;\n}\n.message-input-box-container:focus-within {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 4px 16px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n.input-wrapper {\n position: relative;\n display: flex;\n flex-direction: column;\n padding: 5px;\n}\n.input-wrapper mj-mention-editor {\n display: block;\n}\n/* The action buttons get their own row below the editor instead of floating over its bottom-right corner, so\n text can never run behind them (at any scroll position). The editor gives up the row's height (36px buttons +\n 7px bottom padding = 43px) so the composer's resting and maximum sizes are unchanged, and it no longer needs the\n right padding that used to keep the first lines clear of the send button. */\n.input-wrapper ::ng-deep .mention-editor {\n min-height: calc(100px - 43px);\n max-height: calc(300px - 43px);\n padding-right: 12px;\n}\n.composer-actions {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 2px;\n padding: 0 7px 7px;\n}\n\n.message-input-box-textarea {\n flex: 1;\n min-height: 100px;\n padding: 1rem;\n padding-right: 3.5rem;\n border: 0 !important;\n outline: 0 !important;\n box-shadow: none !important;\n -webkit-appearance: none !important;\n -moz-appearance: none !important;\n appearance: none !important;\n font-family: inherit;\n font-size: 1rem;\n line-height: 1.5;\n resize: vertical;\n background: transparent;\n color: var(--mj-text-primary);\n border: 0 !important;\n}\n.message-input-box-textarea::placeholder {\n color: var(--mj-text-muted);\n}\n.message-input-box-textarea:disabled {\n cursor: not-allowed;\n opacity: 0.6;\n background: var(--mj-bg-surface-sunken);\n}\n.message-input-box-textarea:focus, .message-input-box-textarea:active, .message-input-box-textarea:focus-visible {\n outline: 0 !important;\n box-shadow: none !important;\n}\n\n.send-button-icon {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border: none;\n border-radius: 8px;\n cursor: pointer;\n transition: all 0.2s ease;\n flex-shrink: 0;\n}\n.send-button-icon i {\n font-size: 1rem;\n}\n.send-button-icon:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n transform: scale(1.05);\n}\n.send-button-icon:active:not(:disabled) {\n transform: scale(0.95);\n}\n.send-button-icon:disabled {\n background: var(--mj-border-default);\n color: var(--mj-text-muted);\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n/* The send slot as a stop control. Same element, same place; red says what a click does now. */\n.send-button-icon--stop {\n background: var(--mj-status-error);\n color: var(--mj-brand-on-primary);\n}\n.send-button-icon--stop:hover:not(:disabled) {\n background: color-mix(in srgb, var(--mj-status-error) 85%, var(--mj-text-primary));\n}\n/* \"Stop all N\": the square widens into a labelled button. */\n.send-button-icon--stop-all {\n width: auto;\n padding: 0 12px;\n gap: 8px;\n font-size: var(--mj-text-xs);\n font-weight: 600;\n white-space: nowrap;\n}\n.send-slot-count {\n background: color-mix(in srgb, var(--mj-brand-on-primary) 22%, transparent);\n border-radius: 999px;\n padding: 0 7px;\n font-size: var(--mj-text-xs);\n}\n/* Stopping: neutral and waiting, not greyed-out like an empty Send. */\n.send-button-icon--stopping:disabled {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-secondary);\n cursor: default;\n opacity: 1;\n}\n\n/* Status line: the left side of the action row, shrinking (with an ellipsis) before the buttons do. */\n.send-slot-status {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n margin-right: auto;\n padding-left: 5px;\n font-size: var(--mj-text-xs);\n color: var(--mj-text-secondary);\n}\n.send-slot-status--stopping {\n color: var(--mj-text-muted);\n}\n.send-slot-status-text,\n.send-slot-status-elapsed {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Attach buttons container */\n.attach-buttons {\n display: flex;\n gap: 2px;\n}\n.attach-button-icon {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n color: var(--mj-text-secondary);\n border: none;\n border-radius: 8px;\n cursor: pointer;\n transition: all 0.2s ease;\n flex-shrink: 0;\n}\n.attach-button-icon i {\n font-size: 1rem;\n}\n.attach-button-icon:hover:not(:disabled) {\n background: color-mix(in srgb, var(--mj-text-primary) 5%, transparent);\n color: var(--mj-brand-primary);\n transform: scale(1.05);\n}\n.attach-button-icon:active:not(:disabled) {\n transform: scale(0.95);\n}\n.attach-button-icon:disabled {\n color: var(--mj-text-muted);\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n/* Plan Mode toggle \u2014 inherits .attach-button-icon; when active it fills with the brand color\n so the on/off state reads clearly at a glance (parallels the voice button's active state). */\n.plan-mode-button-icon--active:not(:disabled) {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n.plan-mode-button-icon--active:not(:disabled):hover {\n background: var(--mj-brand-primary-hover, var(--mj-brand-primary));\n color: var(--mj-text-inverse);\n}\n\n/* Same active treatment as Plan Mode \u2014 the two are peers in the strip, so a different one would\n read as a different KIND of control. Everything else comes from .attach-button-icon. */\n.skills-button-icon--active:not(:disabled) {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n.skills-button-icon--active:not(:disabled):hover {\n background: var(--mj-brand-primary-hover, var(--mj-brand-primary));\n color: var(--mj-text-inverse);\n}\n\n/* Mobile adjustments: 481px - 768px */\n@media (max-width: 768px) {\n .message-input-box-container {\n border-radius: 10px;\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-text-primary) 6%, transparent);\n }\n .message-input-box-textarea {\n min-height: 80px;\n padding: 0.75rem;\n padding-right: 3rem;\n font-size: 16px;\n line-height: 1.4;\n }\n /* 32px buttons + 5px bottom padding = 37px row; the editor's own mobile padding is 5px all round */\n .input-wrapper ::ng-deep .mention-editor {\n min-height: calc(100px - 37px);\n max-height: calc(300px - 37px);\n padding-right: 5px;\n }\n .composer-actions {\n padding: 0 5px 5px;\n }\n /* The status line drops its elapsed time; \"Sage is working\" is enough at this width. */\n .send-slot-status-elapsed {\n display: none;\n }\n .send-button-icon {\n width: 32px;\n height: 32px;\n border-radius: 6px;\n }\n .send-button-icon i {\n font-size: 0.875rem;\n }\n .attach-button-icon {\n width: 32px;\n height: 32px;\n }\n .attach-button-icon i {\n font-size: 0.875rem;\n }\n}\n/* Small Phone adjustments: <= 480px */\n@media (max-width: 480px) {\n .message-input-box-container {\n border-radius: 8px;\n border-width: 1.5px;\n }\n .message-input-box-textarea {\n min-height: 60px;\n padding: 0.625rem;\n padding-right: 2.75rem;\n font-size: 16px;\n line-height: 1.3;\n }\n /* 36px send button + 3px bottom padding = 39px row */\n .input-wrapper ::ng-deep .mention-editor {\n min-height: calc(100px - 39px);\n max-height: calc(300px - 39px);\n }\n .composer-actions {\n padding: 0 3px 3px;\n }\n .send-button-icon {\n width: 36px;\n height: 36px;\n border-radius: 6px;\n }\n .send-button-icon i {\n font-size: 0.875rem;\n }\n .attach-button-icon {\n width: 32px;\n height: 32px;\n }\n .attach-button-icon i {\n font-size: 0.875rem;\n }\n}\n\n/* Voice call button \u2014 a phone icon in the attach-buttons cluster, left of send.\n Inherits .attach-button-icon base. Subtle brand glow on hover; active state (call\n live) renders a brand-color tint. The orb visual now lives ONLY inside the call\n overlay's banner. */\n.voice-button-icon i {\n color: var(--mj-brand-primary);\n}\n.voice-button-icon:hover:not(:disabled) i {\n filter: drop-shadow(0 0 4px color-mix(in srgb, var(--mj-brand-primary) 55%, transparent));\n}\n.voice-button-icon:disabled i {\n color: var(--mj-text-disabled);\n}\n.voice-button-icon--active {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, transparent);\n}\n.voice-button-icon--active:hover:not(:disabled) {\n background: color-mix(in srgb, var(--mj-brand-primary) 18%, transparent);\n}\n\n/* Voice OPTIONS caret \u2014 a slim chevron hugging the phone button. Opens the agent/model\n picker; intentionally quieter (smaller icon, narrower hit area) than the phone so the\n common instant-start click stays the visual primary. */\n.voice-options-button-icon {\n width: 20px;\n margin-left: -4px;\n}\n.voice-options-button-icon i {\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n}\n.voice-options-button-icon:hover:not(:disabled) i {\n color: var(--mj-brand-primary);\n}\n.voice-options-button-icon:disabled i {\n color: var(--mj-text-disabled);\n}\n"] }]
|
|
496
|
+
args: [{ standalone: false, selector: 'mj-message-input-box', template: "<div class=\"message-input-box-container\" (click)=\"OnContainerClick($event)\">\n <div class=\"input-wrapper\">\n <mj-mention-editor\n #mentionEditor\n [placeholder]=\"Placeholder\"\n [disabled]=\"Disabled\"\n [currentUser]=\"CurrentUser\"\n [enableMentions]=\"EnableMentions\"\n [TriggerProviders]=\"TriggerProviders\"\n [ExcludedTriggerKeys]=\"ExcludedTriggerKeys\"\n [Provider]=\"Provider\"\n [enableAttachments]=\"EnableAttachments\"\n [maxAttachments]=\"MaxAttachments\"\n [maxAttachmentSizeBytes]=\"MaxAttachmentSizeBytes\"\n [acceptedFileTypes]=\"AcceptedFileTypes\"\n [(ngModel)]=\"Value\"\n (valueChange)=\"OnValueChange($event)\"\n (enterPressed)=\"OnEnterPressed($event)\"\n (mentionSelected)=\"OnMentionSelected($event)\"\n (attachmentsChanged)=\"OnAttachmentsChanged($event)\"\n (attachmentError)=\"OnAttachmentError($event)\"\n (attachmentClicked)=\"OnAttachmentClicked($event)\"\n (editorBlurred)=\"Blurred.emit()\">\n </mj-mention-editor>\n\n <!-- Action row: its own strip below the editor (not floating over it), so text never runs behind a button -->\n <div class=\"composer-actions\">\n <!-- Status line, left of the action row: what the host is waiting on (\"Sage is working\"). A polite\n live region; the elapsed time is a pill in an aria-hidden child so its ticking is never announced. -->\n @if (StatusText) {\n <div class=\"send-slot-status\" [class.send-slot-status--stopping]=\"SendSlotMode === 'stopping'\" role=\"status\" aria-live=\"polite\">\n <i class=\"fa-solid fa-circle-notch fa-spin\" aria-hidden=\"true\"></i>\n <span class=\"send-slot-status-text\">{{ StatusText }}</span>\n @if (StatusElapsed) {\n <span class=\"send-slot-status-elapsed\" aria-hidden=\"true\">{{ StatusElapsed }}</span>\n }\n </div>\n }\n\n @if (EnableAttachments || EnableRealtime || EnablePlanMode || EnableSkills) {\n <div class=\"attach-buttons\">\n @if (EnableSkills) {\n <button\n class=\"attach-button-icon skills-button-icon\"\n [class.skills-button-icon--active]=\"SkillsActive\"\n [disabled]=\"Disabled\"\n [attr.aria-expanded]=\"SkillsActive\"\n aria-haspopup=\"true\"\n (mousedown)=\"$event.preventDefault(); $event.stopPropagation()\"\n (click)=\"OnSkillsClick($event); $event.stopPropagation()\"\n title=\"Skills, or type / in the message box\"\n aria-label=\"Show skills\"\n >\n <i class=\"fa-solid fa-wand-magic-sparkles\" aria-hidden=\"true\"></i>\n </button>\n }\n @if (EnablePlanMode) {\n <button\n class=\"attach-button-icon plan-mode-button-icon\"\n [class.plan-mode-button-icon--active]=\"PlanModeActive\"\n [disabled]=\"Disabled\"\n [attr.aria-pressed]=\"PlanModeActive\"\n (click)=\"PlanModeToggle.emit(); $event.stopPropagation()\"\n [title]=\"PlanModeActive ? 'Plan Mode ON \u2014 the agent presents a plan for your approval before it acts' : 'Plan Mode: have the agent present a plan for your approval before it acts'\"\n aria-label=\"Toggle Plan Mode\"\n >\n <i class=\"fa-solid fa-route\" aria-hidden=\"true\"></i>\n </button>\n }\n @if (EnableAttachments) {\n <button\n class=\"attach-button-icon\"\n [disabled]=\"Disabled\"\n (click)=\"OpenFilePicker(); $event.stopPropagation()\"\n title=\"Attach file\"\n >\n <i class=\"fa-solid fa-paperclip\"></i>\n </button>\n }\n @if (EnableRealtime) {\n <button\n class=\"attach-button-icon voice-button-icon\"\n [class.voice-button-icon--active]=\"VoiceActive\"\n [disabled]=\"!CanStartRealtime\"\n (click)=\"OnRealtimeClick(); $event.stopPropagation()\"\n [title]=\"VoiceActive ? 'Voice call active' : 'Start a voice call'\"\n aria-label=\"Start a voice call\"\n >\n <i class=\"fa-solid fa-phone\" aria-hidden=\"true\"></i>\n </button>\n <!-- Caret beside the phone: opens the agent/model picker so call OPTIONS stay one\n click away while the plain phone click keeps its instant-start behavior. -->\n <button\n class=\"attach-button-icon voice-options-button-icon\"\n [disabled]=\"!CanStartRealtime\"\n (click)=\"OnRealtimeOptionsClick(); $event.stopPropagation()\"\n title=\"Voice call options (choose agent and voice model)\"\n aria-label=\"Voice call options\"\n >\n <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i>\n </button>\n }\n </div>\n }\n\n <!-- The send slot. ONE button in every mode (send / stop / stopping) so focus is kept across the\n change; see SendSlotMode. -->\n <button\n class=\"send-button-icon\"\n [class.send-button-icon--stop]=\"SendSlotMode === 'stop'\"\n [class.send-button-icon--stop-all]=\"IsStopAll\"\n [class.send-button-icon--stopping]=\"SendSlotMode === 'stopping'\"\n [disabled]=\"SendSlotDisabled\"\n (click)=\"OnSendSlotClick()\"\n [title]=\"SendSlotLabel\"\n [attr.aria-label]=\"SendSlotLabel\"\n [attr.aria-busy]=\"SendSlotMode === 'stopping' ? true : null\"\n >\n @switch (SendSlotMode) {\n @case ('stop') {\n <i class=\"fa-solid fa-stop\" aria-hidden=\"true\"></i>\n @if (IsStopAll) {\n <span class=\"send-slot-label\">Stop all</span>\n <span class=\"send-slot-count\">{{ StopCount }}</span>\n }\n }\n @case ('stopping') {\n <i class=\"fa-solid fa-circle-notch fa-spin\" aria-hidden=\"true\"></i>\n }\n @default {\n <i class=\"fa-solid fa-paper-plane\"></i>\n }\n }\n </button>\n </div>\n </div>\n</div>\n", styles: [".message-input-box-container {\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface-card);\n border: 2px solid var(--mj-border-default);\n border-radius: 12px;\n box-shadow: var(--mj-shadow-sm);\n transition: all 0.2s ease;\n overflow: visible;\n position: relative;\n cursor: text;\n}\n.message-input-box-container:focus-within {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 4px 16px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n.input-wrapper {\n position: relative;\n display: flex;\n flex-direction: column;\n padding: 5px;\n}\n.input-wrapper mj-mention-editor {\n display: block;\n}\n/* The action buttons get their own row below the editor instead of floating over its bottom-right corner, so\n text can never run behind them (at any scroll position). The editor gives up the row's height (36px buttons +\n 7px bottom padding = 43px) so the composer's resting and maximum sizes are unchanged, and it no longer needs the\n right padding that used to keep the first lines clear of the send button. */\n.input-wrapper ::ng-deep .mention-editor {\n min-height: calc(100px - 43px);\n max-height: calc(300px - 43px);\n padding-right: 12px;\n}\n.composer-actions {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 2px;\n padding: 0 7px 7px;\n}\n\n.message-input-box-textarea {\n flex: 1;\n min-height: 100px;\n padding: 1rem;\n padding-right: 3.5rem;\n border: 0 !important;\n outline: 0 !important;\n box-shadow: none !important;\n -webkit-appearance: none !important;\n -moz-appearance: none !important;\n appearance: none !important;\n font-family: inherit;\n font-size: 1rem;\n line-height: 1.5;\n resize: vertical;\n background: transparent;\n color: var(--mj-text-primary);\n border: 0 !important;\n}\n.message-input-box-textarea::placeholder {\n color: var(--mj-text-muted);\n}\n.message-input-box-textarea:disabled {\n cursor: not-allowed;\n opacity: 0.6;\n background: var(--mj-bg-surface-sunken);\n}\n.message-input-box-textarea:focus, .message-input-box-textarea:active, .message-input-box-textarea:focus-visible {\n outline: 0 !important;\n box-shadow: none !important;\n}\n\n.send-button-icon {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border: none;\n border-radius: 8px;\n cursor: pointer;\n transition: all 0.2s ease;\n flex-shrink: 0;\n}\n.send-button-icon i {\n font-size: 1rem;\n}\n.send-button-icon:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n transform: scale(1.05);\n}\n.send-button-icon:active:not(:disabled) {\n transform: scale(0.95);\n}\n.send-button-icon:disabled {\n background: var(--mj-border-default);\n color: var(--mj-text-muted);\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n/* The send slot as a stop control. Same element, same place; red says what a click does now. */\n.send-button-icon--stop {\n background: var(--mj-status-error);\n color: var(--mj-brand-on-primary);\n}\n.send-button-icon--stop:hover:not(:disabled) {\n background: color-mix(in srgb, var(--mj-status-error) 85%, var(--mj-text-primary));\n}\n/* \"Stop all N\": the square widens into a labelled button. */\n.send-button-icon--stop-all {\n width: auto;\n padding: 0 12px;\n gap: 8px;\n font-size: var(--mj-text-xs);\n font-weight: 600;\n white-space: nowrap;\n}\n.send-slot-count {\n background: color-mix(in srgb, var(--mj-brand-on-primary) 22%, transparent);\n border-radius: 999px;\n padding: 0 7px;\n font-size: var(--mj-text-xs);\n}\n/* Stopping: neutral and waiting, not greyed-out like an empty Send. */\n.send-button-icon--stopping:disabled {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-secondary);\n cursor: default;\n opacity: 1;\n}\n\n/* Status line: the left side of the action row, shrinking (with an ellipsis) before the buttons do. */\n.send-slot-status {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n margin-right: auto;\n padding-left: 5px;\n font-size: var(--mj-text-xs);\n color: var(--mj-text-secondary);\n}\n.send-slot-status--stopping {\n color: var(--mj-text-muted);\n}\n.send-slot-status-text {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n/* The elapsed time as a pill: the reply time pill's geometry, on the info status pair, which has\n its own dark-mode values (a brand tint on a dark surface is not legible). */\n.send-slot-status-elapsed {\n flex-shrink: 0;\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n line-height: 1.4;\n white-space: nowrap;\n font-variant-numeric: tabular-nums;\n background: var(--mj-status-info-bg);\n color: var(--mj-status-info-text);\n}\n\n/* Attach buttons container */\n.attach-buttons {\n display: flex;\n gap: 2px;\n}\n.attach-button-icon {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n color: var(--mj-text-secondary);\n border: none;\n border-radius: 8px;\n cursor: pointer;\n transition: all 0.2s ease;\n flex-shrink: 0;\n}\n.attach-button-icon i {\n font-size: 1rem;\n}\n.attach-button-icon:hover:not(:disabled) {\n background: color-mix(in srgb, var(--mj-text-primary) 5%, transparent);\n color: var(--mj-brand-primary);\n transform: scale(1.05);\n}\n.attach-button-icon:active:not(:disabled) {\n transform: scale(0.95);\n}\n.attach-button-icon:disabled {\n color: var(--mj-text-muted);\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n/* Plan Mode toggle \u2014 inherits .attach-button-icon; when active it fills with the brand color\n so the on/off state reads clearly at a glance (parallels the voice button's active state). */\n.plan-mode-button-icon--active:not(:disabled) {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n.plan-mode-button-icon--active:not(:disabled):hover {\n background: var(--mj-brand-primary-hover, var(--mj-brand-primary));\n color: var(--mj-text-inverse);\n}\n\n/* Same active treatment as Plan Mode \u2014 the two are peers in the strip, so a different one would\n read as a different KIND of control. Everything else comes from .attach-button-icon. */\n.skills-button-icon--active:not(:disabled) {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n.skills-button-icon--active:not(:disabled):hover {\n background: var(--mj-brand-primary-hover, var(--mj-brand-primary));\n color: var(--mj-text-inverse);\n}\n\n/* Mobile adjustments: 481px - 768px */\n@media (max-width: 768px) {\n .message-input-box-container {\n border-radius: 10px;\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-text-primary) 6%, transparent);\n }\n .message-input-box-textarea {\n min-height: 80px;\n padding: 0.75rem;\n padding-right: 3rem;\n font-size: 16px;\n line-height: 1.4;\n }\n /* 32px buttons + 5px bottom padding = 37px row; the editor's own mobile padding is 5px all round */\n .input-wrapper ::ng-deep .mention-editor {\n min-height: calc(100px - 37px);\n max-height: calc(300px - 37px);\n padding-right: 5px;\n }\n .composer-actions {\n padding: 0 5px 5px;\n }\n /* The status line drops its elapsed time; \"Sage is working\" is enough at this width. */\n .send-slot-status-elapsed {\n display: none;\n }\n .send-button-icon {\n width: 32px;\n height: 32px;\n border-radius: 6px;\n }\n .send-button-icon i {\n font-size: 0.875rem;\n }\n .attach-button-icon {\n width: 32px;\n height: 32px;\n }\n .attach-button-icon i {\n font-size: 0.875rem;\n }\n}\n/* Small Phone adjustments: <= 480px */\n@media (max-width: 480px) {\n .message-input-box-container {\n border-radius: 8px;\n border-width: 1.5px;\n }\n .message-input-box-textarea {\n min-height: 60px;\n padding: 0.625rem;\n padding-right: 2.75rem;\n font-size: 16px;\n line-height: 1.3;\n }\n /* 36px send button + 3px bottom padding = 39px row */\n .input-wrapper ::ng-deep .mention-editor {\n min-height: calc(100px - 39px);\n max-height: calc(300px - 39px);\n }\n .composer-actions {\n padding: 0 3px 3px;\n }\n .send-button-icon {\n width: 36px;\n height: 36px;\n border-radius: 6px;\n }\n .send-button-icon i {\n font-size: 0.875rem;\n }\n .attach-button-icon {\n width: 32px;\n height: 32px;\n }\n .attach-button-icon i {\n font-size: 0.875rem;\n }\n}\n\n/* Voice call button \u2014 a phone icon in the attach-buttons cluster, left of send.\n Inherits .attach-button-icon base. Subtle brand glow on hover; active state (call\n live) renders a brand-color tint. The orb visual now lives ONLY inside the call\n overlay's banner. */\n.voice-button-icon i {\n color: var(--mj-brand-primary);\n}\n.voice-button-icon:hover:not(:disabled) i {\n filter: drop-shadow(0 0 4px color-mix(in srgb, var(--mj-brand-primary) 55%, transparent));\n}\n.voice-button-icon:disabled i {\n color: var(--mj-text-disabled);\n}\n.voice-button-icon--active {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, transparent);\n}\n.voice-button-icon--active:hover:not(:disabled) {\n background: color-mix(in srgb, var(--mj-brand-primary) 18%, transparent);\n}\n\n/* Voice OPTIONS caret \u2014 a slim chevron hugging the phone button. Opens the agent/model\n picker; intentionally quieter (smaller icon, narrower hit area) than the phone so the\n common instant-start click stays the visual primary. */\n.voice-options-button-icon {\n width: 20px;\n margin-left: -4px;\n}\n.voice-options-button-icon i {\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n}\n.voice-options-button-icon:hover:not(:disabled) i {\n color: var(--mj-brand-primary);\n}\n.voice-options-button-icon:disabled i {\n color: var(--mj-text-disabled);\n}\n"] }]
|
|
497
497
|
}], null, { MentionEditor: [{
|
|
498
498
|
type: ViewChild,
|
|
499
499
|
args: ['mentionEditor']
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message-input-box.component.js","sourceRoot":"","sources":["../../../../src/lib/components/message/message-input-box.component.ts","../../../../src/lib/components/message/message-input-box.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, ViewChild } from '@angular/core';\nimport { IMetadataProvider, UserInfo } from '@memberjunction/core';\nimport { ComposerTriggerProvider, MentionSuggestion } from '../../composer-trigger-provider';\nimport { MentionEditorComponent, PendingAttachment } from '../mention/mention-editor.component';\nimport { BeforeSkillsOpenedEventArgs } from '../../events/composer-events';\n\n/**\n * Reusable message input box component (presentational)\n * Now uses MentionEditorComponent for rich @mention functionality with chips\n *\n * Handles:\n * - Text input with keyboard shortcuts via MentionEditorComponent\n * - @mention autocomplete with visual chips (contentEditable)\n * - Send button\n *\n * Does NOT handle:\n * - Saving messages to database\n * - Agent invocation\n * - Artifact creation\n * - Conversation management\n */\n/**\n * What the composer's send slot is doing. The slot is ONE button whose look, label and click\n * target follow this mode, so focus survives the change from Send to Stop and back.\n * - `send`: the send button, as always.\n * - `stop`: a stop control; the click emits `StopClicked`. The host decides what that stops.\n * - `stopping`: a stop was asked for and has not landed yet; the slot is disabled with a spinner.\n */\nexport type SendSlotMode = 'send' | 'stop' | 'stopping';\n\n@Component({\n standalone: false,\n selector: 'mj-message-input-box',\n templateUrl: './message-input-box.component.html',\n styleUrls: ['./message-input-box.component.css']\n})\nexport class MessageInputBoxComponent {\n @ViewChild('mentionEditor') MentionEditor?: MentionEditorComponent;\n\n /** @deprecated Use {@link MentionEditor}. */\n get mentionEditor(): MentionEditorComponent | undefined {\n return this.MentionEditor;\n }\n /** @deprecated Use {@link MentionEditor}. */\n set mentionEditor(value: MentionEditorComponent | undefined) {\n this.MentionEditor = value;\n }\n\n @Input() Placeholder: string = 'Type your message to start a new conversation...';\n @Input() Disabled: boolean = false;\n @Input() Value: string = '';\n @Input() ShowCharacterCount: boolean = false;\n /** Master switch for all mention/command triggers (pass-through to the mention editor). */\n @Input() EnableMentions: boolean = true;\n /** Explicit trigger-provider list (pass-through to the mention editor; explicit list wins over discovery). */\n @Input() TriggerProviders: ComposerTriggerProvider[] | null = null;\n /** Discovery-mode filter: provider Keys to skip (pass-through to the mention editor). */\n @Input() ExcludedTriggerKeys: string[] = [];\n /** Optional metadata provider scoping this composer (pass-through to the mention editor). */\n @Input() Provider: IMetadataProvider | null = null;\n @Input() CurrentUser?: UserInfo;\n @Input() Rows: number = 3;\n\n // Attachment settings\n @Input() EnableAttachments: boolean = true;\n @Input() MaxAttachments: number = 10;\n @Input() MaxAttachmentSizeBytes: number = 20 * 1024 * 1024; // 20MB\n @Input() AcceptedFileTypes: string = 'image/*';\n\n /** Shows the in-composer mic button when true. */\n @Input() EnableRealtime: boolean = false;\n /** Whether a realtime voice session is currently active (mic renders in its active state). */\n @Input() VoiceActive: boolean = false;\n /** Whether a voice session can be started right now (mic Disabled when false). */\n @Input() CanStartRealtime: boolean = true;\n /** Shows the in-composer Plan Mode toggle button when true. */\n @Input() EnablePlanMode: boolean = false;\n /** Current Plan Mode toggle state (renders the button in its active state). */\n @Input() PlanModeActive: boolean = false;\n /**\n * Shows the in-composer Skills button when true.\n *\n * Gated separately from `EnableMentions` because a host can have the `/` trigger active and\n * still not want the button — but the common case is that a composer offering skill commands\n * should advertise them, since `/` is otherwise invisible.\n */\n @Input() EnableSkills: boolean = false;\n\n // ── The send slot as a stop control (semantics belong to the host) ──────────────────────\n /** What the send slot is doing; see {@link SendSlotMode}. The box never decides this itself. */\n @Input() SendSlotMode: SendSlotMode = 'send';\n /** How many things a stop would stop. Above 1 in `stop` mode the slot reads \"Stop all\" with this count. */\n @Input() StopCount: number = 0;\n /** Tooltip and accessible name of the slot in `stop` mode, e.g. \"Stop Sage\" or \"Stop all 2 runs\". */\n @Input() StopLabel: string = 'Stop';\n /**\n * A status line rendered bottom-left inside the box while non-null, e.g. \"Sage is working\". It is\n * a polite live region, so a change is announced once; keep it short.\n */\n @Input() StatusText: string | null = null;\n /**\n * An elapsed time shown after {@link StatusText}, e.g. \"41s\". Separate from the text so it is kept\n * out of the live region (a ticking clock must not be announced every second) and dropped on\n * narrow screens.\n */\n @Input() StatusElapsed: string | null = null;\n\n /** True when the slot reads \"Stop all\": `stop` mode with more than one thing to stop. */\n get IsStopAll(): boolean {\n return this.SendSlotMode === 'stop' && this.StopCount > 1;\n }\n\n /** The slot's disabled state per mode: Send follows {@link CanSend}; Stop is always live; Stopping waits. */\n get SendSlotDisabled(): boolean {\n switch (this.SendSlotMode) {\n case 'stop': return false;\n case 'stopping': return true;\n default: return !this.CanSend;\n }\n }\n\n /** The slot's tooltip and accessible name per mode. */\n get SendSlotLabel(): string {\n switch (this.SendSlotMode) {\n case 'stop': return this.StopLabel;\n case 'stopping': return 'Stopping';\n default: return 'Send message (Enter)';\n }\n }\n\n /** The slot's click: a stop request in `stop` mode, a send in `send` mode, nothing while stopping. */\n OnSendSlotClick(): void {\n switch (this.SendSlotMode) {\n case 'stop': this.StopClicked.emit(); return;\n case 'stopping': return;\n default: this.OnSendClick();\n }\n }\n /**\n * True while the skill dropdown is open, driving the button's expanded state.\n *\n * Note the ARIA differs from Plan Mode next to it, deliberately: Plan Mode is a toggle BUTTON\n * (a mode that stays on), so `aria-pressed` is right there. This one opens a popup, so it is\n * `aria-expanded` + `aria-haspopup`. Using `aria-pressed` here would have a screen reader\n * announce \"pressed\" for something that actually just revealed a list.\n *\n * DERIVED, not an @Input, and this is the difference from Plan Mode. Plan Mode's active state is\n * a persisted user preference the host owns and threads down. \"Is the skill dropdown open\" is\n * intrinsic to this composer, so an @Input would be an API the host cannot meaningfully answer\n * and would leave the button permanently unpressed if nobody bound it.\n */\n get SkillsActive(): boolean {\n return this.MentionEditor?.IsTriggerOpen('/') ?? false;\n }\n\n /** Composer lost focus — hosts persist drafts on this. */\n @Output() Blurred = new EventEmitter<void>();\n @Output() TextSubmitted = new EventEmitter<string>();\n @Output() ValueChange = new EventEmitter<string>();\n @Output() AttachmentsChanged = new EventEmitter<PendingAttachment[]>();\n @Output() AttachmentError = new EventEmitter<string>();\n @Output() AttachmentClicked = new EventEmitter<PendingAttachment>();\n /** Emitted when the user clicks the mic button to start/stop a voice session. */\n @Output() VoiceRequested = new EventEmitter<void>();\n /**\n * Emitted when the user clicks the small caret next to the phone button — the host opens the\n * voice agent/model picker so call options (which agent, which voice model) stay reachable\n * without adding friction to the plain phone click's instant-start path.\n */\n @Output() VoiceOptionsRequested = new EventEmitter<void>();\n /** Emitted when the user clicks the in-composer Plan Mode toggle button. */\n @Output() PlanModeToggle = new EventEmitter<void>();\n /**\n * Fired when the send slot is clicked in `stop` mode. The box only reports the click; what is\n * stopped, and how, is the host's business (this widget knows nothing about agents or runs).\n */\n @Output() StopClicked = new EventEmitter<void>();\n /**\n * Fired BEFORE the Skills button opens the dropdown. Flip `Cancel` to veto — a host gating\n * skills by entitlement, or surfacing them elsewhere, blocks it here.\n *\n * A pair rather than a lone emitter because this is an ACTION a host might reasonably refuse\n * (UI_LAYERING_GUIDE section 6, rule 1). Handlers must be synchronous; `Cancel` travels back\n * through EventEmitter's synchronous dispatch.\n */\n @Output() BeforeSkillsOpened = new EventEmitter<BeforeSkillsOpenedEventArgs>();\n /**\n * Fired AFTER the dropdown actually opened. Not emitted on the canceled path, and not emitted\n * when no active provider owns the trigger — so a host counting this counts dropdowns the user\n * saw, rather than clicks.\n */\n @Output() AfterSkillsOpened = new EventEmitter<void>();\n\n /**\n * Open the skill-command dropdown, the same one `/` opens.\n *\n * Routed through the editor's OpenTrigger rather than a parallel menu so permission filtering,\n * per-skill icons and chip insertion all stay in one implementation.\n */\n /**\n * NOTE: the template pairs this with\n * `(mousedown)=\"$event.preventDefault(); $event.stopPropagation()\"`, and BOTH halves are\n * load-bearing.\n *\n * `preventDefault` — a <button> takes focus on mousedown, which blurs the editor, and the editor\n * closes its dropdown 200ms after losing focus. Without it the menu opened and then shut itself.\n *\n * `stopPropagation` — the editor's click-away listener is on `document:mousedown` and exempts only\n * presses inside the EDITOR's host. This button is a sibling of `<mj-mention-editor>`, so a press\n * on it read as an outside press: mousedown closed the dropdown, then click saw `SkillsActive`\n * already false and reopened it. The menu blinked, could not be dismissed by the button that\n * opened it, and the Before/After pair fired on every click — the exact over-counting the toggle\n * branch below exists to prevent. Stopping propagation states that this press belongs to the\n * dropdown machinery.\n */\n OnSkillsClick(event?: Event): void {\n // Toggle closed first. A disclosure button has to be able to close what it opened, and without\n // this a second click re-ran the open path: it re-emitted the Before/After pair (so a host\n // counting opens over-counted) and re-captured the trigger's baseline at the new caret, which\n // corrupts the query offset once anything has been typed.\n if (this.SkillsActive) {\n this.MentionEditor?.closeMentionDropdown();\n return;\n }\n const args = new BeforeSkillsOpenedEventArgs();\n this.BeforeSkillsOpened.emit(args);\n if (args.Cancel) {\n return;\n }\n const anchor = (event?.currentTarget as HTMLElement | undefined) ?? null;\n if (this.MentionEditor?.OpenTrigger('/', anchor)) {\n this.AfterSkillsOpened.emit();\n }\n }\n\n OnRealtimeClick(): void {\n this.VoiceRequested.emit();\n }\n\n OnRealtimeOptionsClick(): void {\n this.VoiceOptionsRequested.emit();\n }\n\n get CanSend(): boolean {\n const hasText = this.Value.trim().length > 0;\n const hasAttachments = this.MentionEditor?.hasAttachments() || false;\n return !this.Disabled && (hasText || hasAttachments);\n }\n\n /**\n * Handle Value changes from MentionEditorComponent\n */\n OnValueChange(newValue: string): void {\n this.Value = newValue;\n this.ValueChange.emit(this.Value);\n }\n\n /**\n * Handle attachment changes from MentionEditorComponent\n */\n OnAttachmentsChanged(attachments: PendingAttachment[]): void {\n this.AttachmentsChanged.emit(attachments);\n }\n\n /**\n * Handle attachment errors from MentionEditorComponent\n */\n OnAttachmentError(error: string): void {\n this.AttachmentError.emit(error);\n }\n\n /**\n * Handle attachment click from MentionEditorComponent\n */\n OnAttachmentClicked(attachment: PendingAttachment): void {\n this.AttachmentClicked.emit(attachment);\n }\n\n /**\n * Handle Enter key from MentionEditorComponent\n * Extracts plain text with JSON-encoded mentions for message submission\n */\n OnEnterPressed(_text: string): void {\n this.OnSendClick();\n }\n\n /**\n * Handle mention selection from MentionEditorComponent\n */\n OnMentionSelected(suggestion: MentionSuggestion): void {\n // MentionEditorComponent already inserts the mention chip\n // This is just for additional tracking/analytics if needed\n }\n\n /**\n * Send the message\n * Extracts plain text with JSON-encoded mentions for proper persistence\n */\n OnSendClick(): void {\n if (this.CanSend) {\n // Get plain text with JSON-encoded mentions (preserves configuration info)\n const textToSend = this.MentionEditor?.getPlainTextWithJsonMentions() || this.Value.trim();\n this.TextSubmitted.emit(textToSend);\n this.Value = ''; // Clear input after sending\n\n // Clear the editor content\n if (this.MentionEditor) {\n this.MentionEditor.clear();\n }\n\n this.ValueChange.emit(this.Value);\n }\n }\n\n /**\n * Handle clicks on the container - focus the mention editor\n * Only moves cursor to end if clicking outside the contentEditable area\n */\n OnContainerClick(event: MouseEvent): void {\n const target = event.target as HTMLElement;\n\n // Don't handle clicks on the send button\n if (target.closest('.send-button-icon')) {\n return;\n }\n\n const editor = this.MentionEditor?.editorRef?.nativeElement;\n if (!editor) return;\n\n // If clicking directly on the editor or its children, let the browser handle cursor placement\n if (target === editor || editor.contains(target)) {\n return;\n }\n\n // Only if clicking on the container (empty space), focus and move cursor to end\n editor.focus();\n const selection = window.getSelection();\n const range = document.createRange();\n\n if (selection) {\n range.selectNodeContents(editor);\n range.collapse(false); // Collapse to end\n selection.removeAllRanges();\n selection.addRange(range);\n }\n }\n\n /**\n * Public method to focus the input programmatically\n */\n /** Inserts a resolved mention chip + space and focuses (see MentionEditorComponent.InsertMention). */\n InsertMention(suggestion: MentionSuggestion, focus: boolean = true): boolean {\n return this.MentionEditor?.InsertMention(suggestion, focus) ?? false;\n }\n\n /** Focus with the caret at the end of content (see MentionEditorComponent.FocusCaretAtEnd). */\n FocusCaretAtEnd(): boolean {\n return this.MentionEditor?.FocusCaretAtEnd() ?? false;\n }\n\n Focus(): void {\n const editor = this.MentionEditor?.editorRef?.nativeElement;\n if (editor) {\n editor.focus();\n }\n }\n\n /** @deprecated Use {@link Focus}. */\n focus(): void {\n return this.Focus();\n }\n\n /**\n * Get mention chip data including configuration presets\n */\n GetMentionChipsData(): Array<{ id: string; type: string; name: string; presetId?: string; presetName?: string }> {\n return this.MentionEditor?.getMentionChipsData() || [];\n }\n\n /**\n * Get pending attachments from the editor\n */\n GetPendingAttachments(): PendingAttachment[] {\n return this.MentionEditor?.getPendingAttachments() || [];\n }\n\n /**\n * Open file picker programmatically\n */\n OpenFilePicker(): void {\n this.MentionEditor?.openFilePicker();\n }\n\n /**\n * Add an artifact as a pending attachment (called by parent after artifact selection)\n */\n AddArtifactAttachment(artifact: {\n fileID: string; fileName: string; mimeType: string;\n sizeBytes: number; artifactVersionId?: string;\n }): PendingAttachment | undefined {\n return this.MentionEditor?.AddArtifactAttachment(artifact);\n }\n}\n","<div class=\"message-input-box-container\" (click)=\"OnContainerClick($event)\">\n <div class=\"input-wrapper\">\n <mj-mention-editor\n #mentionEditor\n [placeholder]=\"Placeholder\"\n [disabled]=\"Disabled\"\n [currentUser]=\"CurrentUser\"\n [enableMentions]=\"EnableMentions\"\n [TriggerProviders]=\"TriggerProviders\"\n [ExcludedTriggerKeys]=\"ExcludedTriggerKeys\"\n [Provider]=\"Provider\"\n [enableAttachments]=\"EnableAttachments\"\n [maxAttachments]=\"MaxAttachments\"\n [maxAttachmentSizeBytes]=\"MaxAttachmentSizeBytes\"\n [acceptedFileTypes]=\"AcceptedFileTypes\"\n [(ngModel)]=\"Value\"\n (valueChange)=\"OnValueChange($event)\"\n (enterPressed)=\"OnEnterPressed($event)\"\n (mentionSelected)=\"OnMentionSelected($event)\"\n (attachmentsChanged)=\"OnAttachmentsChanged($event)\"\n (attachmentError)=\"OnAttachmentError($event)\"\n (attachmentClicked)=\"OnAttachmentClicked($event)\"\n (editorBlurred)=\"Blurred.emit()\">\n </mj-mention-editor>\n\n <!-- Action row: its own strip below the editor (not floating over it), so text never runs behind a button -->\n <div class=\"composer-actions\">\n <!-- Status line, left of the action row: what the host is waiting on (\"Sage is working\"). A polite\n live region; the elapsed time sits in an aria-hidden child so its ticking is never announced. -->\n @if (StatusText) {\n <div class=\"send-slot-status\" [class.send-slot-status--stopping]=\"SendSlotMode === 'stopping'\" role=\"status\" aria-live=\"polite\">\n <i class=\"fa-solid fa-circle-notch fa-spin\" aria-hidden=\"true\"></i>\n <span class=\"send-slot-status-text\">{{ StatusText }}</span>\n @if (StatusElapsed) {\n <span class=\"send-slot-status-elapsed\" aria-hidden=\"true\">, {{ StatusElapsed }}</span>\n }\n </div>\n }\n\n @if (EnableAttachments || EnableRealtime || EnablePlanMode || EnableSkills) {\n <div class=\"attach-buttons\">\n @if (EnableSkills) {\n <button\n class=\"attach-button-icon skills-button-icon\"\n [class.skills-button-icon--active]=\"SkillsActive\"\n [disabled]=\"Disabled\"\n [attr.aria-expanded]=\"SkillsActive\"\n aria-haspopup=\"true\"\n (mousedown)=\"$event.preventDefault(); $event.stopPropagation()\"\n (click)=\"OnSkillsClick($event); $event.stopPropagation()\"\n title=\"Skills, or type / in the message box\"\n aria-label=\"Show skills\"\n >\n <i class=\"fa-solid fa-wand-magic-sparkles\" aria-hidden=\"true\"></i>\n </button>\n }\n @if (EnablePlanMode) {\n <button\n class=\"attach-button-icon plan-mode-button-icon\"\n [class.plan-mode-button-icon--active]=\"PlanModeActive\"\n [disabled]=\"Disabled\"\n [attr.aria-pressed]=\"PlanModeActive\"\n (click)=\"PlanModeToggle.emit(); $event.stopPropagation()\"\n [title]=\"PlanModeActive ? 'Plan Mode ON — the agent presents a plan for your approval before it acts' : 'Plan Mode: have the agent present a plan for your approval before it acts'\"\n aria-label=\"Toggle Plan Mode\"\n >\n <i class=\"fa-solid fa-route\" aria-hidden=\"true\"></i>\n </button>\n }\n @if (EnableAttachments) {\n <button\n class=\"attach-button-icon\"\n [disabled]=\"Disabled\"\n (click)=\"OpenFilePicker(); $event.stopPropagation()\"\n title=\"Attach file\"\n >\n <i class=\"fa-solid fa-paperclip\"></i>\n </button>\n }\n @if (EnableRealtime) {\n <button\n class=\"attach-button-icon voice-button-icon\"\n [class.voice-button-icon--active]=\"VoiceActive\"\n [disabled]=\"!CanStartRealtime\"\n (click)=\"OnRealtimeClick(); $event.stopPropagation()\"\n [title]=\"VoiceActive ? 'Voice call active' : 'Start a voice call'\"\n aria-label=\"Start a voice call\"\n >\n <i class=\"fa-solid fa-phone\" aria-hidden=\"true\"></i>\n </button>\n <!-- Caret beside the phone: opens the agent/model picker so call OPTIONS stay one\n click away while the plain phone click keeps its instant-start behavior. -->\n <button\n class=\"attach-button-icon voice-options-button-icon\"\n [disabled]=\"!CanStartRealtime\"\n (click)=\"OnRealtimeOptionsClick(); $event.stopPropagation()\"\n title=\"Voice call options (choose agent and voice model)\"\n aria-label=\"Voice call options\"\n >\n <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i>\n </button>\n }\n </div>\n }\n\n <!-- The send slot. ONE button in every mode (send / stop / stopping) so focus is kept across the\n change; see SendSlotMode. -->\n <button\n class=\"send-button-icon\"\n [class.send-button-icon--stop]=\"SendSlotMode === 'stop'\"\n [class.send-button-icon--stop-all]=\"IsStopAll\"\n [class.send-button-icon--stopping]=\"SendSlotMode === 'stopping'\"\n [disabled]=\"SendSlotDisabled\"\n (click)=\"OnSendSlotClick()\"\n [title]=\"SendSlotLabel\"\n [attr.aria-label]=\"SendSlotLabel\"\n [attr.aria-busy]=\"SendSlotMode === 'stopping' ? true : null\"\n >\n @switch (SendSlotMode) {\n @case ('stop') {\n <i class=\"fa-solid fa-stop\" aria-hidden=\"true\"></i>\n @if (IsStopAll) {\n <span class=\"send-slot-label\">Stop all</span>\n <span class=\"send-slot-count\">{{ StopCount }}</span>\n }\n }\n @case ('stopping') {\n <i class=\"fa-solid fa-circle-notch fa-spin\" aria-hidden=\"true\"></i>\n }\n @default {\n <i class=\"fa-solid fa-paper-plane\"></i>\n }\n }\n </button>\n </div>\n </div>\n</div>\n"]}
|
|
1
|
+
{"version":3,"file":"message-input-box.component.js","sourceRoot":"","sources":["../../../../src/lib/components/message/message-input-box.component.ts","../../../../src/lib/components/message/message-input-box.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, ViewChild } from '@angular/core';\nimport { IMetadataProvider, UserInfo } from '@memberjunction/core';\nimport { ComposerTriggerProvider, MentionSuggestion } from '../../composer-trigger-provider';\nimport { MentionEditorComponent, PendingAttachment } from '../mention/mention-editor.component';\nimport { BeforeSkillsOpenedEventArgs } from '../../events/composer-events';\n\n/**\n * Reusable message input box component (presentational)\n * Now uses MentionEditorComponent for rich @mention functionality with chips\n *\n * Handles:\n * - Text input with keyboard shortcuts via MentionEditorComponent\n * - @mention autocomplete with visual chips (contentEditable)\n * - Send button\n *\n * Does NOT handle:\n * - Saving messages to database\n * - Agent invocation\n * - Artifact creation\n * - Conversation management\n */\n/**\n * What the composer's send slot is doing. The slot is ONE button whose look, label and click\n * target follow this mode, so focus survives the change from Send to Stop and back.\n * - `send`: the send button, as always.\n * - `stop`: a stop control; the click emits `StopClicked`. The host decides what that stops.\n * - `stopping`: a stop was asked for and has not landed yet; the slot is disabled with a spinner.\n */\nexport type SendSlotMode = 'send' | 'stop' | 'stopping';\n\n@Component({\n standalone: false,\n selector: 'mj-message-input-box',\n templateUrl: './message-input-box.component.html',\n styleUrls: ['./message-input-box.component.css']\n})\nexport class MessageInputBoxComponent {\n @ViewChild('mentionEditor') MentionEditor?: MentionEditorComponent;\n\n /** @deprecated Use {@link MentionEditor}. */\n get mentionEditor(): MentionEditorComponent | undefined {\n return this.MentionEditor;\n }\n /** @deprecated Use {@link MentionEditor}. */\n set mentionEditor(value: MentionEditorComponent | undefined) {\n this.MentionEditor = value;\n }\n\n @Input() Placeholder: string = 'Type your message to start a new conversation...';\n @Input() Disabled: boolean = false;\n @Input() Value: string = '';\n @Input() ShowCharacterCount: boolean = false;\n /** Master switch for all mention/command triggers (pass-through to the mention editor). */\n @Input() EnableMentions: boolean = true;\n /** Explicit trigger-provider list (pass-through to the mention editor; explicit list wins over discovery). */\n @Input() TriggerProviders: ComposerTriggerProvider[] | null = null;\n /** Discovery-mode filter: provider Keys to skip (pass-through to the mention editor). */\n @Input() ExcludedTriggerKeys: string[] = [];\n /** Optional metadata provider scoping this composer (pass-through to the mention editor). */\n @Input() Provider: IMetadataProvider | null = null;\n @Input() CurrentUser?: UserInfo;\n @Input() Rows: number = 3;\n\n // Attachment settings\n @Input() EnableAttachments: boolean = true;\n @Input() MaxAttachments: number = 10;\n @Input() MaxAttachmentSizeBytes: number = 20 * 1024 * 1024; // 20MB\n @Input() AcceptedFileTypes: string = 'image/*';\n\n /** Shows the in-composer mic button when true. */\n @Input() EnableRealtime: boolean = false;\n /** Whether a realtime voice session is currently active (mic renders in its active state). */\n @Input() VoiceActive: boolean = false;\n /** Whether a voice session can be started right now (mic Disabled when false). */\n @Input() CanStartRealtime: boolean = true;\n /** Shows the in-composer Plan Mode toggle button when true. */\n @Input() EnablePlanMode: boolean = false;\n /** Current Plan Mode toggle state (renders the button in its active state). */\n @Input() PlanModeActive: boolean = false;\n /**\n * Shows the in-composer Skills button when true.\n *\n * Gated separately from `EnableMentions` because a host can have the `/` trigger active and\n * still not want the button — but the common case is that a composer offering skill commands\n * should advertise them, since `/` is otherwise invisible.\n */\n @Input() EnableSkills: boolean = false;\n\n // ── The send slot as a stop control (semantics belong to the host) ──────────────────────\n /** What the send slot is doing; see {@link SendSlotMode}. The box never decides this itself. */\n @Input() SendSlotMode: SendSlotMode = 'send';\n /** How many things a stop would stop. Above 1 in `stop` mode the slot reads \"Stop all\" with this count. */\n @Input() StopCount: number = 0;\n /** Tooltip and accessible name of the slot in `stop` mode, e.g. \"Stop Sage\" or \"Stop all 2 runs\". */\n @Input() StopLabel: string = 'Stop';\n /**\n * A status line rendered bottom-left inside the box while non-null, e.g. \"Sage is working\". It is\n * a polite live region, so a change is announced once; keep it short.\n */\n @Input() StatusText: string | null = null;\n /**\n * An elapsed time shown after {@link StatusText}, e.g. \"41s\". Separate from the text so it is kept\n * out of the live region (a ticking clock must not be announced every second) and dropped on\n * narrow screens.\n */\n @Input() StatusElapsed: string | null = null;\n\n /** True when the slot reads \"Stop all\": `stop` mode with more than one thing to stop. */\n get IsStopAll(): boolean {\n return this.SendSlotMode === 'stop' && this.StopCount > 1;\n }\n\n /** The slot's disabled state per mode: Send follows {@link CanSend}; Stop is always live; Stopping waits. */\n get SendSlotDisabled(): boolean {\n switch (this.SendSlotMode) {\n case 'stop': return false;\n case 'stopping': return true;\n default: return !this.CanSend;\n }\n }\n\n /** The slot's tooltip and accessible name per mode. */\n get SendSlotLabel(): string {\n switch (this.SendSlotMode) {\n case 'stop': return this.StopLabel;\n case 'stopping': return 'Stopping';\n default: return 'Send message (Enter)';\n }\n }\n\n /** The slot's click: a stop request in `stop` mode, a send in `send` mode, nothing while stopping. */\n OnSendSlotClick(): void {\n switch (this.SendSlotMode) {\n case 'stop': this.StopClicked.emit(); return;\n case 'stopping': return;\n default: this.OnSendClick();\n }\n }\n /**\n * True while the skill dropdown is open, driving the button's expanded state.\n *\n * Note the ARIA differs from Plan Mode next to it, deliberately: Plan Mode is a toggle BUTTON\n * (a mode that stays on), so `aria-pressed` is right there. This one opens a popup, so it is\n * `aria-expanded` + `aria-haspopup`. Using `aria-pressed` here would have a screen reader\n * announce \"pressed\" for something that actually just revealed a list.\n *\n * DERIVED, not an @Input, and this is the difference from Plan Mode. Plan Mode's active state is\n * a persisted user preference the host owns and threads down. \"Is the skill dropdown open\" is\n * intrinsic to this composer, so an @Input would be an API the host cannot meaningfully answer\n * and would leave the button permanently unpressed if nobody bound it.\n */\n get SkillsActive(): boolean {\n return this.MentionEditor?.IsTriggerOpen('/') ?? false;\n }\n\n /** Composer lost focus — hosts persist drafts on this. */\n @Output() Blurred = new EventEmitter<void>();\n @Output() TextSubmitted = new EventEmitter<string>();\n @Output() ValueChange = new EventEmitter<string>();\n @Output() AttachmentsChanged = new EventEmitter<PendingAttachment[]>();\n @Output() AttachmentError = new EventEmitter<string>();\n @Output() AttachmentClicked = new EventEmitter<PendingAttachment>();\n /** Emitted when the user clicks the mic button to start/stop a voice session. */\n @Output() VoiceRequested = new EventEmitter<void>();\n /**\n * Emitted when the user clicks the small caret next to the phone button — the host opens the\n * voice agent/model picker so call options (which agent, which voice model) stay reachable\n * without adding friction to the plain phone click's instant-start path.\n */\n @Output() VoiceOptionsRequested = new EventEmitter<void>();\n /** Emitted when the user clicks the in-composer Plan Mode toggle button. */\n @Output() PlanModeToggle = new EventEmitter<void>();\n /**\n * Fired when the send slot is clicked in `stop` mode. The box only reports the click; what is\n * stopped, and how, is the host's business (this widget knows nothing about agents or runs).\n */\n @Output() StopClicked = new EventEmitter<void>();\n /**\n * Fired BEFORE the Skills button opens the dropdown. Flip `Cancel` to veto — a host gating\n * skills by entitlement, or surfacing them elsewhere, blocks it here.\n *\n * A pair rather than a lone emitter because this is an ACTION a host might reasonably refuse\n * (UI_LAYERING_GUIDE section 6, rule 1). Handlers must be synchronous; `Cancel` travels back\n * through EventEmitter's synchronous dispatch.\n */\n @Output() BeforeSkillsOpened = new EventEmitter<BeforeSkillsOpenedEventArgs>();\n /**\n * Fired AFTER the dropdown actually opened. Not emitted on the canceled path, and not emitted\n * when no active provider owns the trigger — so a host counting this counts dropdowns the user\n * saw, rather than clicks.\n */\n @Output() AfterSkillsOpened = new EventEmitter<void>();\n\n /**\n * Open the skill-command dropdown, the same one `/` opens.\n *\n * Routed through the editor's OpenTrigger rather than a parallel menu so permission filtering,\n * per-skill icons and chip insertion all stay in one implementation.\n */\n /**\n * NOTE: the template pairs this with\n * `(mousedown)=\"$event.preventDefault(); $event.stopPropagation()\"`, and BOTH halves are\n * load-bearing.\n *\n * `preventDefault` — a <button> takes focus on mousedown, which blurs the editor, and the editor\n * closes its dropdown 200ms after losing focus. Without it the menu opened and then shut itself.\n *\n * `stopPropagation` — the editor's click-away listener is on `document:mousedown` and exempts only\n * presses inside the EDITOR's host. This button is a sibling of `<mj-mention-editor>`, so a press\n * on it read as an outside press: mousedown closed the dropdown, then click saw `SkillsActive`\n * already false and reopened it. The menu blinked, could not be dismissed by the button that\n * opened it, and the Before/After pair fired on every click — the exact over-counting the toggle\n * branch below exists to prevent. Stopping propagation states that this press belongs to the\n * dropdown machinery.\n */\n OnSkillsClick(event?: Event): void {\n // Toggle closed first. A disclosure button has to be able to close what it opened, and without\n // this a second click re-ran the open path: it re-emitted the Before/After pair (so a host\n // counting opens over-counted) and re-captured the trigger's baseline at the new caret, which\n // corrupts the query offset once anything has been typed.\n if (this.SkillsActive) {\n this.MentionEditor?.closeMentionDropdown();\n return;\n }\n const args = new BeforeSkillsOpenedEventArgs();\n this.BeforeSkillsOpened.emit(args);\n if (args.Cancel) {\n return;\n }\n const anchor = (event?.currentTarget as HTMLElement | undefined) ?? null;\n if (this.MentionEditor?.OpenTrigger('/', anchor)) {\n this.AfterSkillsOpened.emit();\n }\n }\n\n OnRealtimeClick(): void {\n this.VoiceRequested.emit();\n }\n\n OnRealtimeOptionsClick(): void {\n this.VoiceOptionsRequested.emit();\n }\n\n get CanSend(): boolean {\n const hasText = this.Value.trim().length > 0;\n const hasAttachments = this.MentionEditor?.hasAttachments() || false;\n return !this.Disabled && (hasText || hasAttachments);\n }\n\n /**\n * Handle Value changes from MentionEditorComponent\n */\n OnValueChange(newValue: string): void {\n this.Value = newValue;\n this.ValueChange.emit(this.Value);\n }\n\n /**\n * Handle attachment changes from MentionEditorComponent\n */\n OnAttachmentsChanged(attachments: PendingAttachment[]): void {\n this.AttachmentsChanged.emit(attachments);\n }\n\n /**\n * Handle attachment errors from MentionEditorComponent\n */\n OnAttachmentError(error: string): void {\n this.AttachmentError.emit(error);\n }\n\n /**\n * Handle attachment click from MentionEditorComponent\n */\n OnAttachmentClicked(attachment: PendingAttachment): void {\n this.AttachmentClicked.emit(attachment);\n }\n\n /**\n * Handle Enter key from MentionEditorComponent\n * Extracts plain text with JSON-encoded mentions for message submission\n */\n OnEnterPressed(_text: string): void {\n this.OnSendClick();\n }\n\n /**\n * Handle mention selection from MentionEditorComponent\n */\n OnMentionSelected(suggestion: MentionSuggestion): void {\n // MentionEditorComponent already inserts the mention chip\n // This is just for additional tracking/analytics if needed\n }\n\n /**\n * Send the message\n * Extracts plain text with JSON-encoded mentions for proper persistence\n */\n OnSendClick(): void {\n if (this.CanSend) {\n // Get plain text with JSON-encoded mentions (preserves configuration info)\n const textToSend = this.MentionEditor?.getPlainTextWithJsonMentions() || this.Value.trim();\n this.TextSubmitted.emit(textToSend);\n this.Value = ''; // Clear input after sending\n\n // Clear the editor content\n if (this.MentionEditor) {\n this.MentionEditor.clear();\n }\n\n this.ValueChange.emit(this.Value);\n }\n }\n\n /**\n * Handle clicks on the container - focus the mention editor\n * Only moves cursor to end if clicking outside the contentEditable area\n */\n OnContainerClick(event: MouseEvent): void {\n const target = event.target as HTMLElement;\n\n // Don't handle clicks on the send button\n if (target.closest('.send-button-icon')) {\n return;\n }\n\n const editor = this.MentionEditor?.editorRef?.nativeElement;\n if (!editor) return;\n\n // If clicking directly on the editor or its children, let the browser handle cursor placement\n if (target === editor || editor.contains(target)) {\n return;\n }\n\n // Only if clicking on the container (empty space), focus and move cursor to end\n editor.focus();\n const selection = window.getSelection();\n const range = document.createRange();\n\n if (selection) {\n range.selectNodeContents(editor);\n range.collapse(false); // Collapse to end\n selection.removeAllRanges();\n selection.addRange(range);\n }\n }\n\n /**\n * Public method to focus the input programmatically\n */\n /** Inserts a resolved mention chip + space and focuses (see MentionEditorComponent.InsertMention). */\n InsertMention(suggestion: MentionSuggestion, focus: boolean = true): boolean {\n return this.MentionEditor?.InsertMention(suggestion, focus) ?? false;\n }\n\n /** Focus with the caret at the end of content (see MentionEditorComponent.FocusCaretAtEnd). */\n FocusCaretAtEnd(): boolean {\n return this.MentionEditor?.FocusCaretAtEnd() ?? false;\n }\n\n Focus(): void {\n const editor = this.MentionEditor?.editorRef?.nativeElement;\n if (editor) {\n editor.focus();\n }\n }\n\n /** @deprecated Use {@link Focus}. */\n focus(): void {\n return this.Focus();\n }\n\n /**\n * Get mention chip data including configuration presets\n */\n GetMentionChipsData(): Array<{ id: string; type: string; name: string; presetId?: string; presetName?: string }> {\n return this.MentionEditor?.getMentionChipsData() || [];\n }\n\n /**\n * Get pending attachments from the editor\n */\n GetPendingAttachments(): PendingAttachment[] {\n return this.MentionEditor?.getPendingAttachments() || [];\n }\n\n /**\n * Open file picker programmatically\n */\n OpenFilePicker(): void {\n this.MentionEditor?.openFilePicker();\n }\n\n /**\n * Add an artifact as a pending attachment (called by parent after artifact selection)\n */\n AddArtifactAttachment(artifact: {\n fileID: string; fileName: string; mimeType: string;\n sizeBytes: number; artifactVersionId?: string;\n }): PendingAttachment | undefined {\n return this.MentionEditor?.AddArtifactAttachment(artifact);\n }\n}\n","<div class=\"message-input-box-container\" (click)=\"OnContainerClick($event)\">\n <div class=\"input-wrapper\">\n <mj-mention-editor\n #mentionEditor\n [placeholder]=\"Placeholder\"\n [disabled]=\"Disabled\"\n [currentUser]=\"CurrentUser\"\n [enableMentions]=\"EnableMentions\"\n [TriggerProviders]=\"TriggerProviders\"\n [ExcludedTriggerKeys]=\"ExcludedTriggerKeys\"\n [Provider]=\"Provider\"\n [enableAttachments]=\"EnableAttachments\"\n [maxAttachments]=\"MaxAttachments\"\n [maxAttachmentSizeBytes]=\"MaxAttachmentSizeBytes\"\n [acceptedFileTypes]=\"AcceptedFileTypes\"\n [(ngModel)]=\"Value\"\n (valueChange)=\"OnValueChange($event)\"\n (enterPressed)=\"OnEnterPressed($event)\"\n (mentionSelected)=\"OnMentionSelected($event)\"\n (attachmentsChanged)=\"OnAttachmentsChanged($event)\"\n (attachmentError)=\"OnAttachmentError($event)\"\n (attachmentClicked)=\"OnAttachmentClicked($event)\"\n (editorBlurred)=\"Blurred.emit()\">\n </mj-mention-editor>\n\n <!-- Action row: its own strip below the editor (not floating over it), so text never runs behind a button -->\n <div class=\"composer-actions\">\n <!-- Status line, left of the action row: what the host is waiting on (\"Sage is working\"). A polite\n live region; the elapsed time is a pill in an aria-hidden child so its ticking is never announced. -->\n @if (StatusText) {\n <div class=\"send-slot-status\" [class.send-slot-status--stopping]=\"SendSlotMode === 'stopping'\" role=\"status\" aria-live=\"polite\">\n <i class=\"fa-solid fa-circle-notch fa-spin\" aria-hidden=\"true\"></i>\n <span class=\"send-slot-status-text\">{{ StatusText }}</span>\n @if (StatusElapsed) {\n <span class=\"send-slot-status-elapsed\" aria-hidden=\"true\">{{ StatusElapsed }}</span>\n }\n </div>\n }\n\n @if (EnableAttachments || EnableRealtime || EnablePlanMode || EnableSkills) {\n <div class=\"attach-buttons\">\n @if (EnableSkills) {\n <button\n class=\"attach-button-icon skills-button-icon\"\n [class.skills-button-icon--active]=\"SkillsActive\"\n [disabled]=\"Disabled\"\n [attr.aria-expanded]=\"SkillsActive\"\n aria-haspopup=\"true\"\n (mousedown)=\"$event.preventDefault(); $event.stopPropagation()\"\n (click)=\"OnSkillsClick($event); $event.stopPropagation()\"\n title=\"Skills, or type / in the message box\"\n aria-label=\"Show skills\"\n >\n <i class=\"fa-solid fa-wand-magic-sparkles\" aria-hidden=\"true\"></i>\n </button>\n }\n @if (EnablePlanMode) {\n <button\n class=\"attach-button-icon plan-mode-button-icon\"\n [class.plan-mode-button-icon--active]=\"PlanModeActive\"\n [disabled]=\"Disabled\"\n [attr.aria-pressed]=\"PlanModeActive\"\n (click)=\"PlanModeToggle.emit(); $event.stopPropagation()\"\n [title]=\"PlanModeActive ? 'Plan Mode ON — the agent presents a plan for your approval before it acts' : 'Plan Mode: have the agent present a plan for your approval before it acts'\"\n aria-label=\"Toggle Plan Mode\"\n >\n <i class=\"fa-solid fa-route\" aria-hidden=\"true\"></i>\n </button>\n }\n @if (EnableAttachments) {\n <button\n class=\"attach-button-icon\"\n [disabled]=\"Disabled\"\n (click)=\"OpenFilePicker(); $event.stopPropagation()\"\n title=\"Attach file\"\n >\n <i class=\"fa-solid fa-paperclip\"></i>\n </button>\n }\n @if (EnableRealtime) {\n <button\n class=\"attach-button-icon voice-button-icon\"\n [class.voice-button-icon--active]=\"VoiceActive\"\n [disabled]=\"!CanStartRealtime\"\n (click)=\"OnRealtimeClick(); $event.stopPropagation()\"\n [title]=\"VoiceActive ? 'Voice call active' : 'Start a voice call'\"\n aria-label=\"Start a voice call\"\n >\n <i class=\"fa-solid fa-phone\" aria-hidden=\"true\"></i>\n </button>\n <!-- Caret beside the phone: opens the agent/model picker so call OPTIONS stay one\n click away while the plain phone click keeps its instant-start behavior. -->\n <button\n class=\"attach-button-icon voice-options-button-icon\"\n [disabled]=\"!CanStartRealtime\"\n (click)=\"OnRealtimeOptionsClick(); $event.stopPropagation()\"\n title=\"Voice call options (choose agent and voice model)\"\n aria-label=\"Voice call options\"\n >\n <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i>\n </button>\n }\n </div>\n }\n\n <!-- The send slot. ONE button in every mode (send / stop / stopping) so focus is kept across the\n change; see SendSlotMode. -->\n <button\n class=\"send-button-icon\"\n [class.send-button-icon--stop]=\"SendSlotMode === 'stop'\"\n [class.send-button-icon--stop-all]=\"IsStopAll\"\n [class.send-button-icon--stopping]=\"SendSlotMode === 'stopping'\"\n [disabled]=\"SendSlotDisabled\"\n (click)=\"OnSendSlotClick()\"\n [title]=\"SendSlotLabel\"\n [attr.aria-label]=\"SendSlotLabel\"\n [attr.aria-busy]=\"SendSlotMode === 'stopping' ? true : null\"\n >\n @switch (SendSlotMode) {\n @case ('stop') {\n <i class=\"fa-solid fa-stop\" aria-hidden=\"true\"></i>\n @if (IsStopAll) {\n <span class=\"send-slot-label\">Stop all</span>\n <span class=\"send-slot-count\">{{ StopCount }}</span>\n }\n }\n @case ('stopping') {\n <i class=\"fa-solid fa-circle-notch fa-spin\" aria-hidden=\"true\"></i>\n }\n @default {\n <i class=\"fa-solid fa-paper-plane\"></i>\n }\n }\n </button>\n </div>\n </div>\n</div>\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ng-composer",
|
|
3
|
-
"version": "6.2.0-edge.
|
|
3
|
+
"version": "6.2.0-edge.5",
|
|
4
4
|
"description": "MemberJunction: Generic message composer components — mention editor with pluggable trigger providers (ComposerTriggerProvider), mention dropdown, and send-button input box wrapper. Ships zero AI knowledge; trigger plugins are supplied by hosts (see @memberjunction/ng-conversations).",
|
|
5
5
|
"main": "./dist/public-api.js",
|
|
6
6
|
"typings": "./dist/public-api.d.ts",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"devDependencies": {
|
|
14
14
|
"@angular/compiler": "21.2.22",
|
|
15
15
|
"@angular/compiler-cli": "21.2.22",
|
|
16
|
-
"@memberjunction/ng-test-utils": "6.2.0-edge.
|
|
16
|
+
"@memberjunction/ng-test-utils": "6.2.0-edge.5",
|
|
17
17
|
"vitest": "^4.0.18"
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
"@angular/forms": "^21.2.22"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@memberjunction/core": "6.2.0-edge.
|
|
26
|
-
"@memberjunction/global": "6.2.0-edge.
|
|
27
|
-
"@memberjunction/ng-ui-components": "6.2.0-edge.
|
|
25
|
+
"@memberjunction/core": "6.2.0-edge.5",
|
|
26
|
+
"@memberjunction/global": "6.2.0-edge.5",
|
|
27
|
+
"@memberjunction/ng-ui-components": "6.2.0-edge.5",
|
|
28
28
|
"tslib": "^2.8.1"
|
|
29
29
|
},
|
|
30
30
|
"sideEffects": false,
|