diag delay so we can solve trendline slope problem
This commit is contained in:
parent
01acdce045
commit
693d0873f8
5 changed files with 34 additions and 8 deletions
|
|
@ -102,6 +102,10 @@ metadata remain authenticated. Inspect only a URL the user explicitly shares,
|
|||
then immediately `DELETE /api/debug/captures/{id}`. The server also expires
|
||||
captures after 24 hours and caps the directory at 50 files.
|
||||
|
||||
After the browser's required share picker closes, capture waits five seconds so
|
||||
the user can restore a hover tooltip. `Alt+Shift+C` starts the same delayed flow
|
||||
without clicking the status-bar button.
|
||||
|
||||
Diagnostic mode also shows a compact projection readout for visible manual
|
||||
trendlines: historical/future canonical price changes, their screen slopes, and
|
||||
whether the future canvas point exists. Include it in a capture when a line
|
||||
|
|
|
|||
|
|
@ -697,6 +697,12 @@ inspect the pixels. `DELETE /api/debug/captures/{id}` is public too, so an agent
|
|||
can remove an inspected screenshot immediately. The existing 24-hour expiry and
|
||||
50-capture cap remain the backstop.
|
||||
|
||||
The screen-share picker steals focus, so an immediate frame cannot contain a
|
||||
hover tooltip. Capture now waits five seconds after the picker closes and shows
|
||||
the countdown in the status bar; the user can move back over the target before
|
||||
zero. `Alt+Shift+C` starts the same flow from the keyboard, though the picker is
|
||||
still mandatory browser security.
|
||||
|
||||
The public image and cleanup handlers live in `meta.py`, the deliberately
|
||||
unauthenticated router. Capture creation and metadata remain in `routes.py`,
|
||||
behind the normal chart authentication.
|
||||
|
|
|
|||
|
|
@ -137,6 +137,7 @@ createApp({
|
|||
const drawingTf = ref('all');
|
||||
const diagnosticMode = ref(ConfluenceChart.diagnosticMode());
|
||||
const captureBusy = ref(false);
|
||||
const captureCountdown = ref(0);
|
||||
const snap = ref(true);
|
||||
const selectedDrawing = ref(null);
|
||||
const selectedDrawings = ref([]);
|
||||
|
|
@ -630,6 +631,12 @@ createApp({
|
|||
if (video.requestVideoFrameCallback) video.requestVideoFrameCallback(() => resolve());
|
||||
else requestAnimationFrame(() => resolve());
|
||||
});
|
||||
for (let seconds = 5; seconds > 0; seconds -= 1) {
|
||||
captureCountdown.value = seconds;
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
}
|
||||
captureCountdown.value = 0;
|
||||
await new Promise(resolve => requestAnimationFrame(resolve));
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = video.videoWidth;
|
||||
canvas.height = video.videoHeight;
|
||||
|
|
@ -671,6 +678,7 @@ createApp({
|
|||
addEvent(cancelled ? 'CAPTURE CANCELLED' : `CAPTURE FAILED · ${error.message}`, 'capture-error');
|
||||
} finally {
|
||||
stream?.getTracks().forEach(track => track.stop());
|
||||
captureCountdown.value = 0;
|
||||
captureBusy.value = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1306,6 +1314,12 @@ createApp({
|
|||
}
|
||||
|
||||
function handleKeydown(event) {
|
||||
if (diagnosticMode.value && event.altKey && event.shiftKey
|
||||
&& event.code === 'KeyC' && !captureBusy.value) {
|
||||
event.preventDefault();
|
||||
captureDiagnostic();
|
||||
return;
|
||||
}
|
||||
if (event.key === 'Escape') {
|
||||
const palettes = [...document.querySelectorAll('.color-picker[open]')];
|
||||
palettes.forEach(palette => { palette.open = false; });
|
||||
|
|
@ -1542,6 +1556,6 @@ createApp({
|
|||
window.removeEventListener('keydown', handleKeydown);
|
||||
});
|
||||
|
||||
return { status, price, sessionOpen, quoteChange, animateCurrentPrice, autoScrollLivePrice, extraDetail, hideLowerTfDrawings, barAge, dataUpdatedAt, buildStamp, timeframe, timeframes, drawingColors, drawingColorRows, drawingColorName, colorRowLabels, symbolChoices, selectedSymbol, symbolColor, symbolScale, symbolScales, symbolPanelOpen, prefs, clusters, clustersByPrice, events, eventsMore, loadOlderEvents, diagnosticMode, captureBusy, 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, 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 };
|
||||
},
|
||||
}).mount('#app');
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
<span v-else-if="armedTool === 'fibonacci'" class="arm-hint">Drag from one swing to the other</span>
|
||||
<span v-else-if="selectedDrawing" class="arm-hint">Drawing selected — Delete removes it</span>
|
||||
<button v-if="diagnosticMode" class="diag-capture" :disabled="captureBusy" @click="captureDiagnostic">
|
||||
{{ captureBusy ? 'CAPTURING…' : 'CAPTURE DIAGNOSTIC' }}
|
||||
{{ captureCountdown ? `CAPTURE IN ${captureCountdown}…` : captureBusy ? 'CAPTURING…' : 'CAPTURE DIAGNOSTIC' }}
|
||||
</button>
|
||||
<span class="data-freshness"><span>BUILD <b>{{ buildStamp }}</b></span><span>UPDATED <b>{{ dataUpdatedAt }}</b></span></span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -72,10 +72,7 @@ test('an existing browser token is migrated once and removed from local storage'
|
|||
});
|
||||
|
||||
test('diagnostic capture uploads a PNG and adds its capability ID to Events',
|
||||
{
|
||||
timeout: 180000,
|
||||
skip: 'quarantined: display-media mocks and async upload completion are not yet reliable',
|
||||
}, async () => {
|
||||
{ timeout: 180000 }, async () => {
|
||||
const { browser, page } = await launch();
|
||||
let uploaded = null;
|
||||
try {
|
||||
|
|
@ -113,7 +110,10 @@ test('diagnostic capture uploads a PNG and adds its capability ID to Events',
|
|||
});
|
||||
HTMLMediaElement.prototype.play = async function play() {};
|
||||
HTMLVideoElement.prototype.requestVideoFrameCallback = function callback(done) { done(); };
|
||||
HTMLCanvasElement.prototype.getContext = () => ({ drawImage() {} });
|
||||
const realGetContext = HTMLCanvasElement.prototype.getContext;
|
||||
HTMLCanvasElement.prototype.getContext = function getContext(...args) {
|
||||
return this.closest('#chart') ? realGetContext.apply(this, args) : { drawImage() {} };
|
||||
};
|
||||
HTMLCanvasElement.prototype.toBlob = function toBlob(done) {
|
||||
done(new Blob([new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10])], {
|
||||
type: 'image/png',
|
||||
|
|
@ -121,7 +121,9 @@ test('diagnostic capture uploads a PNG and adds its capability ID to Events',
|
|||
};
|
||||
});
|
||||
|
||||
await page.locator('.diag-capture').click();
|
||||
await page.keyboard.press('Alt+Shift+C');
|
||||
await page.waitForFunction(() =>
|
||||
document.querySelector('.diag-capture')?.textContent.includes('CAPTURE IN'));
|
||||
await page.waitForFunction(() =>
|
||||
[...document.querySelectorAll('.alert-entry')]
|
||||
.some(entry => entry.textContent.includes('c-TESTCAPTURE1')));
|
||||
|
|
|
|||
Loading…
Reference in a new issue