The right column ran past the viewport with nothing selected. Five changes, none of which remove functionality. The five daily MA periods now share one line. 10px gaps and a 22px indent had pushed 200 onto a row by itself; nowrap, 7px gaps and 12px boxes fit all five with room to spare. Auto trendlines becomes a parenthetical on the Manual lines row instead of owning one — it is disabled until M8, so a full row overstated it. The alert log becomes a collapsible section like Confluence zones, closed by default with its count in the summary, so activity stays visible while it is shut and the banner, sound and phone push are untouched. Tools becomes a collapsible section as well, open by default, and each tool's panel is now bound to armedTool: the label, colour, width and side controls appear only for the tool actually armed. armTool already toggled and allowed one armed tool at a time, so the panels follow it with no new state. Layers moves above Tools and starts collapsed. The zeroed top margin moves from a Tools-specific class to .sidebar-section:first-of-type, so reordering again cannot reintroduce a gap above the first section. Measured with nothing armed: 1110px of content down to 900px, inside the viewport rather than past it. Verified in a browser — arming each tool reveals that tool's panel and no other, disarming hides both, Tools opens by default, Layers starts closed and still lays its periods out on one line when opened. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
136 lines
9.5 KiB
HTML
136 lines
9.5 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>/ES Confluence</title>
|
||
<link rel="stylesheet" href="/static/style.css">
|
||
<!-- Font Awesome Free 7.3.1, from unpkg like the other two dependencies.
|
||
Pinned deliberately: an unpinned icon set is a silent redesign on someone
|
||
else's release. cdnjs 404s on this path — it does not carry 7.3.1. -->
|
||
<link rel="stylesheet" href="https://unpkg.com/@fortawesome/fontawesome-free@7.3.1/css/all.min.css">
|
||
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
|
||
<script src="https://unpkg.com/lightweight-charts@5.2.0/dist/lightweight-charts.standalone.production.js"></script>
|
||
</head>
|
||
<body>
|
||
<div id="app">
|
||
<header>
|
||
<div><span class="eyebrow">CME FUTURES</span><h1>/ES <strong>CONFLUENCE</strong></h1></div>
|
||
<div class="status" :class="status.stream"><i></i>{{ status.stream }}<span v-if="status.delay_minutes"> ({{ status.delay_minutes }}min delay)</span> · {{ status.source || 'source' }}</div>
|
||
</header>
|
||
<main>
|
||
<section class="chart-shell">
|
||
<div class="chart-head">
|
||
<div><span class="symbol">{{ status.symbol || 'ES=F' }}</span><span class="price">{{ price == null ? '—' : price.toFixed(2) }}</span></div>
|
||
<div class="timeframes"><button v-for="tf in timeframes" :key="tf" :class="{active: timeframe === tf}" @click="selectTimeframe(tf)">{{ tf }}</button></div>
|
||
</div>
|
||
<div id="chart"></div>
|
||
<div class="statusbar">
|
||
<span>FEED <b>{{ status.stream }}</b></span>
|
||
<span>LAST BAR <b>{{ barAge }}</b></span>
|
||
<span>HELD <b>{{ status.bars_held?.[timeframe] || 0 }} {{ timeframe }}</b></span>
|
||
<span v-if="armedTool === 'trendline'" class="arm-hint">Drag on the chart from one point to the other</span>
|
||
<span v-else-if="armedTool === 'level'" class="arm-hint">Click or drag on the chart to set the price</span>
|
||
<span v-else-if="selectedLine" class="arm-hint">Line selected — Delete removes it</span>
|
||
</div>
|
||
</section>
|
||
<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"><label v-for="period in [10,20,50,100,200]" :key="period"><input type="checkbox" :value="period" v-model="prefs.enabled.ma['1d']">{{ period }}</label></div>
|
||
</div>
|
||
<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>
|
||
</div>
|
||
<div class="layer-group layer-inline">
|
||
<label><input type="checkbox" v-model="prefs.enabled.manual"><span class="swatch manual"></span>Manual lines</label>
|
||
<label class="disabled" title="Automatic trendline detection is not built yet">(<input type="checkbox" disabled>auto)</label>
|
||
</div>
|
||
<label class="score-hidden"><input type="checkbox" v-model="prefs.hidden_levels_score">Hidden levels still count toward confluence</label>
|
||
</details>
|
||
<details class="sidebar-section tools-section" open>
|
||
<summary>Tools</summary>
|
||
<div class="tool" :class="{armed: armedTool === 'trendline'}">
|
||
<button class="tool-head" @click="armTool('trendline')" :aria-pressed="armedTool === 'trendline'">
|
||
<span class="tool-glyph"><i class="fa-solid fa-arrow-trend-up"></i></span>Trendline
|
||
<span class="tool-state">{{ armedTool === 'trendline' ? 'drag on chart' : '' }}</span>
|
||
</button>
|
||
<div class="tool-body" v-show="armedTool === 'trendline'">
|
||
<label>Label<input v-model.trim="drawName" placeholder="optional" aria-label="Trendline label"></label>
|
||
<div class="row">
|
||
<label>Colour<input type="color" v-model="drawColor" aria-label="Trendline colour"></label>
|
||
<label>Width<select v-model.number="drawWidth" aria-label="Trendline width"><option v-for="width in [1,2,3,4]" :value="width">{{ width }}px</option></select></label>
|
||
</div>
|
||
<div class="row">
|
||
<label>Side<select v-model="drawSide" aria-label="Trendline side"><option value="support">Support</option><option value="resistance">Resistance</option></select></label>
|
||
<label class="check"><input type="checkbox" v-model="snap">Snap to highs/lows</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="tool" :class="{armed: armedTool === 'level'}">
|
||
<button class="tool-head" @click="armTool('level')" :aria-pressed="armedTool === 'level'">
|
||
<span class="tool-glyph"><i class="fa-solid fa-minus"></i></span>Price level
|
||
<span class="tool-state">{{ armedTool === 'level' ? 'click on chart' : '' }}</span>
|
||
</button>
|
||
<div class="tool-body" v-show="armedTool === 'level'">
|
||
<label>Label<input v-model.trim="alertNote" placeholder="optional" aria-label="Level label"></label>
|
||
<div class="row">
|
||
<label>Colour<input type="color" v-model="levelColor" aria-label="Level colour"></label>
|
||
<label>Width<select v-model.number="levelWidth" aria-label="Level width"><option v-for="width in [1,2,3,4]" :value="width">{{ width }}px</option></select></label>
|
||
</div>
|
||
<form class="row price-row" @submit.prevent="addPriceAlert">
|
||
<label>Price<input type="number" step="0.25" v-model.number="alertPrice" :placeholder="price == null ? '0.00' : price.toFixed(2)" aria-label="Level price"></label>
|
||
<button type="submit" :disabled="!alertPrice">Add</button>
|
||
</form>
|
||
<p class="hint">Drag on the chart, or type an exact price. Alerts whenever price reaches it, whatever the confluence score.</p>
|
||
</div>
|
||
</div>
|
||
</details>
|
||
<details class="sidebar-section" open>
|
||
<summary>Lines & levels ({{ manualLines.length }})</summary>
|
||
<div class="trendline-actions" v-if="manualLines.length">
|
||
<button @click="toggleSelectAll">{{ allManualSelected ? 'Clear' : 'Select all' }}</button>
|
||
<button @click="deleteSelectedLines" :disabled="!selectedLines.length">Delete selected ({{ selectedLines.length }})</button>
|
||
</div>
|
||
<div v-if="!manualLines.length" class="empty">No manual trendlines.</div>
|
||
<div v-for="line in manualLines" :key="line.id" class="trendline-row" :class="{selected: selectedLines.includes(line.id)}" @click="toggleLineSelection(line.id)">
|
||
<input class="line-select" type="checkbox" :checked="selectedLines.includes(line.id)" :aria-label="`Select trendline ${line.number}`" @click.stop @change="toggleLineSelection(line.id)">
|
||
<input :value="line.label" aria-label="Trendline name" @click.stop @change="renameLine(line, $event.target.value)">
|
||
<span v-if="line.slope === 0">#{{ line.number }} · level {{ line.anchor_p.toFixed(2) }}</span>
|
||
<span v-else>#{{ line.number }} · {{ line.tf }} · {{ line.side }}</span>
|
||
<label class="armed-toggle" :class="{off: !line.armed}" @click.stop :title="line.armed ? 'Armed — will alert once, then disarm' : 'Tripped — re-arm to alert again'">
|
||
<input type="checkbox" :checked="line.armed" @change="setArmed(line, $event.target.checked)">
|
||
{{ line.armed ? 'armed' : 'tripped' }}
|
||
</label>
|
||
<button @click.stop="deleteLine(line.id)" aria-label="Delete trendline">Delete</button>
|
||
<div class="line-style-controls" @click.stop>
|
||
<input type="color" :value="line.color || '#65b7cf'" aria-label="Trendline color" @change="updateLineStyle(line, { color: $event.target.value })">
|
||
<select :value="line.line_width || 2" aria-label="Trendline width" @change="updateLineStyle(line, { line_width: Number($event.target.value) })"><option v-for="width in [1,2,3,4]" :value="width">{{ width }}px</option></select>
|
||
</div>
|
||
</div>
|
||
</details>
|
||
<details class="sidebar-section">
|
||
<summary>Confluence zones</summary>
|
||
<div v-if="!clusters.length" class="empty">No active zones near current structure.</div>
|
||
<div v-for="cluster in clusters" :key="cluster.id" class="cluster" :class="cluster.side">
|
||
<div class="cluster-top"><b>{{ cluster.side }}</b><strong>{{ cluster.score.toFixed(1) }}</strong></div>
|
||
<div class="zone">{{ cluster.low.toFixed(2) }} – {{ cluster.high.toFixed(2) }}</div>
|
||
<div class="members">{{ cluster.members.map(member => member.label).join(' · ') }}</div>
|
||
<div class="distance">{{ cluster.distance > 0 ? '+' : '' }}{{ cluster.distance.toFixed(2) }} pts</div>
|
||
</div>
|
||
</details>
|
||
<details class="sidebar-section">
|
||
<summary>Alert log ({{ alerts.length }})</summary>
|
||
<div v-if="!alerts.length" class="empty">No alerts fired.</div>
|
||
<div v-for="alert in alerts" :key="alert.key" class="alert-entry"><time>{{ alert.at }}</time>{{ alert.message }}</div>
|
||
</details>
|
||
</aside>
|
||
</main>
|
||
</div>
|
||
<script src="/static/chart.js"></script>
|
||
<script src="/static/app.js"></script>
|
||
</body>
|
||
</html>
|