current bar fix and ma features
This commit is contained in:
parent
92a9d4b3d3
commit
6e69b7337a
4 changed files with 42 additions and 18 deletions
|
|
@ -1205,6 +1205,12 @@ createApp({
|
|||
return available.every(period => (prefs.value.enabled.ma[tf] || []).includes(period));
|
||||
}
|
||||
|
||||
function maValue(period) {
|
||||
const level = levels.value.find(item => item.kind === 'ma' && item.tf === '1d' && item.period === period);
|
||||
const value = level?.anchor_p;
|
||||
return value == null ? '—' : Number(value).toFixed(2);
|
||||
}
|
||||
|
||||
function maAlertOn(period) {
|
||||
return (maAlerts.value['1d'] || []).includes(period);
|
||||
}
|
||||
|
|
@ -1345,6 +1351,6 @@ createApp({
|
|||
window.removeEventListener('keydown', handleKeydown);
|
||||
});
|
||||
|
||||
return { status, price, sessionOpen, quoteChange, animateCurrentPrice, autoScrollLivePrice, extraDetail, 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, maAlertOn, toggleMaAlert, deleteSelected, deleteLine, toggleDrawingSelection, 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 };
|
||||
return { status, price, sessionOpen, quoteChange, animateCurrentPrice, autoScrollLivePrice, extraDetail, 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, 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 };
|
||||
},
|
||||
}).mount('#app');
|
||||
|
|
|
|||
|
|
@ -646,6 +646,11 @@ class ConfluenceChart {
|
|||
this.chart.timeScale().subscribeVisibleLogicalRangeChange(() => {
|
||||
this.scheduleOverlays();
|
||||
});
|
||||
this.scaleEndListener = () => {
|
||||
requestAnimationFrame(() => this.syncCurrentPricePulse());
|
||||
};
|
||||
el.addEventListener('wheel', this.scaleEndListener, { passive: true });
|
||||
el.addEventListener('pointerup', this.scaleEndListener);
|
||||
this.installPinchZoom();
|
||||
}
|
||||
|
||||
|
|
@ -847,10 +852,7 @@ class ConfluenceChart {
|
|||
|
||||
setCurrentPriceAnimation(enabled) {
|
||||
this.animateCurrentPrice = Boolean(enabled);
|
||||
if (!this.currentPricePulse) return;
|
||||
const active = this.animateCurrentPrice && this.lastCurrentPrice != null;
|
||||
this.currentPricePulse.hidden = !active;
|
||||
this.currentPricePulse.classList.toggle('active', active);
|
||||
this.syncCurrentPricePulse();
|
||||
}
|
||||
|
||||
setAutoScrollLivePrice(enabled) {
|
||||
|
|
@ -881,14 +883,17 @@ class ConfluenceChart {
|
|||
else this.currentPriceLine = this.candles.createPriceLine(options);
|
||||
|
||||
this.lastCurrentPrice = price;
|
||||
this.syncCurrentPricePulse();
|
||||
}
|
||||
|
||||
syncCurrentPricePulse() {
|
||||
if (!this.currentPricePulse) return;
|
||||
requestAnimationFrame(() => {
|
||||
const y = this.candles.priceToCoordinate(price);
|
||||
if (y == null) return;
|
||||
this.currentPricePulse.style.top = `${Math.round(y)}px`;
|
||||
this.currentPricePulse.hidden = !this.animateCurrentPrice;
|
||||
this.currentPricePulse.classList.toggle('active', this.animateCurrentPrice);
|
||||
});
|
||||
const show = this.animateCurrentPrice && this.lastCurrentPrice != null;
|
||||
this.currentPricePulse.hidden = !show;
|
||||
this.currentPricePulse.classList.toggle('active', show);
|
||||
if (!show || !this.candles || this.lastCurrentPrice == null) return;
|
||||
const y = this.candles.priceToCoordinate(this.lastCurrentPrice);
|
||||
if (y != null) this.currentPricePulse.style.top = `${Math.round(y)}px`;
|
||||
}
|
||||
|
||||
setCommentHandlers({ onToggle, onMove, onSelect }) {
|
||||
|
|
@ -1445,6 +1450,7 @@ class ConfluenceChart {
|
|||
this.renderProjectionDiagnostic();
|
||||
this.renderComments();
|
||||
this.renderContextLabels();
|
||||
this.syncCurrentPricePulse();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -2985,6 +2991,10 @@ class ConfluenceChart {
|
|||
clearTimeout(this.lineFocusTimer);
|
||||
clearTimeout(this.fibFocusTimer);
|
||||
for (const node of this.commentNodes.values()) clearTimeout(node.focusTimer);
|
||||
if (this.chartEl && this.scaleEndListener) {
|
||||
this.chartEl.removeEventListener('wheel', this.scaleEndListener);
|
||||
this.chartEl.removeEventListener('pointerup', this.scaleEndListener);
|
||||
}
|
||||
if (this.chartEl && this.clickListener) this.chartEl.removeEventListener('click', this.clickListener);
|
||||
if (this.chartEl && this.contextListener) this.chartEl.removeEventListener('contextmenu', this.contextListener);
|
||||
if (this.anchorMoveListener) window.removeEventListener('pointermove', this.anchorMoveListener);
|
||||
|
|
|
|||
|
|
@ -42,10 +42,18 @@
|
|||
<aside>
|
||||
<details class="sidebar-section">
|
||||
<summary>Layers</summary>
|
||||
<div class="layer-group">
|
||||
<label class="master"><input type="checkbox" :checked="allEnabled('1d')" @change="toggleGroup('1d', $event.target.checked)"><span class="swatch tf-1d"></span>Daily MAs</label>
|
||||
<div class="periods"><span v-for="period in [10,20,50,100,200]" :key="period" class="period"><label><input type="checkbox" :value="period" v-model="prefs.enabled.ma['1d']">{{ period }}</label><button type="button" class="ma-alert" :class="{on: maAlertOn(period)}" :aria-pressed="maAlertOn(period)" :title="maAlertOn(period) ? 'Stop alerting on this average' : 'Alert when price reaches this average'" @click="toggleMaAlert(period)"><i :class="maAlertOn(period) ? 'fa-solid fa-bell' : 'fa-solid fa-bell-slash'"></i></button></span></div>
|
||||
</div>
|
||||
<details class="layer-group ma-fold" open>
|
||||
<summary>
|
||||
<label class="master" @click.stop><input type="checkbox" :checked="allEnabled('1d')" @change="toggleGroup('1d', $event.target.checked)"><span class="swatch tf-1d"></span>Daily MAs</label>
|
||||
</summary>
|
||||
<div class="ma-rows">
|
||||
<div v-for="period in [10,20,50,100,200]" :key="period" class="ma-row">
|
||||
<label><input type="checkbox" :value="period" v-model="prefs.enabled.ma['1d']">{{ period }}</label>
|
||||
<span class="ma-value">{{ maValue(period) }}</span>
|
||||
<button type="button" class="ma-alert" :class="{on: maAlertOn(period)}" :aria-pressed="maAlertOn(period)" :title="maAlertOn(period) ? 'Stop alerting on this average' : 'Alert when price reaches this average'" @click="toggleMaAlert(period)"><i :class="maAlertOn(period) ? 'fa-solid fa-bell' : 'fa-solid fa-bell-slash'"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
<div class="layer-group">
|
||||
<label><input type="checkbox" v-model="prefs.enabled.horizontal"><span class="swatch horizontal"></span>Prior day H/L/C</label>
|
||||
<label><input type="checkbox" v-model="prefs.enabled.vwap"><span class="swatch vwap"></span>Session VWAP</label>
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ button { border:1px solid var(--line); background:transparent; color:var(--muted
|
|||
.chart-context-menu { position:absolute; z-index:8; width:165px; padding:4px; border:1px solid var(--line); background:var(--panel); box-shadow:0 5px 18px color-mix(in srgb,var(--fg) 15%,transparent); pointer-events:auto; }.chart-context-menu[hidden] { display:none; }.chart-context-menu button { width:100%; padding:6px 8px; text-align:left; color:var(--fg); font-size:10px; }
|
||||
.statusbar { min-height:34px; display:flex; align-items:center; gap:24px; padding:6px 13px; border-top:1px solid var(--line); color:var(--muted); font-size:10px; }.statusbar b { color:var(--fg); text-transform:uppercase; }.data-freshness { display:flex; gap:24px; margin-left:auto; white-space:nowrap; }.diag-capture { padding:3px 7px; border-color:var(--accent); color:var(--accent); font-size:9px; white-space:nowrap; }.diag-capture:disabled { opacity:.55; cursor:wait; }
|
||||
aside { padding:16px; }h2 { margin:0 0 12px; color:var(--muted); font-size:11px; text-transform:uppercase; letter-spacing:1.3px; }h2:not(:first-child) { margin-top:30px; }.empty { border-left:2px solid var(--line); padding:10px 12px; color:var(--muted); font-size:11px; }
|
||||
.sidebar-section { margin-top:30px; }.sidebar-section:first-of-type { margin-top:0; }.sidebar-section summary { margin-bottom:12px; color:var(--muted); font-size:11px; text-transform:uppercase; letter-spacing:1.3px; cursor:pointer; user-select:none; }.sidebar-section:not([open]) summary { margin-bottom:0; }
|
||||
.sidebar-section { margin-top:30px; }.sidebar-section:first-of-type { margin-top:0; }.sidebar-section > summary { margin-bottom:12px; color:var(--muted); font-size:11px; text-transform:uppercase; letter-spacing:1.3px; cursor:pointer; user-select:none; }.sidebar-section:not([open]) > summary { margin-bottom:0; }
|
||||
.drawing-list-shell { display:flex; flex-direction:column; min-width:0; }
|
||||
.drawing-list { overflow:auto; min-height:88px; height:180px; border:1px solid var(--line); border-bottom:0; }
|
||||
.drawing-list-resize { flex:none; height:11px; border:1px solid var(--line); background:var(--chart-bg); cursor:ns-resize; touch-action:none; }
|
||||
|
|
@ -36,7 +36,7 @@ aside { padding:16px; }h2 { margin:0 0 12px; color:var(--muted); font-size:11px;
|
|||
.color-popover { grid-template-columns:1fr; width:178px; gap:3px; }
|
||||
.color-family-row { display:grid; grid-template-columns:70px 1fr; align-items:center; gap:5px; min-width:0; }.color-family-row:nth-child(3),.color-family-row:nth-child(5) { margin-top:4px; padding-top:4px; border-top:1px solid var(--line); }.color-family-input { width:70px; height:18px; padding:1px 3px; border:1px solid transparent; border-radius:2px; background:transparent; color:var(--muted); font:inherit; font-size:8px; }.color-family-input:hover,.color-family-input:focus { border-color:var(--line); background:var(--chart-bg); color:var(--fg); outline:0; }.color-family-swatches { display:grid; grid-template-columns:repeat(4,18px); gap:3px; }.color-family-swatches>button,.color-popover-footer button { width:18px; height:18px; padding:0; border:1px solid color-mix(in srgb,var(--fg) 20%,transparent); border-radius:2px; cursor:pointer; }.color-family-swatches>button.selected { outline:2px solid var(--fg); outline-offset:1px; }.color-popover-footer { display:grid; grid-template-columns:18px 1fr 18px; align-items:center; gap:3px; margin-top:4px; padding-top:5px; border-top:1px solid var(--line); }.color-popover-footer .palette-close { grid-column:3; color:var(--muted); background:var(--chart-bg); }
|
||||
.level-editors { display:flex; gap:4px; overflow:visible; text-transform:none; }.level-editors label { display:flex; align-items:center; gap:2px; }.level-editors input { width:54px; height:19px; padding:1px 3px; border:1px solid var(--line); border-radius:3px; background:var(--panel); color:var(--fg); font:inherit; font-size:8px; }
|
||||
.layer-group { padding:9px 0; border-bottom:1px solid var(--line); display:grid; gap:7px; }.layer-group label,.score-hidden { display:flex; align-items:center; gap:7px; font-size:11px; cursor:pointer; }.layer-group input,.score-hidden input { accent-color:var(--accent); }.periods { display:grid; grid-template-columns:repeat(3, max-content); gap:7px 10px; padding-left:20px; }.periods .period { display:flex; align-items:center; gap:2px; }.periods label { color:var(--muted); gap:4px; }.periods input { width:12px; height:12px; margin:0; flex:none; }.ma-alert { display:grid; place-items:center; width:16px; height:16px; padding:0; border:0; background:transparent; color:var(--muted); font-size:9px; }.ma-alert.on { color:var(--accent); }.layer-inline { display:flex; align-items:center; gap:12px; }.layer-inline .disabled { gap:2px; }.swatch { width:13px; height:3px; display:inline-block; background:var(--muted); }.tf-1d { background:#d96073; }.tf-1h { background:#efb643; }.manual { background:#65b7cf; }.drawings { background:#c4a36a; }.vwap { background:#b07ad6; }.horizontal { background:#9fb0c4; }.rth { background:rgba(44,41,36,.35); }
|
||||
.layer-group { padding:9px 0; border-bottom:1px solid var(--line); display:grid; gap:7px; }.layer-group label,.score-hidden { display:flex; align-items:center; gap:7px; font-size:11px; cursor:pointer; }.layer-group input,.score-hidden input { accent-color:var(--accent); }.ma-fold { margin:0; }.ma-fold > summary { display:flex; align-items:center; gap:7px; cursor:pointer; list-style:none; user-select:none; }.ma-fold > summary::-webkit-details-marker { display:none; }.ma-rows { display:grid; gap:5px; padding:8px 0 2px 4px; }.ma-row { display:grid; grid-template-columns:auto 1fr auto auto; align-items:center; gap:8px; font-size:11px; }.ma-row label { color:var(--muted); gap:6px; }.ma-row input { width:12px; height:12px; margin:0; flex:none; }.ma-value { font-variant-numeric:tabular-nums; color:var(--fg); text-align:right; }.ma-alert { display:grid; place-items:center; width:16px; height:16px; padding:0; border:0; background:transparent; color:var(--muted); font-size:9px; }.ma-alert.on { color:var(--accent); }.layer-inline { display:flex; align-items:center; gap:12px; }.layer-inline .disabled { gap:2px; }.swatch { width:13px; height:3px; display:inline-block; background:var(--muted); }.tf-1d { background:#d96073; }.tf-1h { background:#efb643; }.manual { background:#65b7cf; }.drawings { background:#c4a36a; }.vwap { background:#b07ad6; }.horizontal { background:#9fb0c4; }.rth { background:rgba(44,41,36,.35); }
|
||||
.hint { margin:6px 0 2px; font-size:10px; color:var(--muted); line-height:1.35; }
|
||||
|
||||
/* Tool palette: the head arms the tool, the body configures what it creates. */
|
||||
|
|
|
|||
Loading…
Reference in a new issue