diff --git a/public/index.html b/public/index.html
index 730fb78..cf8663b 100644
--- a/public/index.html
+++ b/public/index.html
@@ -144,7 +144,7 @@
.prompt {
background: #2a2a2a;
- border: 1px solid #ff9800;
+ border: 2px solid #ff9800;
border-radius: 8px;
padding: 16px;
margin-bottom: 12px;
@@ -156,6 +156,93 @@
line-height: 1.5;
}
+ .prompt-header {
+ font-size: 16px;
+ font-weight: 600;
+ margin-bottom: 12px;
+ color: #ff9800;
+ }
+
+ .prompt-tool {
+ font-size: 14px;
+ font-weight: 600;
+ margin-bottom: 8px;
+ color: #888;
+ }
+
+ .prompt-tool-name {
+ color: #e0e0e0;
+ }
+
+ .prompt-tool-input {
+ font-size: 13px;
+ font-family: "SF Mono", Monaco, "Cascadia Code", "Courier New", monospace;
+ color: #888;
+ margin-bottom: 12px;
+ word-break: break-all;
+ }
+
+ .prompt-question {
+ font-size: 14px;
+ margin-bottom: 16px;
+ line-height: 1.5;
+ }
+
+ .prompt-options {
+ margin-bottom: 16px;
+ }
+
+ .prompt-option {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 12px;
+ border: 1px solid #3a3a3a;
+ border-radius: 6px;
+ margin-bottom: 8px;
+ min-height: 48px;
+ cursor: pointer;
+ user-select: none;
+ transition: background 0.2s;
+ }
+
+ .prompt-option:hover {
+ background: rgba(255, 255, 255, 0.05);
+ }
+
+ .prompt-option input[type="radio"],
+ .prompt-option input[type="checkbox"] {
+ width: 20px;
+ height: 20px;
+ margin: 0;
+ cursor: pointer;
+ flex-shrink: 0;
+ }
+
+ .prompt-option-label {
+ flex: 1;
+ font-size: 14px;
+ line-height: 1.5;
+ cursor: pointer;
+ }
+
+ .prompt-other-input {
+ width: 100%;
+ padding: 12px;
+ margin-top: 8px;
+ border: 1px solid #3a3a3a;
+ border-radius: 6px;
+ background: #1a1a1a;
+ color: #e0e0e0;
+ font-size: 14px;
+ font-family: inherit;
+ }
+
+ .prompt-other-input:focus {
+ outline: none;
+ border-color: #ff9800;
+ }
+
.prompt-actions {
display: flex;
gap: 8px;
@@ -169,7 +256,7 @@
font-size: 16px;
font-weight: 600;
cursor: pointer;
- min-height: 44px;
+ min-height: 48px;
transition: opacity 0.2s;
}
@@ -177,6 +264,11 @@
opacity: 0.7;
}
+ .btn:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+ }
+
.btn-approve {
background: #4CAF50;
color: white;
@@ -187,6 +279,34 @@
color: white;
}
+ .btn-submit {
+ background: #ff9800;
+ color: white;
+ }
+
+ .btn-secondary {
+ background: #3a3a3a;
+ color: #e0e0e0;
+ position: relative;
+ }
+
+ .btn-secondary[data-tooltip]:hover::after {
+ content: attr(data-tooltip);
+ position: absolute;
+ bottom: 100%;
+ left: 50%;
+ transform: translateX(-50%);
+ margin-bottom: 8px;
+ padding: 8px 12px;
+ background: #1a1a1a;
+ color: #e0e0e0;
+ font-size: 12px;
+ white-space: nowrap;
+ border-radius: 4px;
+ border: 1px solid #3a3a3a;
+ z-index: 1000;
+ }
+
.empty-state {
padding: 32px 16px;
text-align: center;
@@ -208,7 +328,7 @@
.session,
.prompt {
background: white;
- border-color: #e0e0e0;
+ border-color: #ff9800;
}
.session-output {
@@ -219,6 +339,24 @@
.terminal {
color: #1a1a1a;
}
+
+ .prompt-option {
+ border-color: #e0e0e0;
+ }
+
+ .prompt-option:hover {
+ background: rgba(0, 0, 0, 0.05);
+ }
+
+ .prompt-other-input {
+ background: white;
+ border-color: #e0e0e0;
+ color: #1a1a1a;
+ }
+
+ .prompt-tool-input {
+ color: #666;
+ }
}
@@ -328,6 +466,7 @@
id: data.prompt_id,
session_id: data.session_id,
text: data.prompt_text,
+ json: data.prompt_json ? JSON.parse(data.prompt_json) : null,
});
renderPrompts();
});
@@ -397,17 +536,260 @@
return;
}
- $prompts.innerHTML = Array.from(state.prompts.values()).map(p => `
+ $prompts.innerHTML = Array.from(state.prompts.values()).map(p => {
+ if (p.json) {
+ return renderRichPrompt(p);
+ } else {
+ // Fallback for old-style prompts
+ return `
+
+
${escapeHtml(p.text)}
+
+
+
+
+
+ `;
+ }
+ }).join('');
+ }
+
+ function renderRichPrompt(p) {
+ const json = p.json;
+ const promptType = json.prompt_type;
+
+ if (promptType === 'permission') {
+ return renderPermissionPrompt(p);
+ } else if (promptType === 'question') {
+ return renderQuestionPrompt(p);
+ } else if (promptType === 'exit_plan') {
+ return renderExitPlanPrompt(p);
+ } else {
+ // Unknown type, fallback
+ return `
+
+
${escapeHtml(p.text)}
+
+
+
+
+
+ `;
+ }
+ }
+
+ function renderPermissionPrompt(p) {
+ const json = p.json;
+ const toolName = json.tool_name || 'Unknown';
+ const toolInput = json.tool_input || {};
+ const options = json.options || [];
+ const selectedOption = json.selected_option;
+ const allowsTabInstructions = json.allows_tab_instructions || false;
+
+ // Extract relevant tool input info
+ let toolInputDisplay = '';
+ if (toolName === 'Write' || toolName === 'Edit' || toolName === 'Read') {
+ toolInputDisplay = toolInput.file_path || '';
+ } else if (toolName === 'Bash') {
+ toolInputDisplay = toolInput.command || '';
+ } else if (toolInput.path) {
+ toolInputDisplay = toolInput.path;
+ }
+
+ const optionsHtml = options.map((opt, idx) => `
+
+ `).join('');
+
+ return `
-
${escapeHtml(p.text)}
+
Tool: ${escapeHtml(toolName)}
+ ${toolInputDisplay ? `
${escapeHtml(toolInputDisplay)}
` : ''}
+
${escapeHtml(p.text)}
+
+ ${optionsHtml}
+
-
-
+
+ ${allowsTabInstructions ? '' : ''}
- `).join('');
+ `;
}
+ function renderQuestionPrompt(p) {
+ const json = p.json;
+ const header = json.header || '';
+ const question = json.question || p.text;
+ const options = json.options || [];
+ const multiSelect = json.multi_select || false;
+ const allowsOther = json.allows_other || false;
+ const inputType = multiSelect ? 'checkbox' : 'radio';
+
+ const optionsHtml = options.map((opt, idx) => `
+
+ `).join('');
+
+ const otherHtml = allowsOther ? `
+
+
+ ` : '';
+
+ return `
+
+ ${header ? `` : ''}
+
${escapeHtml(question)}
+
+ ${optionsHtml}
+ ${otherHtml}
+
+
+
+
+
+ `;
+ }
+
+ function renderExitPlanPrompt(p) {
+ const json = p.json;
+ const options = json.options || [];
+
+ const optionsHtml = options.map((opt, idx) => `
+
+ `).join('');
+
+ return `
+
+
+
${escapeHtml(p.text)}
+
+ ${optionsHtml}
+
+
+
+
+
+ `;
+ }
+
+ window.selectOption = (_promptId, _optionIdx) => {
+ // Radio buttons and checkboxes handle their own state
+ // This function is just for accessibility/mobile tap handling
+ };
+
+ window.enableOtherInput = (promptId) => {
+ const input = document.getElementById(`other-${promptId}`);
+ if (input) {
+ input.disabled = false;
+ input.focus();
+ }
+ };
+
+ window.submitRichPrompt = async (promptId) => {
+ promptId = Number(promptId);
+ const prompt = state.prompts.get(promptId);
+ if (!prompt || !prompt.json) {
+ console.error('Invalid prompt');
+ return;
+ }
+
+ const json = prompt.json;
+ let response;
+
+ if (json.prompt_type === 'permission') {
+ // Get selected radio button
+ const selected = document.querySelector(`input[name="prompt-${promptId}"]:checked`);
+ if (!selected) {
+ console.error('No option selected');
+ return;
+ }
+ response = {
+ type: 'option',
+ value: selected.value,
+ };
+ } else if (json.prompt_type === 'question') {
+ if (json.multi_select) {
+ // Get all checked checkboxes
+ const checked = Array.from(document.querySelectorAll(`input[name="prompt-${promptId}"]:checked`));
+ if (checked.length === 0) {
+ console.error('No options selected');
+ return;
+ }
+ const values = checked.map(el => {
+ if (el.value === '__other__') {
+ const otherInput = document.getElementById(`other-${promptId}`);
+ return otherInput ? otherInput.value : '';
+ }
+ return el.value;
+ });
+ response = {
+ type: 'text',
+ value: values.join(', '),
+ };
+ } else {
+ // Get selected radio button
+ const selected = document.querySelector(`input[name="prompt-${promptId}"]:checked`);
+ if (!selected) {
+ console.error('No option selected');
+ return;
+ }
+ if (selected.value === '__other__') {
+ const otherInput = document.getElementById(`other-${promptId}`);
+ if (!otherInput || !otherInput.value) {
+ console.error('Please enter a value for "Other"');
+ return;
+ }
+ response = {
+ type: 'text',
+ value: otherInput.value,
+ };
+ } else {
+ response = {
+ type: 'text',
+ value: selected.value,
+ };
+ }
+ }
+ } else if (json.prompt_type === 'exit_plan') {
+ // Get selected radio button
+ const selected = document.querySelector(`input[name="prompt-${promptId}"]:checked`);
+ if (!selected) {
+ console.error('No option selected');
+ return;
+ }
+ response = {
+ type: 'text',
+ value: selected.value,
+ };
+ }
+
+ try {
+ const res = await fetch(`/api/prompts/${promptId}/answer`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(response),
+ });
+ if (!res.ok) {
+ console.error('Failed to submit prompt response:', await res.text());
+ }
+ } catch (error) {
+ console.error('Error submitting prompt response:', error);
+ }
+ };
+
window.respondToPrompt = async (promptId, action) => {
promptId = Number(promptId);
try {