diff --git a/web/superball.html b/web/superball.html
index fa93f82..65b827a 100644
--- a/web/superball.html
+++ b/web/superball.html
@@ -830,6 +830,35 @@
input.value = '';
clearRelayDropdown(bounceId);
}
+
+ // Check if bounce inputs are valid and update button state
+ updateCreateBounceButtonState(bounceId);
+ }
+
+ // Check if bounce is ready to be created and update button state
+ function updateCreateBounceButtonState(bounceId) {
+ const button = document.getElementById(`create-bounce-btn-${bounceId}`);
+ if (!button) return;
+
+ const throwerPubkey = getThrowerPubkeyForBounce(bounceId);
+ const relayInput = document.getElementById(`bounce-relays-${bounceId}`);
+ const hasRelays = relayInput && relayInput.value.trim().length > 0;
+
+ const isValid = throwerPubkey && hasRelays;
+
+ if (isValid) {
+ // Enable button
+ button.disabled = false;
+ button.style.color = '';
+ button.style.borderColor = '';
+ button.style.cursor = '';
+ } else {
+ // Disable button
+ button.disabled = true;
+ button.style.color = 'var(--muted-color)';
+ button.style.borderColor = 'var(--muted-color)';
+ button.style.cursor = 'not-allowed';
+ }
}
// Handle manual thrower pubkey input
@@ -866,6 +895,9 @@
} else {
clearRelayDropdown(bounceId);
}
+
+ // Update button state after thrower input change
+ updateCreateBounceButtonState(bounceId);
}
// Get thrower pubkey for a bounce (from dropdown or manual input)
@@ -1022,6 +1054,9 @@
manualDiv.classList.add('hidden');
input.value = '';
}
+
+ // Update button state after relay selection change
+ updateCreateBounceButtonState(bounceId);
}
// Handle manual relay input
@@ -1036,6 +1071,9 @@
}
console.log('INFO', `Manual relay input for bounce ${bounceId}: ${input.value}`);
}
+
+ // Update button state after manual relay input change
+ updateCreateBounceButtonState(bounceId);
}
// Get all writable relays for a thrower as comma-separated string
@@ -1172,7 +1210,7 @@
-
+
@@ -1623,7 +1661,9 @@
// For the first bounce, we skip the user publishing step since it's handled by the button
// Start directly with relay propagation after the button click
const routingEventSize = JSON.stringify(bounce.routingEvent).length;
- const relays = getRelaysForBounce(bounceNumber);
+
+ // Get the relays from the current bounce's routing instructions (where user publishes to)
+ const relays = bounce.payload?.routing?.relays || [];
// Step 1: Relay propagates (immediate after button click)
currentTime += 2000; // 2 seconds for relay propagation
@@ -1645,7 +1685,10 @@
if (isLast) {
// Last bounce - posts final event
const finalEventSize = JSON.stringify(finalEvent).length + paddingAdjustment;
- const finalRelays = getRelaysForBounce(bounceNumber);
+
+ // Get the relays from the current bounce's routing instructions (where final event gets posted)
+ const finalRelays = bounce.payload?.routing?.relays || [];
+
const delaySeconds = getDelayForBounce(bounceNumber);
const paddingAdded = getPaddingAdjustmentForBounce(bounceNumber);
@@ -1673,38 +1716,43 @@
size: Math.max(finalEventSize, 0)
});
} else {
- // Intermediate bounce - forwards to next superball
- const nextBounce = bounces[index + 1];
- const nextThrowerName = getThrowerName(nextBounce, bounceNumber + 1);
- const nextRoutingSize = JSON.stringify(nextBounce.routingEvent).length + paddingAdjustment;
- const nextRelays = getRelaysForBounce(bounceNumber + 1); // Next superball's relays
- const delaySeconds = getDelayForBounce(bounceNumber);
- const paddingAdded = getPaddingAdjustmentForBounce(bounceNumber);
+ // Intermediate bounce - forwards to next superball
+ const nextBounce = reversedBounces[index + 1];
+ const nextBounceNumber = bounces.length - (index + 1);
+ const nextThrowerName = getThrowerName(nextBounce, nextBounceNumber);
+ const nextRoutingSize = JSON.stringify(nextBounce.routingEvent).length + paddingAdjustment;
+
+ // Get the relays from the current bounce's routing instructions (where this thrower forwards to)
+ const currentBounce = reversedBounces[index];
+ const forwardingRelays = currentBounce.payload?.routing?.relays || [];
+
+ const delaySeconds = getDelayForBounce(bounceNumber);
+ const paddingAdded = getPaddingAdjustmentForBounce(bounceNumber);
- // Create detailed forwarding action description
- let actionDescription = `Grabs message, waits ${delaySeconds} seconds`;
- if (paddingAdded > 0) {
- actionDescription += `, adds ${paddingAdded} bytes of padding`;
- }
- actionDescription += `, and forwards to: ${nextRelays.join(', ')}`;
+ // Create detailed forwarding action description
+ let actionDescription = `Grabs message, waits ${delaySeconds} seconds`;
+ if (paddingAdded > 0) {
+ actionDescription += `, adds ${paddingAdded} bytes of padding`;
+ }
+ actionDescription += `, and forwards to: ${forwardingRelays.join(', ')}`;
- // Step 3: Superball forwards to next relay
- flow.push({
- time: currentTime,
- actor: throwerName,
- action: actionDescription,
- size: Math.max(nextRoutingSize, 0)
- });
+ // Step 3: Superball forwards to next relay
+ flow.push({
+ time: currentTime,
+ actor: throwerName,
+ action: actionDescription,
+ size: Math.max(nextRoutingSize, 0)
+ });
- // Step 4: Relay propagates to next superball
- currentTime += 2000; // 2 seconds for relay propagation
- flow.push({
- time: currentTime,
- actor: `Relay (${nextRelays.join(', ')})`,
- action: `Event available for ${nextThrowerName}`,
- size: Math.max(nextRoutingSize, 0)
- });
- }
+ // Step 4: Relay propagates to next superball
+ currentTime += 2000; // 2 seconds for relay propagation
+ flow.push({
+ time: currentTime,
+ actor: `Relay (${forwardingRelays.join(', ')})`,
+ action: `Event available for ${nextThrowerName}`,
+ size: Math.max(nextRoutingSize, 0)
+ });
+ }
});
return flow;
@@ -1827,6 +1875,11 @@
existingViz.remove();
}
+ // Refresh thrower status after reset so new dropdowns have current availability
+ setTimeout(() => {
+ discoverThrowers();
+ }, 500);
+
console.log('INFO: Builder reset successfully');
}
diff --git a/web/thrower.html b/web/thrower.html
index ba899f8..9a115fe 100644
--- a/web/thrower.html
+++ b/web/thrower.html
@@ -2113,8 +2113,8 @@
item.status.charAt(0).toUpperCase() + item.status.slice(1);
div.innerHTML = `
+
Status: ${statusText}
Event: ${item.id.substring(0, 32)}...
-
Status: ${statusText}
Target Relays: ${item.routing.relays.length}
Delay: ${item.routing.delay}s
${item.routing.padding ? `
Padding: ${item.routing.padding}
` : ''}