diff --git a/test/core/workflow/workflow.firefly.test.js b/test/core/workflow/workflow.firefly.test.js
index 3f0ddd66..d86a0ea1 100644
--- a/test/core/workflow/workflow.firefly.test.js
+++ b/test/core/workflow/workflow.firefly.test.js
@@ -26,7 +26,13 @@ describe('Firefly Workflow Tests', () => {
unityElement = document.querySelector('.unity');
workflowCfg = {
name: 'workflow-firefly',
- targetCfg: { renderWidget: true, insert: 'before', target: 'a:last-of-type' },
+ targetCfg: {
+ renderWidget: true,
+ insert: 'before',
+ target: 'a:last-of-type',
+ limits: { 'max-char-limit': 750 },
+ 'limits-prompt-bar-audio': { 'max-char-limit': 5000 },
+ },
};
spriteContainer = '';
block = document.querySelector('.unity-enabled');
@@ -2279,9 +2285,7 @@ describe('Firefly Workflow Tests', () => {
});
it('should pass empty object to logAnalytics when splunkData is omitted', async () => {
- await testActionBinder.sendFireflyAnalytics(new CustomEvent('firefly-analytics', {
- detail: { adobeEventName: 'Enter Prompt|UnityWidget' },
- }));
+ await testActionBinder.sendFireflyAnalytics(new CustomEvent('firefly-analytics', { detail: { adobeEventName: 'Enter Prompt|UnityWidget' } }));
expect(logStub.calledOnceWithExactly('Enter Prompt|UnityWidget', {})).to.be.true;
});
});
@@ -2758,7 +2762,7 @@ describe('Firefly Workflow Tests', () => {
{ label: 'Var A', url: 'https://u1' },
{ label: 'Var B', url: 'https://u2' },
{ label: 'Var C', url: 'https://u3' },
- { label: 'Var D', url: 'https://u4' }
+ { label: 'Var D', url: 'https://u4' },
]);
expect(pm.image).to.exist;
expect(pm.image[0].variations).to.deep.equal([]);
diff --git a/unitylibs/core/widgets/prompt-bar-audio/prompt-bar-audio.css b/unitylibs/core/widgets/prompt-bar-audio/prompt-bar-audio.css
new file mode 100644
index 00000000..8bbae74d
--- /dev/null
+++ b/unitylibs/core/widgets/prompt-bar-audio/prompt-bar-audio.css
@@ -0,0 +1,826 @@
+.unity-slf-sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border: 0;
+}
+
+.unity.workflow-firefly.widget-prompt-bar-audio.unity-prompt-bar-audio-host {
+ position: relative;
+ min-height: 0;
+}
+
+.unity-prompt-bar-audio {
+ width: 100%;
+ box-sizing: border-box;
+}
+
+.unity-prompt-bar-audio .unity-paf-main {
+ display: flex;
+ flex-direction: column;
+ gap: 0;
+ align-items: stretch;
+ flex: 1;
+ min-width: 0;
+}
+
+.unity-prompt-bar-audio .unity-slf-left {
+ display: flex;
+ flex-direction: column;
+ gap: 0;
+ min-width: 0;
+ flex: 1;
+ background: #1b1b1b;
+ border-radius: 10px;
+ padding: 16px;
+ box-sizing: border-box;
+}
+
+.unity-prompt-bar-audio .unity-slf-controls {
+ display: flex;
+ flex-direction: column;
+ gap: 0;
+ min-width: 0;
+}
+
+.unity-prompt-bar-audio .unity-paf-voice-section {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ min-width: 0;
+}
+
+.unity-prompt-bar-audio .unity-paf-voice-heading {
+ margin: 18px 0 0;
+ padding-top: 16px;
+ border-top: 2px solid rgb(255 255 255 / 12%);
+}
+
+.unity-prompt-bar-audio .unity-paf-voice-row {
+ display: flex;
+ flex-flow: row nowrap;
+ gap: 4.76px;
+ align-items: stretch;
+ justify-content: flex-start;
+ width: 100%;
+ min-width: 0;
+ overflow-x: auto;
+ overflow-y: hidden;
+ -webkit-overflow-scrolling: touch;
+ overscroll-behavior-x: contain;
+ padding-bottom: 2px;
+}
+
+.unity-prompt-bar-audio .unity-paf-voice-row.unity-paf-voice-row-peek {
+ box-sizing: border-box;
+ width: calc(100% + 16px);
+ padding-right: 16px;
+ scroll-padding-right: 16px;
+}
+
+.unity-prompt-bar-audio .unity-paf-voice-row.unity-paf-voice-row-peek.unity-paf-voice-row-peek-scrolled {
+ width: calc(100% + 32px);
+ transform: translateX(-16px);
+ padding-left: 16px;
+ padding-right: 16px;
+ scroll-padding-left: 16px;
+ scroll-padding-right: 16px;
+}
+
+.unity-prompt-bar-audio .unity-paf-voice-tile {
+ --unity-paf-voice-tile-width: 231px;
+ --paf-voice-tile-border-width: 1.78px;
+
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ gap: 5px;
+ box-sizing: border-box;
+ width: var(--unity-paf-voice-tile-width);
+ min-width: var(--unity-paf-voice-tile-width);
+ max-width: var(--unity-paf-voice-tile-width);
+ flex: 0 0 var(--unity-paf-voice-tile-width);
+ padding: 14px;
+ border-radius: 9.51px;
+ border: var(--paf-voice-tile-border-width) solid transparent;
+ background: #292929;
+ backdrop-filter: blur(29.72px);
+ overflow: hidden;
+ cursor: pointer;
+ text-align: start;
+ transition: background 0.15s ease, border-color 0.15s ease;
+}
+
+.unity-prompt-bar-audio .unity-paf-voice-tile:hover {
+ background: #333;
+}
+
+.unity-prompt-bar-audio .unity-paf-voice-tile:focus {
+ outline: none;
+}
+
+.unity-prompt-bar-audio .unity-paf-voice-tile:focus-visible {
+ border-color: #274dea;
+}
+
+.unity-prompt-bar-audio .unity-paf-voice-tile.selected {
+ border-color: #274dea;
+}
+
+.unity-prompt-bar-audio .unity-paf-voice-tile.selected:hover {
+ background: #2f2f2f;
+}
+
+.unity-prompt-bar-audio .unity-paf-voice-tile-text {
+ display: flex;
+ flex-direction: column;
+ gap: 5px;
+ min-width: 0;
+ flex: 1 1 auto;
+}
+
+.unity-prompt-bar-audio .unity-paf-voice-name {
+ font-family: "Adobe Clean", adobe-clean, "Adobe Clean Serif", sans-serif;
+ font-size: 16px;
+ font-weight: 800;
+ line-height: 0.98;
+ letter-spacing: -0.48px;
+ color: #fff;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.unity-prompt-bar-audio .unity-paf-voice-desc {
+ font-family: "Adobe Clean", adobe-clean, "Adobe Clean Serif", sans-serif;
+ font-size: 14px;
+ font-weight: 400;
+ line-height: 1.2;
+ letter-spacing: 0.14px;
+ color: rgb(255 255 255 / 92%);
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.unity-prompt-bar-audio .unity-paf-voice-player {
+ position: relative;
+ width: 37.17px;
+ height: 37.17px;
+ flex: 0 0 37.17px;
+}
+
+@media (hover: hover) and (pointer: fine) {
+ .unity-prompt-bar-audio .unity-paf-voice-tile .unity-paf-voice-player {
+ opacity: 0;
+ transition: opacity 0.15s ease;
+ }
+
+ .unity-prompt-bar-audio .unity-paf-voice-tile:hover .unity-paf-voice-player,
+ .unity-prompt-bar-audio .unity-paf-voice-tile:focus-visible .unity-paf-voice-player,
+ .unity-prompt-bar-audio .unity-paf-voice-tile[aria-pressed="true"] .unity-paf-voice-player,
+ .unity-prompt-bar-audio .unity-paf-voice-tile.selected .unity-paf-voice-player,
+ .unity-prompt-bar-audio .unity-paf-voice-tile:has(.unity-paf-voice-player--buffering) .unity-paf-voice-player {
+ opacity: 1;
+ }
+}
+
+.unity-prompt-bar-audio .unity-paf-progress-svg {
+ display: block;
+ width: 37.17px;
+ height: 37.17px;
+}
+
+.unity-prompt-bar-audio .unity-paf-ring-bg {
+ stroke: rgb(255 255 255 / 22%);
+}
+
+.unity-prompt-bar-audio .unity-paf-ring-fg {
+ stroke: #f8f8f8;
+ transition: stroke-dashoffset 0.05s linear;
+}
+
+.unity-prompt-bar-audio .unity-paf-pp-center {
+ position: absolute;
+ inset: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ pointer-events: none;
+}
+
+.unity-prompt-bar-audio .unity-paf-pp-center .unity-paf-pp-svg {
+ display: block;
+ width: 18px;
+ height: 18px;
+}
+
+.unity-prompt-bar-audio .unity-paf-pp-center .unity-paf-pp-svg use {
+ filter: brightness(0) invert(1);
+}
+
+.unity-prompt-bar-audio .unity-paf-voice-player-loading {
+ box-sizing: border-box;
+ display: flex;
+ width: 100%;
+ height: 100%;
+ align-items: center;
+ justify-content: center;
+ color: #f8f8f8;
+ pointer-events: none;
+}
+
+.unity-prompt-bar-audio .unity-paf-voice-player-loading-svg {
+ display: block;
+ width: 100%;
+ height: 100%;
+}
+
+.unity-prompt-bar-audio .unity-paf-voice-player-loading-circle {
+ stroke-dasharray: 125.664;
+ stroke-dashoffset: 125.664;
+ animation: unity-paf-voice-player-loading-sweep 1s ease-in-out infinite;
+}
+
+@keyframes unity-paf-voice-player-loading-sweep {
+ to {
+ stroke-dashoffset: 0;
+ }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .unity-prompt-bar-audio .unity-paf-voice-player-loading-circle {
+ animation: none;
+ stroke-dashoffset: 62.8;
+ }
+}
+
+.unity-prompt-bar-audio .unity-paf-voice-subfoot {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 4px;
+ margin: 4px 0 0;
+ text-align: center;
+ font-family: "Adobe Clean", adobe-clean, "Adobe Clean Serif", sans-serif;
+ font-size: 12px;
+ line-height: 16px;
+ color: #fff;
+}
+
+.unity-prompt-bar-audio .unity-paf-voice-subfoot-line {
+ margin: 0;
+ max-width: 100%;
+}
+
+.unity-prompt-bar-audio .unity-paf-terms-banner {
+ box-sizing: border-box;
+ width: 100%;
+ flex-shrink: 0;
+ margin: 24px 0 0;
+ padding: 0 8px;
+ text-align: center;
+ font-family: "Adobe Clean", adobe-clean, "Adobe Clean Serif", sans-serif;
+ font-size: 12px;
+ line-height: 18px;
+ color: #fff;
+}
+
+.unity-prompt-bar-audio:has(.interactive-area.light) .unity-paf-terms-banner {
+ color: rgb(46 46 46 / 92%);
+}
+
+.unity-prompt-bar-audio .unity-paf-terms-banner-line {
+ margin: 0 auto;
+ max-width: 56rem;
+}
+
+.unity-prompt-bar-audio .unity-paf-voice-subfoot-line a,
+.unity-prompt-bar-audio .unity-paf-voice-footer-link {
+ color: inherit;
+ text-decoration: underline;
+ text-underline-offset: 2px;
+}
+
+.unity-prompt-bar-audio .unity-paf-terms-banner-line a {
+ color: inherit;
+ text-decoration: underline;
+ text-underline-offset: 2px;
+}
+
+.unity-prompt-bar-audio .unity-paf-terms-banner-line a:hover {
+ color: inherit;
+}
+
+.unity-prompt-bar-audio .unity-paf-terms-banner-line a:focus-visible {
+ outline: 2px solid currentColor;
+ outline-offset: 2px;
+ border-radius: 2px;
+}
+
+.unity-prompt-bar-audio .unity-paf-voice-subfoot-line a:hover {
+ color: #fff;
+}
+
+.unity-prompt-bar-audio .unity-paf-voice-footer {
+ margin: 4px 0 0;
+ text-align: center;
+ font-family: "Adobe Clean", adobe-clean, "Adobe Clean Serif", sans-serif;
+ font-size: 12px;
+ line-height: 16px;
+ color: #fff;
+}
+
+.unity-prompt-bar-audio .unity-paf-voice-footer-link:hover {
+ color: #fff;
+}
+
+.unity-prompt-bar-audio .unity-slf-sprite {
+ position: absolute;
+ width: 0;
+ height: 0;
+ overflow: hidden;
+ pointer-events: none;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .ex-unity-wrap .ex-unity-widget .inp-wrap .inp-field {
+ border: none;
+ width: 100%;
+ box-sizing: border-box;
+ font-family: inherit;
+ outline: none;
+ background: transparent;
+ resize: none;
+ margin: 0;
+ scrollbar-width: thin;
+ scrollbar-color: #888 transparent;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .ex-unity-wrap .ex-unity-widget .inp-wrap .inp-field::placeholder {
+ color: #7d7d7d;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .ex-unity-wrap.verb-options .models-container,
+.unity-prompt-bar-audio.unity-enabled .interactive-area .ex-unity-wrap.verb-options .verbs-container,
+.unity-prompt-bar-audio.unity-enabled .interactive-area .ex-unity-wrap.verb-options .action-container {
+ position: relative;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .selected-model,
+.unity-prompt-bar-audio.unity-enabled .interactive-area .selected-verb {
+ display: flex;
+ align-items: center;
+ gap: 7px;
+ justify-content: flex-start;
+ padding: 7px 12px;
+ cursor: pointer;
+ border: none;
+ font-size: 14px;
+ font-family: inherit;
+ font-weight: 400;
+ width: 100%;
+ min-width: unset;
+ background: #2c2c2c;
+ border-radius: 8px;
+ color: #fff;
+ height: 32px;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .selected-model {
+ padding-left: 4px;
+ padding-right: 11px;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .selected-model .model-name {
+ flex: 0 1 auto;
+ min-width: min-content;
+ overflow: visible;
+ text-overflow: clip;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .selected-model img,
+.unity-prompt-bar-audio.unity-enabled .interactive-area .verb-list .verb-link img {
+ width: 20px;
+ height: 20px;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .selected-verb .menu-icon,
+.unity-prompt-bar-audio.unity-enabled .interactive-area .selected-model .menu-icon {
+ font-size: 0;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .selected-verb .menu-icon,
+.unity-prompt-bar-audio.unity-enabled .interactive-area .selected-verb .menu-icon svg,
+.unity-prompt-bar-audio.unity-enabled .interactive-area .selected-model .menu-icon,
+.unity-prompt-bar-audio.unity-enabled .interactive-area .selected-model .menu-icon svg {
+ width: 12px;
+ height: 12px;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .selected-model .menu-icon svg,
+.unity-prompt-bar-audio.unity-enabled .interactive-area .selected-verb .menu-icon svg {
+ filter: invert(1);
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .verb-list .verb-link .selected-icon {
+ font-size: 0;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .verb-list .verb-link .selected-icon,
+.unity-prompt-bar-audio.unity-enabled .interactive-area .verb-list .verb-link .selected-icon svg {
+ width: 12px;
+ height: 12px;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .verbs-container .menu-icon,
+.unity-prompt-bar-audio.unity-enabled .interactive-area .models-container .menu-icon {
+ position: relative;
+ top: 1px;
+ flex-shrink: 0;
+ transition: transform 0.15s ease-in;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .verbs-container.show-menu .menu-icon,
+.unity-prompt-bar-audio.unity-enabled .interactive-area .models-container.show-menu .menu-icon {
+ transform: rotate(-180deg);
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .verbs-container .verb-list .verb-link:not(.model-link) img {
+ filter: invert(1);
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .ex-unity-wrap .models-container .model-name {
+ color: #fff;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .ex-unity-wrap.verb-options .models-container .verb-list,
+.unity-prompt-bar-audio.unity-enabled .interactive-area .ex-unity-wrap.verb-options .verbs-container .verb-list {
+ background: #000;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .verb-list {
+ padding: 18px;
+ list-style: none;
+ box-shadow: 0 0 10px #0000001c;
+ border-radius: 10px;
+ color: #fff;
+ margin: 0;
+ animation: none;
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 5;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .models-container.show-menu .verb-list,
+.unity-prompt-bar-audio.unity-enabled .interactive-area .verbs-container.show-menu .verb-list {
+ display: block;
+ animation: none;
+ transform: none;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .models-container .verb-list {
+ padding: 10px 12px;
+ min-height: 0;
+ box-sizing: border-box;
+ min-width: 100%;
+ width: max-content;
+ max-width: 100vw;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .verb-list .verb-item {
+ margin: 0;
+ padding: 0;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .verb-list .verb-link {
+ color: inherit;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 10px;
+ padding-inline-start: 25px;
+ text-transform: capitalize;
+ text-decoration: none;
+ text-align: start;
+ position: relative;
+ opacity: 1;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .verb-list .verb-link.model-link {
+ padding: 8px 12px 8px 30px;
+ gap: 10px;
+ line-height: 1.25;
+ font-size: 14px;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .verb-list .verb-link.model-link img {
+ object-fit: contain;
+ flex-shrink: 0;
+ align-self: center;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .verb-list .verb-link.model-link .model-name {
+ align-self: center;
+ line-height: 20px;
+ white-space: nowrap;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .verb-list .verb-item .selected-icon {
+ display: none;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .verb-list .verb-item.selected .verb-link .selected-icon {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: absolute;
+ left: 8px;
+ top: 0;
+ bottom: 0;
+ margin-block: auto;
+ width: 14px;
+ height: 14px;
+ transform: none;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .verb-list .verb-item.selected .verb-link .selected-icon svg {
+ display: block;
+}
+
+[dir="rtl"] .unity-prompt-bar-audio.unity-enabled .interactive-area .verb-list .verb-item.selected .verb-link .selected-icon {
+ left: auto;
+ right: 8px;
+}
+
+.unity-prompt-bar-audio .unity-slf-copy-label {
+ color: #d1d1d1;
+ font-family: "Adobe Clean", adobe-clean, "Adobe Clean Serif", sans-serif;
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: 18px;
+ letter-spacing: 0;
+}
+
+.unity-prompt-bar-audio .unity-slf-prompt-label {
+ display: block;
+ margin-bottom: 8px;
+}
+
+.unity-prompt-bar-audio.unity-enabled > .interactive-area.dark .unity-slf-prompt-label {
+ margin-bottom: 0;
+}
+
+.unity-prompt-bar-audio .inp-field {
+ min-height: 100px;
+ resize: vertical;
+}
+
+.unity-prompt-bar-audio .unity-slf-gen-btn {
+ white-space: nowrap;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .ex-unity-wrap .ex-unity-widget .act-wrap {
+ display: flex;
+ width: fit-content;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .ex-unity-wrap .ex-unity-widget .act-wrap .unity-act-btn {
+ text-decoration: none;
+ display: flex;
+ align-items: center;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .ex-unity-wrap .ex-unity-widget .act-wrap .unity-act-btn:focus {
+ outline: 2px solid #005fcc;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .ex-unity-wrap .ex-unity-widget .act-wrap .unity-act-btn .btn-ico {
+ display: flex;
+ align-content: center;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .ex-unity-wrap .ex-unity-widget .act-wrap .unity-act-btn .btn-ico img {
+ width: 22px;
+ height: 22px;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .ex-unity-wrap .ex-unity-widget .act-wrap .unity-act-btn .btn-txt {
+ display: flex;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .ex-unity-wrap .ex-unity-widget .act-wrap .gen-btn {
+ flex-shrink: 0;
+ border-radius: 25px;
+ background: linear-gradient(90deg, #d73220 0%, #d92361 33%, #7155fa 100%);
+ border: none;
+ padding: 10px 20px 10px 18px;
+ gap: 8px;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .ex-unity-wrap .ex-unity-widget .act-wrap .gen-btn .btn-txt {
+ color: var(--color-white);
+ font-size: 16px;
+ font-weight: 700;
+ line-height: normal;
+}
+
+.unity-prompt-bar-audio.unity-enabled {
+ margin-inline: auto;
+ padding-bottom: 76px;
+}
+
+.unity-prompt-bar-audio.unity-enabled > .interactive-area {
+ display: flex;
+ box-sizing: border-box;
+ padding: 14px;
+ border-radius: 20px;
+ gap: 8px;
+ isolation: isolate;
+ overflow-x: clip;
+}
+
+.unity-prompt-bar-audio.unity-enabled > .interactive-area.dark {
+ background: rgb(24 24 24 / 48%);
+ backdrop-filter: blur(32px) saturate(165%);
+ -webkit-backdrop-filter: blur(32px) saturate(165%);
+ border: 1px solid rgb(255 255 255 / 14%);
+ box-shadow:
+ inset 0 1px 0 rgb(255 255 255 / 12%),
+ 0 16px 48px rgb(0 0 0 / 28%);
+}
+
+.unity-prompt-bar-audio.unity-enabled > .interactive-area.light {
+ background: rgb(255 255 255 / 52%);
+ backdrop-filter: blur(32px) saturate(165%);
+ -webkit-backdrop-filter: blur(32px) saturate(165%);
+ border: 1px solid rgb(0 0 0 / 8%);
+ box-shadow:
+ inset 0 1px 0 rgb(255 255 255 / 85%),
+ 0 16px 48px rgb(0 0 0 / 10%);
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area.dark .unity-slf-left {
+ background: rgb(255 255 255 / 5%);
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area.light .unity-slf-left {
+ background: rgb(0 0 0 / 4%);
+}
+
+@media (prefers-reduced-transparency: reduce) {
+ .unity-prompt-bar-audio.unity-enabled > .interactive-area.dark {
+ backdrop-filter: none;
+ -webkit-backdrop-filter: none;
+ background: #222;
+ border: 1px solid rgb(255 255 255 / 10%);
+ box-shadow: none;
+ }
+
+ .unity-prompt-bar-audio.unity-enabled > .interactive-area.light {
+ backdrop-filter: none;
+ -webkit-backdrop-filter: none;
+ background: #eaeaea;
+ border: 1px solid rgb(0 0 0 / 10%);
+ box-shadow: none;
+ }
+
+ .unity-prompt-bar-audio.unity-enabled .interactive-area.dark .unity-slf-left {
+ background: #1b1b1b;
+ }
+
+ .unity-prompt-bar-audio.unity-enabled .interactive-area.light .unity-slf-left {
+ background: #fff;
+ }
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .ex-unity-wrap .ex-unity-widget .autocomplete {
+ background: transparent !important;
+ border: none !important;
+ border-radius: 0 !important;
+ width: 100% !important;
+ max-width: 100%;
+ margin: 0 !important;
+ box-sizing: border-box;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .ex-unity-wrap .ex-unity-widget .autocomplete::after {
+ content: none !important;
+ display: none !important;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .ex-unity-wrap:not(.sticky) .ex-unity-widget {
+ padding-left: 0;
+ padding-right: 0;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .ex-unity-wrap .ex-unity-widget .inp-wrap {
+ display: grid;
+ grid-template-columns: 1fr auto;
+ column-gap: 8px;
+ align-items: center;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .ex-unity-wrap .ex-unity-widget .inp-wrap > .unity-slf-copy-label,
+.unity-prompt-bar-audio.unity-enabled .interactive-area .ex-unity-wrap .ex-unity-widget .inp-wrap > label {
+ grid-column: 1 / -1;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .ex-unity-wrap .ex-unity-widget .inp-wrap > .inp-field {
+ grid-column: 1 / -1;
+ color: #f8f8f8;
+ padding: 10px 0;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .ex-unity-wrap .ex-unity-widget .inp-wrap > .action-container {
+ grid-column: 1;
+ margin-top: 32px;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ justify-content: flex-start;
+ min-width: 0;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .ex-unity-wrap .ex-unity-widget .inp-wrap > .act-wrap {
+ grid-column: 2;
+ margin-top: 24px;
+ display: flex;
+ justify-content: flex-end;
+ align-items: center;
+ align-self: center;
+ min-width: max-content;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .ex-unity-wrap .ex-unity-widget .inp-wrap > .action-container:empty {
+ display: none;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .ex-unity-wrap .ex-unity-widget .inp-wrap > .action-container:empty + .act-wrap {
+ grid-column: 1 / -1;
+ justify-self: end;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .ex-unity-wrap .ex-unity-widget .inp-wrap > .action-container > .models-container,
+.unity-prompt-bar-audio.unity-enabled .interactive-area .ex-unity-wrap .ex-unity-widget .inp-wrap > .action-container > .verbs-container {
+ display: flex;
+ justify-content: flex-start;
+ align-items: center;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .ex-unity-wrap .ex-unity-widget .inp-wrap > .action-container > .models-container {
+ min-width: 0;
+ max-width: 100%;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .models-container {
+ display: flex;
+ width: 100%;
+ max-width: 100%;
+ position: relative;
+}
+
+.unity-prompt-bar-audio.unity-enabled .interactive-area .ex-unity-wrap.verb-options .models-container,
+.unity-prompt-bar-audio.unity-enabled .interactive-area .ex-unity-wrap.verb-options .verbs-container {
+ width: fit-content;
+}
+
+@media screen and (max-width: 1199px) {
+ .unity-prompt-bar-audio.unity-enabled {
+ width: var(--grid-container-width);
+ }
+
+ .unity-prompt-bar-audio .unity-paf-voice-row {
+ gap: 10px;
+ }
+
+ .unity-prompt-bar-audio .unity-paf-voice-tile {
+ --unity-paf-voice-tile-width: 201px;
+ }
+}
+
+@media screen and (min-width: 1200px) {
+ .unity-prompt-bar-audio.unity-enabled {
+ max-width: 1000px;
+ }
+
+ .hero-marquee:has(+ .unity-prompt-bar-audio) > .foreground {
+ max-width: 1000px;
+ min-width: unset;
+ }
+
+ .hero-marquee:has(+ .unity-prompt-bar-audio) .foreground .main-copy {
+ align-items: center;
+ }
+
+ .hero-marquee:has(+ .unity-prompt-bar-audio) .foreground .main-copy p {
+ max-width: 800px;
+ }
+}
diff --git a/unitylibs/core/widgets/prompt-bar-audio/prompt-bar-audio.js b/unitylibs/core/widgets/prompt-bar-audio/prompt-bar-audio.js
new file mode 100644
index 00000000..0f09c7ab
--- /dev/null
+++ b/unitylibs/core/widgets/prompt-bar-audio/prompt-bar-audio.js
@@ -0,0 +1,1119 @@
+/* eslint-disable class-methods-use-this */
+/* eslint-disable max-classes-per-file */
+
+import { createTag, getConfig, getUnityPromptConfigsBaseUrl } from '../../../scripts/utils.js';
+
+let promptWithStyleEvents = null;
+
+function buildVoiceModelIndex(voices) {
+ const indexMap = new Map();
+ voices.forEach((voice) => {
+ const modelId = `${voice?.modelId ?? ''}`.trim().toLowerCase();
+ if (!indexMap.has(modelId)) indexMap.set(modelId, []);
+ indexMap.get(modelId).push(voice);
+ });
+ return indexMap;
+}
+
+function filterVoicesByModelId(voices, voiceModelIndex, selectedModelId) {
+ const id = (selectedModelId || '').trim().toLowerCase();
+ if (!id) return voices;
+ const shared = voiceModelIndex?.get('') || [];
+ const specific = voiceModelIndex?.get(id) || [];
+ return [...shared, ...specific];
+}
+
+async function loadVoicesFromCurrentPageJson(sourceUrl) {
+ const finalUrl = sourceUrl?.trim();
+ if (!finalUrl) return [];
+ const res = await fetch(finalUrl);
+ if (!res.ok) {
+ throw new Error(`Current page config fetch failed: ${res.status}`);
+ }
+ const json = await res.json();
+ const data = json?.content?.data;
+ let rows = [];
+ if (Array.isArray(data)) {
+ rows = data;
+ } else if (data && typeof data === 'object' && Array.isArray(data.voices)) {
+ rows = data.voices;
+ }
+ if (!rows.length) return [];
+ return rows
+ .filter((row) => row && typeof row === 'object')
+ .map((row) => ({
+ name: `${row.Name ?? ''}`.trim(),
+ description: `${row.Description ?? ''}`.trim(),
+ url: `${row.url ?? ''}`.trim(),
+ voiceId: `${row.VoiceId ?? ''}`.trim(),
+ modelId: `${row.ModelId ?? ''}`.trim(),
+ }));
+}
+
+function resolveCurrentPageVariationFileUrl(root) {
+ const el = root?.querySelector?.('[class*="icon-operation-"]');
+ const token = el && [...el.classList].find((c) => c.startsWith('icon-operation-'));
+ const rawSuffix = token ? token.slice('icon-operation-'.length) : '';
+ const normalized = rawSuffix.trim().replace(/\s+/g, '-');
+ const base = normalized.replace(/[^a-zA-Z0-9._-]/g, '');
+ const fileBase = base ? base.toLowerCase() : null;
+ if (!fileBase) return null;
+ const baseUrl = getUnityPromptConfigsBaseUrl();
+ const { locale } = getConfig();
+ return locale.prefix && locale.prefix !== '/'
+ ? `${baseUrl}${locale.prefix}/unity/configs/prompt/${fileBase}.json`
+ : `${baseUrl}/unity/configs/prompt/${fileBase}.json`;
+}
+
+class UnityWidget {
+ constructor(target, el, workflowCfg, spriteCon) {
+ this.el = el;
+ this.target = target;
+ this.workflowCfg = workflowCfg;
+ this.widget = null;
+ this.actionMap = {};
+ this.spriteCon = spriteCon;
+ this.prompts = null;
+ this.models = null;
+ this.selectedVerbType = '';
+ this.selectedVerbText = '';
+ this.selectedModelModule = '';
+ this.selectedModelId = '';
+ this.selectedModelText = '';
+ this.selectedModelVersion = '';
+ this.selectedModelName = '';
+ this.promptItems = [];
+ this.genBtn = null;
+ this.hasPromptSuggestions = false;
+ this.hasModelOptions = false;
+ this.voices = null;
+ this.voiceConfigAll = null;
+ this.voiceModelIndex = null;
+ this.lanaOptions = { sampleRate: 100, tags: 'Unity-FF' };
+ this.sound = { audio: null, currentTile: null, currentUrl: '' };
+ this.durationCache = new Map();
+ }
+
+ verbDropdown() {
+ const verb = this.el.querySelector('[class*="icon-verb"]');
+ const selectedVerb = verb?.nextElementSibling;
+ this.selectedVerbType = verb?.className.split('-')[2];
+ this.selectedVerbText = selectedVerb?.textContent.trim() ?? '';
+ this.widgetWrap.setAttribute('data-selected-verb', this.selectedVerbType);
+ }
+
+ closeVerbOrModelMenu(selectedElement) {
+ const menuContainer = selectedElement?.parentElement;
+ if (!menuContainer) return;
+ menuContainer.classList.remove('show-menu');
+ selectedElement.setAttribute('aria-expanded', 'false');
+ const list = selectedElement.nextElementSibling;
+ if (list?.classList?.contains('verb-list')) {
+ list.setAttribute('style', 'display: none;');
+ }
+ }
+
+ showVerbMenu(selectedElement) {
+ const menuContainer = selectedElement.parentElement;
+ document.querySelectorAll('.models-container').forEach((container) => {
+ if (container !== menuContainer) {
+ const sm = container.querySelector('.selected-model');
+ if (sm) this.closeVerbOrModelMenu(sm);
+ }
+ });
+ menuContainer.classList.toggle('show-menu');
+ const isOpen = menuContainer.classList.contains('show-menu');
+ selectedElement.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
+ const siblingList = selectedElement.nextElementSibling;
+ if (siblingList?.classList?.contains('verb-list')) {
+ if (isOpen) {
+ siblingList.removeAttribute('style');
+ } else {
+ siblingList.setAttribute('style', 'display: none;');
+ }
+ }
+ }
+
+ showVerbOrModelMenuAndTrackOpen(selectedElement, adobeEventName) {
+ const menuContainer = selectedElement.parentElement;
+ const wasOpen = menuContainer.classList.contains('show-menu');
+ this.hidePromptDropdown(selectedElement);
+ this.showVerbMenu(selectedElement);
+ if (!wasOpen) {
+ this.widgetWrap.dispatchEvent(new CustomEvent('firefly-analytics', {
+ detail: {
+ adobeEventName,
+ splunkData: { action: 'open' },
+ },
+ }));
+ }
+ }
+
+ hidePromptDropdown(exceptElement = null) {
+ const dropdown = this.widget.querySelector('.prompt-dropdown-container');
+ if (dropdown && !dropdown.classList.contains('hidden')) {
+ dropdown.classList.add('hidden');
+ dropdown.setAttribute('inert', '');
+ dropdown.setAttribute('aria-hidden', 'true');
+ }
+ if (this.selectedVerbType === 'sound') {
+ this.resetAllSoundVariations?.(dropdown);
+ }
+ const modelDropdown = this.widget.querySelector('.models-container');
+ const modelButton = modelDropdown?.querySelector('.selected-model');
+ if (modelDropdown && modelDropdown.classList.contains('show-menu') && modelButton && modelButton !== exceptElement) {
+ this.closeVerbOrModelMenu(modelButton);
+ }
+ }
+
+ updateAnalytics(verb) {
+ if (this.promptItems && this.promptItems.length > 0) {
+ this.promptItems.forEach((item) => {
+ const ariaLabel = item.getAttribute('aria-label') || '';
+ item.setAttribute('daa-ll', `${ariaLabel.slice(0, 20)}--${verb}--Prompt suggestion`);
+ });
+ }
+ }
+
+ clearSelectedModelState() {
+ this.selectedModelId = '';
+ this.selectedModelName = '';
+ this.selectedModelVersion = '';
+ this.selectedModelModule = '';
+ this.selectedModelText = '';
+ this.widgetWrap?.removeAttribute('data-selected-model-name');
+ }
+
+ handleModelLinkClick(link, listContainer, selectedElement, menuIcon) {
+ return (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ const verbLinkTexts = [];
+ listContainer.querySelectorAll('.verb-link').forEach((listLink) => {
+ listLink.parentElement.classList.remove('selected');
+ listLink.setAttribute('aria-selected', 'false');
+ const text = listLink.textContent.trim();
+ if (text) verbLinkTexts.push(text);
+ });
+ verbLinkTexts.sort((a, b) => b.length - a.length);
+ this.closeVerbOrModelMenu(selectedElement);
+ link.parentElement.classList.add('selected');
+ link.setAttribute('aria-selected', 'true');
+ this.selectedModelId = link.getAttribute('data-model-id');
+ this.selectedModelName = link.textContent.trim();
+ this.selectedModelVersion = link.getAttribute('data-model-version');
+ this.selectedModelModule = link.getAttribute('data-model-module');
+ this.selectedModelText = link.textContent.trim();
+ const copiedNodes = link.cloneNode(true).childNodes;
+ copiedNodes[0].remove();
+ selectedElement.replaceChildren(...copiedNodes, menuIcon);
+ selectedElement.dataset.selectedModelId = this.selectedModelId;
+ selectedElement.dataset.selectedModelVersion = this.selectedModelVersion;
+ selectedElement.focus();
+ const verbsWithoutPromptSuggestions = this.workflowCfg.targetCfg?.verbsWithoutPromptSuggestions ?? [];
+ if (verbsWithoutPromptSuggestions.includes(this.selectedVerbType)) {
+ this.widgetWrap.dispatchEvent(new CustomEvent('firefly-reinit-action-listeners'));
+ }
+ if (link.getAttribute('data-model-module') !== this.selectedVerbType) {
+ const oldModelContainer = this.widget.querySelector('.models-container');
+ const modelDropdown = this.modelDropdown();
+ if (oldModelContainer) {
+ if (modelDropdown.length > 1) {
+ const newModelContainer = createTag('div', { class: 'models-container', 'aria-label': 'Model options' });
+ newModelContainer.append(...modelDropdown);
+ oldModelContainer.replaceWith(newModelContainer);
+ } else {
+ oldModelContainer.remove();
+ this.clearSelectedModelState();
+ }
+ } else if (modelDropdown.length > 1) {
+ const actionContainer = this.widget.querySelector('.action-container');
+ if (actionContainer) {
+ const newModelContainer = createTag('div', { class: 'models-container', 'aria-label': 'Prompt options' });
+ newModelContainer.append(...modelDropdown);
+ actionContainer.append(newModelContainer);
+ }
+ } else this.clearSelectedModelState();
+ }
+ this.widgetWrap.setAttribute('data-selected-verb', this.selectedVerbType);
+ if (this.selectedModelId) {
+ this.widgetWrap.setAttribute('data-selected-model-id', this.selectedModelId);
+ this.widgetWrap.setAttribute('data-selected-model-name', this.selectedModelName || '');
+ } else {
+ this.widgetWrap.removeAttribute('data-selected-model-id');
+ this.widgetWrap.removeAttribute('data-selected-model-name');
+ }
+ if (this.selectedModelVersion) this.widgetWrap.setAttribute('data-selected-model-version', this.selectedModelVersion);
+ else this.widgetWrap.removeAttribute('data-selected-model-version');
+ this.updateAnalytics(this.selectedVerbType);
+ if (typeof this.refreshVoiceTilesForModel === 'function') {
+ this.refreshVoiceTilesForModel();
+ }
+ if (this.genBtn) {
+ const img = this.genBtn.querySelector('img[src*=".svg"]');
+ this.genBtn.setAttribute(
+ 'aria-label',
+ (this.genBtn.getAttribute('aria-label') || '').replace(
+ new RegExp(`\\b(${verbLinkTexts.join('|')})\\b`),
+ this.selectedVerbText,
+ ),
+ );
+ if (img) img.setAttribute('alt', `${this.genBtn.getAttribute('aria-label') || ''}`);
+ }
+ };
+ }
+
+ createDropdownItems(items, listContainer, selectedElement, menuIcon) {
+ const fragment = document.createDocumentFragment();
+ items.forEach((item, idx) => {
+ const { name, icon, module, id, version } = item;
+ const listItem = createTag('li', {
+ class: 'verb-item',
+ role: 'presentation',
+ });
+ const selectedIcon = createTag('span', { class: 'selected-icon' }, '');
+ const nameContainer = createTag('span', { class: 'model-name' }, name.trim());
+ const link = createTag('a', {
+ href: '#',
+ class: 'verb-link model-link',
+ 'data-model-module': module,
+ 'data-model-id': id,
+ 'data-model-version': version,
+ 'aria-selected': 'false',
+ role: 'option',
+ }, `${nameContainer.outerHTML}`);
+ if (idx === 0) {
+ listItem.classList.add('selected');
+ link.setAttribute('aria-selected', 'true');
+ }
+ link.prepend(selectedIcon);
+ listItem.append(link);
+ fragment.append(listItem);
+ });
+ listContainer.append(fragment);
+ listContainer.addEventListener('click', (e) => {
+ const link = e.target.closest('.verb-link');
+ if (!link) return;
+ this.handleModelLinkClick(link, listContainer, selectedElement, menuIcon)(e);
+ });
+ listContainer.addEventListener('keydown', (e) => {
+ if (e.key !== 'Tab') return;
+ const menuContainer = selectedElement.parentElement;
+ if (!menuContainer?.classList.contains('show-menu')) return;
+ const links = listContainer.querySelectorAll('.verb-link');
+ if (!links.length) return;
+ const active = document.activeElement;
+ const idx = [...links].findIndex((a) => a === active || a.contains(active));
+ if (idx < 0) return;
+ const atStart = idx === 0;
+ const atEnd = idx === links.length - 1;
+ if ((e.shiftKey && atStart) || (!e.shiftKey && atEnd)) {
+ this.closeVerbOrModelMenu(selectedElement);
+ }
+ });
+ }
+
+ modelDropdown() {
+ if (!this.hasModelOptions) return [];
+ const models = Array.isArray(this.models)
+ ? this.models.filter((obj) => obj.module === this.selectedVerbType)
+ : [];
+ if (models.length === 0) return [];
+ const selectedModelType = models[0].id;
+ const selectedModelVersion = models[0].version;
+ const selectedModelModule = models[0].module;
+ const selectedModelName = models[0].name.trim();
+ const nameContainer = createTag('span', { class: 'model-name' }, models[0].name.trim());
+ const selectedElement = createTag('button', {
+ class: 'selected-model',
+ 'aria-expanded': 'false',
+ 'aria-controls': 'model-menu',
+ 'aria-label': 'model type',
+ 'aria-haspopup': 'listbox',
+ role: 'combobox',
+ 'aria-labelledby': 'listbox-label',
+ 'data-selected-model-id': selectedModelType,
+ 'data-selected-model-version': selectedModelVersion,
+ 'data-selected-model-module': selectedModelModule,
+ }, `
${nameContainer.outerHTML}`);
+ this.selectedModelModule = selectedModelModule;
+ this.selectedModelId = selectedModelType;
+ this.selectedModelVersion = selectedModelVersion;
+ this.selectedModelName = selectedModelName;
+ this.widgetWrap.setAttribute('data-selected-model-id', this.selectedModelId);
+ this.widgetWrap.setAttribute('data-selected-model-version', this.selectedModelVersion);
+ this.widgetWrap.setAttribute('data-selected-model-name', this.selectedModelName);
+ this.widgetWrap.setAttribute('data-selected-verb', this.selectedVerbType);
+ this.selectedModelText = models[0].name.trim();
+ const menuIcon = createTag('span', { class: 'menu-icon' }, '');
+ const listItems = createTag('ul', { class: 'verb-list', id: 'model-menu', role: 'listbox', 'aria-labelledby': 'listbox-label' });
+ listItems.setAttribute('style', 'display: none;');
+ selectedElement.append(menuIcon);
+ const handleDocumentClick = (e) => {
+ const menuContainer = selectedElement.parentElement;
+ if (!menuContainer.contains(e.target)) {
+ document.removeEventListener('click', handleDocumentClick);
+ this.closeVerbOrModelMenu(selectedElement);
+ }
+ };
+ selectedElement.addEventListener('click', (e) => {
+ e.stopPropagation();
+ this.showVerbOrModelMenuAndTrackOpen(selectedElement, promptWithStyleEvents.MODEL_SELECT_DROPDOWN);
+ document.addEventListener('click', handleDocumentClick);
+ }, true);
+ selectedElement.addEventListener('keydown', (e) => {
+ if (e.key === 'Enter' || e.key === ' ') {
+ this.showVerbOrModelMenuAndTrackOpen(selectedElement, promptWithStyleEvents.MODEL_SELECT_DROPDOWN);
+ }
+ if (e.key === 'Escape') {
+ this.closeVerbOrModelMenu(selectedElement);
+ selectedElement.focus();
+ }
+ });
+ this.createDropdownItems(models, listItems, selectedElement, menuIcon);
+ return [selectedElement, listItems];
+ }
+
+ createActBtn(cfg, cls) {
+ if (!cfg) return null;
+ const txt = cfg.innerText?.trim();
+ const img = cfg.querySelector('img[src*=".svg"]');
+ if (img) img.setAttribute('alt', `${txt?.split('\n')[0]} ${this.selectedVerbText}`);
+ const btn = createTag('a', { href: '#', class: `unity-act-btn ${cls}`, 'daa-ll': promptWithStyleEvents.GENERATE_CTA, 'aria-label': `${txt?.split('\n')[0]} ${this.selectedVerbText}` });
+ if (img) btn.append(createTag('div', { class: 'btn-ico' }, img));
+ if (txt) btn.append(createTag('div', { class: 'btn-txt' }, txt.split('\n')[0]));
+ this.genBtn = btn;
+ return btn;
+ }
+
+ async loadModels() {
+ const modelFile = `${getUnityPromptConfigsBaseUrl()}/unity/configs/prompt/model-picker.json`;
+ const results = await fetch(modelFile);
+ if (!results.ok) {
+ throw new Error('Failed to fetch models.');
+ }
+ const modelJson = await results.json();
+ this.models = modelJson?.content?.data;
+ }
+
+ async getModel() {
+ if (!this.hasModelOptions) return [];
+ try {
+ if (!this.models || Object.keys(this.models).length === 0) await this.loadModels();
+ return this.models;
+ } catch (e) {
+ window.lana?.log(`Message: Error loading models, Error: ${e}`, this.lanaOptions);
+ return [];
+ }
+ }
+}
+
+const RING_C = 2 * Math.PI * 20;
+const RING_STROKE_ATTR = `${(2.78751 * 48) / 33}`;
+const PAF_PP_PLAY_SVG = '';
+const PAF_PP_PAUSE_SVG = '';
+const PAF_PROGRESS_SVG = ``;
+const PAF_PLAYER_LOADING_SVG = ``;
+const voiceTileState = new WeakMap();
+
+function setVoiceTilePlayerBuffering(tile, isBuffering) {
+ const p = voiceTileState.get(tile);
+ if (!p?.player || !p.bufferLayer || !p.progressSvg || !p.center) return;
+ const on = Boolean(isBuffering);
+ if (on === p.bufferingUi) return;
+ p.bufferingUi = on;
+ if (on) {
+ p.player.classList.add('unity-paf-voice-player--buffering');
+ p.bufferLayer.setAttribute('aria-busy', 'true');
+ p.player.replaceChildren(p.bufferLayer);
+ } else {
+ p.player.classList.remove('unity-paf-voice-player--buffering');
+ p.bufferLayer.removeAttribute('aria-busy');
+ p.player.replaceChildren(p.progressSvg, p.center);
+ }
+}
+
+function setVoiceTileCenterIcon(tile, iconSvg) {
+ const p = voiceTileState.get(tile);
+ if (!p?.center) return;
+ setVoiceTilePlayerBuffering(tile, false);
+ p.center.innerHTML = iconSvg;
+}
+
+function primeVoiceAudioForPlayback(tile) {
+ const p = voiceTileState.get(tile);
+ if (!p || p.audio.src) return;
+ setVoiceTilePlayerBuffering(tile, true);
+ p.audio.preload = 'auto';
+ p.audio.src = p.url;
+}
+
+function findPlaceholderIconLi(root, iconClass) {
+ const icon = root.querySelector(`.${iconClass}`)
+ || root.querySelector(`[class*="${iconClass}"]`);
+ return icon?.closest('li') ?? null;
+}
+
+function placeholderRowText(root, iconClass) {
+ const li = findPlaceholderIconLi(root, iconClass);
+ if (!li) return '';
+ return (li.innerText || '').replace(/\s+/g, ' ').trim();
+}
+
+function placeholderRowHtmlAfterIcon(root, iconClass) {
+ const li = findPlaceholderIconLi(root, iconClass);
+ if (!li) return '';
+ const clone = li.cloneNode(true);
+ const rm = clone.querySelector(`.${iconClass}`) || clone.querySelector(`[class*="${iconClass}"]`);
+ if (rm) rm.remove();
+ return (clone.innerHTML || '').replace(/^\s+/, '').trim();
+}
+
+function findFooterLinkInRoot(root) {
+ const anchors = Array.from(root.querySelectorAll('a[href^="https://"]'));
+ const a = anchors.find((el) => {
+ const href = el.getAttribute('href')?.trim() ?? '';
+ if (!href) return false;
+ try {
+ if (/\.json$/i.test(new URL(href, window.location.href).pathname)) return false;
+ } catch {
+ return false;
+ }
+ return true;
+ });
+ if (!a) return null;
+ const href = a.getAttribute('href')?.trim() ?? '';
+ return { href, text: a.textContent?.trim() || href };
+}
+
+function dispatchAudioPlaybackFailed(widgetWrap) {
+ try {
+ widgetWrap?.dispatchEvent(new CustomEvent('firefly-audio-error', { detail: { error: 'audio-playback-failed' } }));
+ } catch (e) {
+ window.lana?.log(`Message: Error dispatching audio playback failed event, Error: ${e}`, this.lanaOptions);
+ }
+}
+
+export function parsePromptBarAudioAuthoring(root) {
+ return {
+ footerLink: findFooterLinkInRoot(root),
+ sectionHeading: placeholderRowText(root, 'icon-placeholder-voice') || 'Choose a voice',
+ currentPageSourceUrl: resolveCurrentPageVariationFileUrl(root),
+ defaultPrompt: placeholderRowText(root, 'placeholder-prompt-default') || '',
+ exploreHtml: placeholderRowHtmlAfterIcon(root, 'placeholder-explore'),
+ termsHtml: placeholderRowHtmlAfterIcon(root, 'placeholder-terms'),
+ };
+}
+
+function buildVoiceTile(voice, index, row, widgetInstance) {
+ const { name, description, url, voiceId } = voice;
+ const tile = createTag('div', {
+ class: `unity-paf-voice-tile${index === 0 ? ' selected' : ''}`,
+ role: 'listitem',
+ tabindex: '0',
+ 'aria-pressed': 'false',
+ 'data-voice-index': String(index),
+ 'data-voice-name': name,
+ });
+ if (voiceId) tile.setAttribute('data-voice-id', voiceId);
+ if (index === 0) tile.setAttribute('aria-current', 'true');
+
+ const textCol = createTag('div', { class: 'unity-paf-voice-tile-text' });
+ textCol.append(
+ createTag('span', { class: 'unity-paf-voice-name' }, name),
+ createTag('span', { class: 'unity-paf-voice-desc' }, description),
+ );
+
+ const player = createTag('div', { class: 'unity-paf-voice-player' });
+ player.insertAdjacentHTML('beforeend', PAF_PROGRESS_SVG);
+ const progressSvg = player.querySelector('.unity-paf-progress-svg');
+ const ringFg = progressSvg?.querySelector('.unity-paf-ring-fg');
+ if (!progressSvg || !ringFg) return tile;
+ ringFg.style.strokeDasharray = String(RING_C);
+ ringFg.style.strokeDashoffset = String(RING_C);
+ const center = createTag('div', { class: 'unity-paf-pp-center' });
+ center.innerHTML = PAF_PP_PLAY_SVG;
+ const bufferLayer = createTag('div', { class: 'unity-paf-voice-player-loading' });
+ bufferLayer.innerHTML = PAF_PLAYER_LOADING_SVG;
+ const audioObj = new Audio();
+ audioObj.preload = 'none';
+ voiceTileState.set(tile, {
+ audio: audioObj,
+ ringFg,
+ player,
+ bufferLayer,
+ progressSvg,
+ center,
+ playing: false,
+ url,
+ bufferingUi: false,
+ });
+ player.append(progressSvg, center);
+ tile.append(textCol, player);
+ row.append(tile);
+ const setRingProgress = (t) => {
+ const a = audioObj;
+ if (!Number.isFinite(a.duration) || a.duration <= 0) return;
+ const p = t / a.duration;
+ ringFg.style.strokeDashoffset = String(RING_C * (1 - p));
+ };
+ const showPlayIcon = () => setVoiceTileCenterIcon(tile, PAF_PP_PLAY_SVG);
+ const showPauseIcon = () => setVoiceTileCenterIcon(tile, PAF_PP_PAUSE_SVG);
+ let rafId = null;
+ const tick = () => {
+ if (audioObj.paused && !audioObj.ended) {
+ rafId = null;
+ return;
+ }
+ setRingProgress(audioObj.currentTime);
+ rafId = requestAnimationFrame(tick);
+ };
+ const startRaf = () => {
+ if (rafId) cancelAnimationFrame(rafId);
+ rafId = requestAnimationFrame(tick);
+ };
+ const stopRaf = () => {
+ if (rafId) {
+ cancelAnimationFrame(rafId);
+ rafId = null;
+ }
+ };
+ audioObj.addEventListener('loadedmetadata', () => {
+ const dur = Number.isFinite(audioObj.duration) && audioObj.duration > 0 ? audioObj.duration : 0;
+ if (dur > 0) widgetInstance.durationCache.set(url, dur);
+ setRingProgress(0);
+ });
+ audioObj.addEventListener('play', () => {
+ voiceTileState.get(tile).playing = true;
+ showPauseIcon();
+ tile.setAttribute('aria-pressed', 'true');
+ startRaf();
+ });
+ audioObj.addEventListener('pause', () => {
+ const atEnd = audioObj.ended || (Number.isFinite(audioObj.duration) && audioObj.currentTime >= audioObj.duration - 0.25);
+ voiceTileState.get(tile).playing = !atEnd && audioObj.currentTime > 0;
+ showPlayIcon();
+ if (atEnd) {
+ setRingProgress(0);
+ ringFg.style.strokeDashoffset = String(RING_C);
+ } else {
+ setRingProgress(audioObj.currentTime);
+ }
+ tile.setAttribute('aria-pressed', 'false');
+ stopRaf();
+ });
+ audioObj.addEventListener('ended', () => {
+ voiceTileState.get(tile).playing = false;
+ showPlayIcon();
+ setRingProgress(0);
+ ringFg.style.strokeDashoffset = String(RING_C);
+ tile.setAttribute('aria-pressed', 'false');
+ try { audioObj.currentTime = 0; } catch (e) { /* noop */ }
+ stopRaf();
+ });
+ audioObj.addEventListener('error', () => {
+ setVoiceTileCenterIcon(tile, PAF_PP_PLAY_SVG);
+ dispatchAudioPlaybackFailed(widgetInstance.widgetWrap);
+ });
+ audioObj.addEventListener('waiting', () => {
+ if (!audioObj.paused) setVoiceTilePlayerBuffering(tile, true);
+ });
+ audioObj.addEventListener('playing', () => {
+ if (!audioObj.paused) showPauseIcon();
+ });
+ return tile;
+}
+
+function attachVoiceInteractivity(tiles, widgetInstance, inpField, voices) {
+ const wrap = widgetInstance.widgetWrap;
+ let selectedIdx = 0;
+ const authoring = (widgetInstance.defaultPromptFromAuthoring ?? '').trim();
+
+ function setSelectedVisual(idx) {
+ selectedIdx = idx;
+ if (idx < 0) {
+ wrap.removeAttribute('data-selected-voice-index');
+ wrap.removeAttribute('data-selected-voice-name');
+ wrap.removeAttribute('data-selected-voice-id');
+ tiles.forEach((t) => {
+ t.classList.remove('selected');
+ t.removeAttribute('aria-current');
+ });
+ return;
+ }
+ wrap.setAttribute('data-selected-voice-index', String(idx));
+ wrap.setAttribute('data-selected-voice-name', voices[idx]?.name ?? '');
+ const voiceId = voices[idx]?.voiceId;
+ if (voiceId) wrap.setAttribute('data-selected-voice-id', voiceId);
+ else wrap.removeAttribute('data-selected-voice-id');
+ tiles.forEach((t, i) => {
+ t.classList.toggle('selected', i === idx);
+ if (i === idx) t.setAttribute('aria-current', 'true');
+ else t.removeAttribute('aria-current');
+ });
+ }
+
+ function resetTileIdle(tile) {
+ const p = voiceTileState.get(tile);
+ if (!p) return;
+ try { p.audio.pause(); } catch { /* ignore */ }
+ try { p.audio.currentTime = 0; } catch { /* ignore */ }
+ p.playing = false;
+ p.ringFg.style.strokeDashoffset = String(RING_C);
+ setVoiceTileCenterIcon(tile, PAF_PP_PLAY_SVG);
+ tile.setAttribute('aria-pressed', 'false');
+ }
+
+ function syncPromptIfStuckToDefaults() {
+ const { value } = inpField;
+ if (value === authoring || value.trim() === '') {
+ inpField.value = authoring;
+ }
+ }
+
+ function toggleTile(idx) {
+ const tile = tiles[idx];
+ const p = tile && voiceTileState.get(tile);
+ if (!p) return;
+ const { audio } = p;
+ const isPlaying = !audio.paused && !audio.ended;
+ if (isPlaying) {
+ audio.pause();
+ return;
+ }
+ primeVoiceAudioForPlayback(tile);
+ audio.play().catch(() => {
+ setVoiceTileCenterIcon(tile, PAF_PP_PLAY_SVG);
+ dispatchAudioPlaybackFailed(wrap);
+ });
+ }
+
+ function onTileActivate(idx) {
+ if (idx !== selectedIdx) {
+ syncPromptIfStuckToDefaults();
+ setSelectedVisual(idx);
+ tiles.forEach((t, i) => { if (i !== idx) resetTileIdle(t); });
+ const nextTile = tiles[idx];
+ primeVoiceAudioForPlayback(nextTile);
+ voiceTileState.get(nextTile).audio.play().catch(() => {
+ setVoiceTileCenterIcon(nextTile, PAF_PP_PLAY_SVG);
+ dispatchAudioPlaybackFailed(wrap);
+ });
+ return;
+ }
+ toggleTile(idx);
+ }
+
+ tiles.forEach((tile, idx) => {
+ tile.addEventListener('click', (ev) => {
+ if (ev.target?.closest && ev.target.closest('a[href]')) return;
+ ev.preventDefault();
+ onTileActivate(idx);
+ });
+ tile.addEventListener('keydown', (e) => {
+ if (e.key === 'Enter' || e.key === ' ') {
+ e.preventDefault();
+ onTileActivate(idx);
+ }
+ });
+ });
+ setSelectedVisual(0);
+ return () => { tiles.forEach(resetTileIdle); };
+}
+
+function createPromptAudioShellBase(widgetInstance, el) {
+ const widgetWrap = createTag('div', { class: 'ex-unity-wrap verb-options' });
+ const [widget, unitySprite] = ['ex-unity-widget', 'unity-sprite-container']
+ .map((c) => createTag('div', { class: c }));
+ widgetInstance.widgetWrap = widgetWrap;
+ widgetInstance.widget = widget;
+ unitySprite.innerHTML = widgetInstance.spriteCon;
+ unitySprite.classList.add('unity-slf-sprite');
+ widgetWrap.append(unitySprite);
+ const phStub = createTag('div', { hidden: true, 'aria-hidden': 'true' });
+ phStub.innerHTML = '