initial commit: reforgermods.net launcher 0.1.0
A desktop server browser for Arma Reforger, built on the reforgermods.net v2 API. Live server list with search, sort and filters; a detail inspector with population history and mod readiness; measured ICMP latency; favorites, recents and settings; and direct joining through Steam. Tauri 2 and Rust, with a Preact frontend that builds to 80 KB of JavaScript in one chunk. Logic lives in rfm-core, which has no Tauri dependency and is tested directly; src-tauri is the window, the command surface and the wiring. Packaged for the public beta: a per-user NSIS installer, a portable .exe and an Arch package, with signature-verified background updates on Windows and a release pipeline that builds from a tag. The artifacts are not yet Authenticode-signed — that is wired and waiting on an Azure Trusted Signing identity, and docs/distribution.md says so plainly rather than letting anyone discover it at the SmartScreen dialog. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
91 files changed
+24994
No files matched your search
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Population history for the selected server.
|
||||
*
|
||||
* One inline SVG, no charting library: a min–max band, an average line, and a
|
||||
* range switcher. The paths come from `lib/chart.ts`, which is pure and tested.
|
||||
*/
|
||||
|
||||
import { historyLoading, historyRange, historySeries, setHistoryRange } from "../lib/state";
|
||||
import { buildChart } from "../lib/chart";
|
||||
import { HISTORY_RANGES } from "../lib/types";
|
||||
|
||||
/** Drawing box. `preserveAspectRatio="none"` stretches it to the panel width. */
|
||||
const WIDTH = 300;
|
||||
const HEIGHT = 56;
|
||||
|
||||
export function HistoryChart() {
|
||||
const series = historySeries.value;
|
||||
const loading = historyLoading.value;
|
||||
const range = historyRange.value;
|
||||
const geometry = series ? buildChart(series.points, WIDTH, HEIGHT) : null;
|
||||
|
||||
return (
|
||||
<div class="section">
|
||||
<div class="section__h">
|
||||
<span>Population</span>
|
||||
<div class="rangeswitch">
|
||||
{HISTORY_RANGES.map((option) => (
|
||||
<button
|
||||
key={option.value}
|
||||
data-on={option.value === range}
|
||||
onClick={() => setHistoryRange(option.value)}
|
||||
>
|
||||
{option.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{geometry ? (
|
||||
<>
|
||||
<svg
|
||||
class="chart"
|
||||
viewBox={`0 0 ${WIDTH} ${HEIGHT}`}
|
||||
preserveAspectRatio="none"
|
||||
role="img"
|
||||
aria-label={`Population over the last ${range}: peak ${geometry.peak}, average ${geometry.mean}`}
|
||||
>
|
||||
{/* Band first so the average line sits on top of it. */}
|
||||
<path class="chart__band" d={geometry.band} />
|
||||
{/* non-scaling-stroke keeps the line 1px despite the stretched viewBox. */}
|
||||
<path class="chart__line" d={geometry.line} vector-effect="non-scaling-stroke" />
|
||||
</svg>
|
||||
<div class="chart__legend">
|
||||
<span>
|
||||
peak <b>{geometry.peak}</b>
|
||||
</span>
|
||||
<span>
|
||||
avg <b>{geometry.mean}</b>
|
||||
</span>
|
||||
<span>
|
||||
low <b>{geometry.low}</b>
|
||||
</span>
|
||||
{geometry.cap > 0 && <span>of {geometry.cap}</span>}
|
||||
<span class="chart__bucket">{series?.bucket} buckets</span>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<p class="state__hint" style={{ margin: 0 }}>
|
||||
{loading ? "Loading history…" : "No recorded history for this range."}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user