Put Duplicate on the drawings list; do not require a time under the click.

The handler never ran — no event, no POST. Right-click in empty time
also hid the menu. List button uses the selected trendline.
This commit is contained in:
Chris Amow 2026-08-28 15:17:19 -05:00
parent c1d508cb4f
commit a057341bbf
3 changed files with 32 additions and 4 deletions

View file

@ -1112,6 +1112,23 @@ createApp({
return `${base} ${number}`;
}
const selectedTrendline = computed(() => {
const id = selectedDrawing.value;
if (!id) return null;
const line = levels.value.find(level => level.id === id);
if (!line || line.kind !== 'manual' || !line.slope) return null;
return line;
});
async function duplicateSelected() {
const line = selectedTrendline.value;
if (!line) {
addEvent('DUPLICATE FAILED · select one trendline', 'error');
return;
}
await duplicateLine(line);
}
async function duplicateLine(line) {
const fail = reason => {
addEvent(`DUPLICATE #${line.number} FAILED · ${reason}`, 'error');
@ -1125,6 +1142,15 @@ createApp({
}),
}).catch(() => {});
};
fetch('/api/debug/snap', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
kind: 'duplicate',
tf: line.tf,
note: `start #${line.number} last_t ${line.last_t} cutoff ${line.cutoff_t}`,
}),
}).catch(() => {});
if (line.geometry_resolved === false) {
fail('no source geometry');
return;
@ -1656,6 +1682,6 @@ createApp({
window.removeEventListener('keydown', handleKeydown);
});
return { status, price, sessionOpen, quoteChange, animateCurrentPrice, autoScrollLivePrice, extraDetail, hideLowerTfDrawings, confluenceAlerts, barAge, dataUpdatedAt, buildStamp, timeframe, timeframes, drawingColors, drawingColorRows, drawingColorName, colorRowLabels, symbolChoices, selectedSymbol, symbolColor, symbolScale, symbolScales, symbolPanelOpen, prefs, clusters, clustersByPrice, events, eventsMore, loadOlderEvents, diagnosticMode, captureBusy, captureCountdown, captureDiagnostic, armedTool, drawName, drawColor, drawWidth, drawSide, snap, selectedDrawing, selectedDrawings, drawingList, startDrawingListResize, manualLines, hasDrawingSelection, allShownSelected, selectedAreHidden, alertPrice, alertNote, alertEarlyPoints, levelColor, levelWidth, addPriceAlert, armTool, selectTimeframe, allEnabled, toggleGroup, maValue, maAlertOn, toggleMaAlert, deleteSelected, deleteLine, toggleDrawingSelection, activateDrawing, toggleSelectAll, toggleSelectedVisibility, renameLine, updateLineStyle, updateLevelNumber, setArmed, commentText, commentFloat, comments, drawings, filteredDrawings, drawingFilter, drawingKind, drawingTf, deleteDrawing, toggleComment, togglePinned, chooseSymbol, toggleSymbolPanel, startSymbolDrag, dropSymbol, optionExpirations, optionExpiryId, optionSide, optionMode, optionMin, optionMax, optionContracts, optionUnderlying, optionBusy, optionError, optionSearched, optionCopied, onOptionsToggle, searchOptions, copyOption, canUndo, undoTitle, undo };
return { status, price, sessionOpen, quoteChange, animateCurrentPrice, autoScrollLivePrice, extraDetail, hideLowerTfDrawings, confluenceAlerts, barAge, dataUpdatedAt, buildStamp, timeframe, timeframes, drawingColors, drawingColorRows, drawingColorName, colorRowLabels, symbolChoices, selectedSymbol, symbolColor, symbolScale, symbolScales, symbolPanelOpen, prefs, clusters, clustersByPrice, events, eventsMore, loadOlderEvents, diagnosticMode, captureBusy, captureCountdown, captureDiagnostic, armedTool, drawName, drawColor, drawWidth, drawSide, snap, selectedDrawing, selectedDrawings, drawingList, startDrawingListResize, manualLines, hasDrawingSelection, allShownSelected, selectedAreHidden, selectedTrendline, duplicateSelected, alertPrice, alertNote, alertEarlyPoints, levelColor, levelWidth, addPriceAlert, armTool, selectTimeframe, allEnabled, toggleGroup, maValue, maAlertOn, toggleMaAlert, deleteSelected, deleteLine, toggleDrawingSelection, activateDrawing, toggleSelectAll, toggleSelectedVisibility, renameLine, updateLineStyle, updateLevelNumber, setArmed, commentText, commentFloat, comments, drawings, filteredDrawings, drawingFilter, drawingKind, drawingTf, deleteDrawing, toggleComment, togglePinned, chooseSymbol, toggleSymbolPanel, startSymbolDrag, dropSymbol, optionExpirations, optionExpiryId, optionSide, optionMode, optionMin, optionMax, optionContracts, optionUnderlying, optionBusy, optionError, optionSearched, optionCopied, onOptionsToggle, searchOptions, copyOption, canUndo, undoTitle, undo };
},
}).mount('#app');

View file

@ -569,7 +569,8 @@ class ConfluenceChart {
duplicate.textContent = 'Duplicate';
duplicate.addEventListener('click', event => {
event.stopPropagation();
const level = this.levels.find(value => value.id === this.contextLineId);
const id = this.contextLineId || this.selectedLineId;
const level = this.levels.find(value => value.id === id);
this.hideContextMenu();
if (level && this.onLineDuplicate) this.onLineDuplicate({ ...level });
});
@ -3167,9 +3168,9 @@ class ConfluenceChart {
if (!level || !this.bars.length) return;
event.preventDefault();
const point = this.eventPoint(event);
if (!point || !this.withinPlot(point) || point.t == null) return;
if (!point || !this.withinPlot(point)) return;
const lastBar = this.bars[this.bars.length - 1];
const cutoff = point.t > lastBar.t
const cutoff = point.t == null ? null : point.t > lastBar.t
? Math.round(point.t)
: this.bars.reduce((nearest, bar) =>
Math.abs(bar.t - point.t) < Math.abs(nearest.t - point.t) ? bar : nearest

View file

@ -282,6 +282,7 @@
<i :class="selectedAreHidden ? 'fa-solid fa-eye' : 'fa-solid fa-eye-slash'"></i>
{{ selectedAreHidden ? 'Show' : 'Hide' }}
</button>
<button @click="duplicateSelected" :disabled="!selectedTrendline" aria-label="Duplicate selected trendline">Duplicate</button>
<button @click="deleteSelected" :disabled="!selectedDrawings.length" aria-label="Delete selected drawings">Delete</button>
</div>
<div class="drawing-list-shell">