Skip to content

Commit 4e8f6b3

Browse files
committed
feat: Enhance form filling experience with sticky button and notification system for extracted values
1 parent 1beb743 commit 4e8f6b3

1 file changed

Lines changed: 153 additions & 22 deletions

File tree

form-flow-extension/content.js

Lines changed: 153 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,10 @@
319319
this.recognition = null;
320320
this.isListening = false;
321321
this.currentFormSchema = null;
322+
// Track all extracted values across the session
323+
this.allExtractedValues = {};
324+
this.isFormComplete = false;
325+
this.fillFormBtn = null;
322326
}
323327

324328
create() {
@@ -652,8 +656,70 @@
652656
.fill-btn-inline:hover { opacity: 0.9; transform: scale(1.02); }
653657
.fill-btn-inline svg { width: 14px; height: 14px; }
654658
659+
/* Sticky Fill Form Button */
660+
.fill-form-container {
661+
padding: 12px 16px;
662+
background: linear-gradient(180deg, transparent 0%, rgba(13, 13, 13, 0.95) 20%);
663+
opacity: 0;
664+
transform: translateY(20px);
665+
transition: all 0.4s cubic-bezier(0.16, 1, 0.3, 1);
666+
}
667+
.fill-form-container.visible {
668+
opacity: 1;
669+
transform: translateY(0);
670+
}
671+
672+
.fill-form-btn {
673+
width: 100%;
674+
padding: 14px 24px;
675+
border-radius: 12px;
676+
background: linear-gradient(135deg, var(--primary) 0%, #059669 100%);
677+
color: white;
678+
border: none;
679+
cursor: pointer;
680+
font-weight: 600;
681+
font-size: 15px;
682+
display: flex;
683+
align-items: center;
684+
justify-content: center;
685+
gap: 10px;
686+
transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
687+
box-shadow: 0 4px 20px rgba(16, 185, 129, 0.3);
688+
}
689+
.fill-form-btn:hover:not(:disabled) {
690+
transform: translateY(-2px);
691+
box-shadow: 0 8px 30px rgba(16, 185, 129, 0.4);
692+
}
693+
.fill-form-btn:active:not(:disabled) {
694+
transform: translateY(0);
695+
}
696+
.fill-form-btn:disabled {
697+
opacity: 0.7;
698+
cursor: not-allowed;
699+
}
700+
.fill-form-btn svg { width: 20px; height: 20px; }
701+
702+
/* Toast Notification */
703+
.notification {
704+
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
705+
color: white;
706+
padding: 10px 16px;
707+
font-size: 13px;
708+
font-weight: 500;
709+
text-align: center;
710+
opacity: 0;
711+
transform: translateY(-100%);
712+
transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
713+
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
714+
}
715+
.notification.visible {
716+
opacity: 1;
717+
transform: translateY(0);
718+
}
719+
655720
@keyframes fadeIn { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: translateY(0); } }
656721
722+
657723
/* Prompt Input Box (Redesigned) */
658724
.prompt-box {
659725
background: var(--bg-dark);
@@ -1216,28 +1282,24 @@
12161282
if (response.success) {
12171283
this.addMessage(response.response, 'ai');
12181284

1219-
if (Object.keys(response.extractedValues || {}).length > 0) {
1220-
// Create a fill form button inside a chat bubble
1221-
const actionBubble = document.createElement('div');
1222-
actionBubble.className = 'chat-bubble ai';
1223-
actionBubble.innerHTML = `
1224-
< div class="avatar ai" > AI</div >
1225-
<div class="msg-content">
1226-
<span>Form data ready!</span>
1227-
<button class="fill-btn-inline">
1228-
${ICONS.SEND}
1229-
Fill Form
1230-
</button>
1231-
</div>
1232-
`;
1233-
this.chatHistory.appendChild(actionBubble);
1234-
actionBubble.querySelector('button').onclick = () => {
1235-
this.autoFill(response.extractedValues);
1236-
actionBubble.remove();
1237-
};
1238-
this.scrollToBottom();
1285+
// Accumulate extracted values across the session
1286+
if (response.extractedValues && Object.keys(response.extractedValues).length > 0) {
1287+
Object.assign(this.allExtractedValues, response.extractedValues);
1288+
console.log('FormFlow: Accumulated values:', this.allExtractedValues);
1289+
1290+
// Show extraction notification
1291+
const extractedCount = Object.keys(this.allExtractedValues).length;
1292+
this.showNotification(`✓ ${extractedCount} field${extractedCount > 1 ? 's' : ''} collected`);
1293+
}
1294+
1295+
// Check if form is complete - show sticky Fill Form button
1296+
if (response.isComplete) {
1297+
this.isFormComplete = true;
1298+
this.showFillFormButton();
1299+
this.showNotification('🎉 All fields collected! Ready to fill form.');
12391300
}
12401301

1302+
12411303
} else {
12421304
this.addMessage("Error: " + response.error, 'ai');
12431305
}
@@ -1248,10 +1310,79 @@
12481310
}
12491311
}
12501312

1313+
showFillFormButton() {
1314+
// Only create if not already present
1315+
if (this.fillFormBtn) return;
1316+
1317+
// Get the shadow root
1318+
const shadow = this.container.shadowRoot;
1319+
if (!shadow) return;
1320+
1321+
// Create sticky button container
1322+
const btnContainer = document.createElement('div');
1323+
btnContainer.className = 'fill-form-container';
1324+
btnContainer.innerHTML = `
1325+
<button class="fill-form-btn">
1326+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1327+
<path d="M12 5v14"/><path d="m19 12-7 7-7-7"/>
1328+
</svg>
1329+
<span>Fill Form (${Object.keys(this.allExtractedValues).length} fields)</span>
1330+
</button>
1331+
`;
1332+
1333+
// Insert before the prompt box
1334+
const promptBox = shadow.querySelector('.prompt-box');
1335+
if (promptBox) {
1336+
promptBox.parentNode.insertBefore(btnContainer, promptBox);
1337+
}
1338+
1339+
this.fillFormBtn = btnContainer;
1340+
1341+
// Add click handler
1342+
btnContainer.querySelector('button').onclick = async () => {
1343+
btnContainer.querySelector('button').disabled = true;
1344+
btnContainer.querySelector('span').textContent = 'Filling...';
1345+
await this.autoFill(this.allExtractedValues);
1346+
btnContainer.querySelector('span').textContent = '✓ Done!';
1347+
setTimeout(() => {
1348+
btnContainer.remove();
1349+
this.fillFormBtn = null;
1350+
}, 1500);
1351+
};
1352+
1353+
// Add animation
1354+
setTimeout(() => btnContainer.classList.add('visible'), 50);
1355+
}
1356+
1357+
showNotification(message) {
1358+
const shadow = this.container?.shadowRoot;
1359+
if (!shadow) return;
1360+
1361+
// Create notification element
1362+
const notification = document.createElement('div');
1363+
notification.className = 'notification';
1364+
notification.textContent = message;
1365+
1366+
// Insert at top of panel
1367+
const panel = shadow.querySelector('.panel');
1368+
if (panel) {
1369+
panel.insertBefore(notification, panel.firstChild);
1370+
1371+
// Animate in
1372+
setTimeout(() => notification.classList.add('visible'), 10);
1373+
1374+
// Remove after delay
1375+
setTimeout(() => {
1376+
notification.classList.remove('visible');
1377+
setTimeout(() => notification.remove(), 300);
1378+
}, 2500);
1379+
}
1380+
}
1381+
12511382
async autoFill(data) {
12521383
const formFiller = new FormFiller();
1253-
await formFiller.fillFields(data, this.currentFormSchema);
1254-
this.addMessage("✅ Form updated.", 'ai');
1384+
const filledCount = await formFiller.fillFields(data, this.currentFormSchema);
1385+
this.addMessage(`✅ Form filled! (${filledCount} fields updated)`, 'ai');
12551386
}
12561387

12571388
hide() {

0 commit comments

Comments
 (0)