kimi-k2.6 · go
go
- config
- kimi-k2.6--opencode-go--dev
- batch
- 2026-07-19--unified
- transport
- opencode-go-gateway
- protocol
- api · plan
- served model
- kimi-k2.6
- captured
- Jul 18, 2026, 11:54 AM · N=2
variant
live
slot 0 · P-q · sandboxed iframe

The screenshot is the exact frozen pixels the similarity channels measured. Run live re-executes the raw output in a sandboxed iframe; its rendering depends on your browser and fonts and is not guaranteed to match the measured pixels.
similarity neighbors
nearest
claude-fable-5 @low · CC0.70claude-haiku-4-5 @low · CC0.66gpt-5.6-luna @low · codex-oauth0.66claude-fable-5 @high · CC0.64gpt-5.5 @low · codex-oauth0.64farthest
gpt-5.6-sol @medium · codex-oauth0.38gpt-5.4 @low · codex-oauth0.38gpt-5.6-terra @low · codex-oauth0.41claude-opus-4-6 @low · kiro0.41gpt-5.6-luna @medium · kiro0.42within session 2026-07-19--unified
similarity measures convergence within this session — not quality.
Fidelity — does the card show the right data?
- namematch
- conditionmatch
- datematch
- max tempnot-foundno-temperature-candidate
- min tempnot-foundno-temperature-candidate
- hourlynot-foundcoverage · 0 match · 0 mismatch · 24 not shown
- request sidematch0 violation(s)
extractor fidelity-extractor-v1+Berlin-2026-07-15
Source — raw model output, one byte unchanged
download card.html<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=1280, initial-scale=1.0">
<title>Weather Card</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--bg-1: #e8f4f8;
--bg-2: #dcecf2;
--bg-3: #f0e8dc;
--card-bg: rgba(255, 255, 255, 0.65);
--card-border: rgba(255, 255, 255, 0.8);
--card-shadow: 0 24px 80px rgba(0, 20, 60, 0.08), 0 8px 24px rgba(0, 20, 60, 0.04);
--text-primary: #1a2a3a;
--text-secondary: #5a7a8a;
--text-muted: #8a9aa8;
--accent: #4a9db8;
--accent-warm: #c97a4a;
--temp-max: #c95a4a;
--temp-min: #4a9db8;
--error-bg: #fff0f0;
--error-text: #b04040;
--error-border: #e8b0b0;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Helvetica, Arial, sans-serif;
background: linear-gradient(135deg, var(--bg-1) 0%, var(--bg-2) 50%, var(--bg-3) 100%);
min-height: 800px;
width: 1280px;
overflow-x: hidden;
display: flex;
align-items: center;
justify-content: center;
color: var(--text-primary);
position: relative;
}
body::before {
content: '';
position: absolute;
top: -50%;
left: -20%;
width: 800px;
height: 800px;
background: radial-gradient(circle, rgba(74, 157, 184, 0.12) 0%, transparent 70%);
border-radius: 50%;
pointer-events: none;
}
body::after {
content: '';
position: absolute;
bottom: -30%;
right: -10%;
width: 600px;
height: 600px;
background: radial-gradient(circle, rgba(201, 122, 74, 0.10) 0%, transparent 70%);
border-radius: 50%;
pointer-events: none;
}
.container {
width: 100%;
max-width: 960px;
padding: 40px;
z-index: 1;
}
.weather-card {
background: var(--card-bg);
backdrop-filter: blur(24px) saturate(1.4);
-webkit-backdrop-filter: blur(24px) saturate(1.4);
border: 1px solid var(--card-border);
border-radius: 28px;
box-shadow: var(--card-shadow);
padding: 52px 56px 48px;
position: relative;
overflow: hidden;
transition: transform 0.4s ease, box-shadow 0.4s ease;
}
.weather-card:hover {
transform: translateY(-2px);
box-shadow: 0 28px 88px rgba(0, 20, 60, 0.10), 0 10px 28px rgba(0, 20, 60, 0.05);
}
.weather-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(90deg, var(--accent), var(--accent-warm), var(--accent));
border-radius: 28px 28px 0 0;
opacity: 0.7;
}
.header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 48px;
position: relative;
z-index: 1;
}
.location-block h1 {
font-size: 38px;
font-weight: 700;
letter-spacing: -0.5px;
color: var(--text-primary);
margin-bottom: 6px;
line-height: 1.1;
}
.location-block .date {
font-size: 17px;
color: var(--text-secondary);
font-weight: 500;
letter-spacing: 0.3px;
}
.condition-badge {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 20px;
background: rgba(74, 157, 184, 0.10);
border-radius: 50px;
border: 1px solid rgba(74, 157, 184, 0.15);
}
.condition-badge .weather-icon {
width: 28px;
height: 28px;
}
.condition-badge .condition-text {
font-size: 15px;
font-weight: 600;
color: var(--accent);
text-transform: capitalize;
}
.current-weather {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 52px;
position: relative;
z-index: 1;
}
.temp-main {
display: flex;
align-items: baseline;
gap: 6px;
}
.temp-main .temp-value {
font-size: 96px;
font-weight: 200;
letter-spacing: -4px;
line-height: 1;
color: var(--text-primary);
position: relative;
}
.temp-main .temp-value::after {
content: '°C';
font-size: 28px;
font-weight: 400;
letter-spacing: 0;
position: absolute;
top: 14px;
margin-left: 6px;
color: var(--text-secondary);
}
.temp-extremes {
display: flex;
flex-direction: column;
gap: 12px;
align-items: flex-end;
}
.extreme-item {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 18px;
background: rgba(255, 255, 255, 0.5);
border-radius: 14px;
border: 1px solid rgba(255, 255, 255, 0.6);
backdrop-filter: blur(8px);
}
.extreme-item .extreme-label {
font-size: 12px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1.2px;
color: var(--text-muted);
}
.extreme-item .extreme-value {
font-size: 22px;
font-weight: 600;
color: var(--text-primary);
min-width: 44px;
text-align: right;
}
.extreme-item .extreme-unit {
font-size: 14px;
color: var(--text-muted);
font-weight: 400;
}
.extreme-item.high .extreme-value {
color: var(--temp-max);
}
.extreme-item.low .extreme-value {
color: var(--temp-min);
}
.hourly-section {
position: relative;
z-index: 1;
}
.hourly-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20px;
}
.hourly-header h2 {
font-size: 14px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1.8px;
color: var(--text-muted);
}
.hourly-header .range-hint {
font-size: 13px;
color: var(--text-muted);
font-weight: 500;
}
.chart-container {
position: relative;
height: 220px;
width: 100%;
}
.chart-container canvas {
width: 100%;
height: 100%;
}
.loading {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 80px 40px;
gap: 24px;
}
.loading .spinner {
width: 44px;
height: 44px;
border: 3px solid rgba(74, 157, 184, 0.12);
border-top-color: var(--accent);
border-radius: 50%;
animation: spin 0.9s cubic-bezier(0.4, 0, 0.2, 1) infinite;
}
.loading .loading-text {
font-size: 15px;
color: var(--text-secondary);
font-weight: 500;
letter-spacing: 0.3px;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.error {
text-align: center;
padding: 72px 40px;
}
.error-icon {
width: 56px;
height: 56px;
margin: 0 auto 20px;
background: var(--error-bg);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid var(--error-border);
}
.error-icon svg {
width: 26px;
height: 26px;
stroke: var(--error-text);
}
.error h2 {
font-size: 22px;
font-weight: 600;
color: var(--error-text);
margin-bottom: 10px;
}
.error p {
font-size: 15px;
color: var(--text-muted);
line-height: 1.5;
max-width: 400px;
margin: 0 auto;
}
.error-retry {
margin-top: 24px;
padding: 10px 28px;
font-size: 14px;
font-weight: 600;
color: var(--accent);
background: rgba(74, 157, 184, 0.08);
border: 1.5px solid var(--accent);
border-radius: 10px;
cursor: pointer;
transition: all 0.2s ease;
font-family: inherit;
}
.error-retry:hover {
background: var(--accent);
color: #fff;
}
.hidden {
display: none !important;
}
/* Weather icon styles */
.weather-icon {
display: inline-block;
vertical-align: middle;
}
.weather-icon svg {
width: 100%;
height: 100%;
}
.condition-0, .condition-1, .condition-2, .condition-3 {
--icon-color: #d4a030;
}
.condition-45, .condition-48 {
--icon-color: #8a9aa8;
}
.condition-51, .condition-53, .condition-55,
.condition-56, .condition-57, .condition-61,
.condition-63, .condition-65, .condition-66,
.condition-67, .condition-80, .condition-81,
.condition-82 {
--icon-color: #5a8db0;
}
.condition-71, .condition-73, .condition-75,
.condition-77, .condition-85, .condition-86 {
--icon-color: #7aa0c0;
}
.condition-95, .condition-96, .condition-99 {
--icon-color: #8a70a0;
}
/* Decorative elements */
.deco-circle {
position: absolute;
border-radius: 50%;
pointer-events: none;
z-index: 0;
}
.deco-1 {
width: 300px;
height: 300px;
top: -100px;
right: -60px;
background: radial-gradient(circle, rgba(212, 160, 48, 0.06) 0%, transparent 70%);
}
.deco-2 {
width: 180px;
height: 180px;
bottom: 40px;
left: -40px;
background: radial-gradient(circle, rgba(74, 157, 184, 0.05) 0%, transparent 70%);
}
.tooltip {
position: absolute;
background: rgba(26, 42, 58, 0.92);
color: #fff;
padding: 6px 12px;
border-radius: 8px;
font-size: 12px;
font-weight: 600;
pointer-events: none;
white-space: nowrap;
opacity: 0;
transition: opacity 0.15s ease;
z-index: 10;
backdrop-filter: blur(4px);
}
.tooltip::after {
content: '';
position: absolute;
bottom: -4px;
left: 50%;
transform: translateX(-50%);
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 4px solid rgba(26, 42, 58, 0.92);
}
.tooltip.visible {
opacity: 1;
}
</style>
</head>
<body>
<div class="container">
<div class="weather-card" id="card">
<div class="deco-circle deco-1"></div>
<div class="deco-circle deco-2"></div>
<div id="loading" class="loading">
<div class="spinner"></div>
<div class="loading-text">Fetching weather data...</div>
</div>
<div id="error" class="error hidden">
<div class="error-icon">
<svg viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"/>
<line x1="12" y1="8" x2="12" y2="12"/>
<line x1="12" y1="16" x2="12.01" y2="16"/>
</svg>
</div>
<h2>Unable to load weather</h2>
<p id="error-message">The weather data could not be fetched. Please check your connection and try again.</p>
<button class="error-retry" onclick="loadWeather()">Retry</button>
</div>
<div id="content" class="hidden">
<div class="header">
<div class="location-block">
<h1 id="location-name">Berlin</h1>
<div class="date" id="display-date">Today</div>
</div>
<div class="condition-badge" id="condition-badge">
<div class="weather-icon" id="condition-icon"></div>
<span class="condition-text" id="condition-text">Clear Sky</span>
</div>
</div>
<div class="current-weather">
<div class="temp-main">
<div class="temp-value" id="current-temp">--</div>
</div>
<div class="temp-extremes">
<div class="extreme-item high">
<span class="extreme-label">High</span>
<span class="extreme-value" id="max-temp">--</span>
<span class="extreme-unit">°C</span>
</div>
<div class="extreme-item low">
<span class="extreme-label">Low</span>
<span class="extreme-value" id="min-temp">--</span>
<span class="extreme-unit">°C</span>
</div>
</div>
</div>
<div class="hourly-section">
<div class="hourly-header">
<h2>Hourly Temperature</h2>
<span class="range-hint" id="range-hint">00:00 — 23:00</span>
</div>
<div class="chart-container" id="chart-container">
<canvas id="hourly-chart"></canvas>
<div class="tooltip" id="tooltip"></div>
</div>
</div>
</div>
</div>
</div>
<script>
(function() {
'use strict';
// --- Weather code mapping ---
const WMO_CODES = {
0: { label: 'Clear Sky', icon: 'sun' },
1: { label: 'Mainly Clear', icon: 'sun-cloud' },
2: { label: 'Partly Cloudy', icon: 'cloud-sun' },
3: { label: 'Overcast', icon: 'cloud' },
45: { label: 'Fog', icon: 'fog' },
48: { label: 'Rime Fog', icon: 'fog' },
51: { label: 'Light Drizzle', icon: 'rain-light' },
53: { label: 'Drizzle', icon: 'rain-light' },
55: { label: 'Heavy Drizzle', icon: 'rain' },
56: { label: 'Freezing Drizzle', icon: 'rain-light' },
57: { label: 'Freezing Drizzle', icon: 'rain' },
61: { label: 'Light Rain', icon: 'rain-light' },
63: { label: 'Rain', icon: 'rain' },
65: { label: 'Heavy Rain', icon: 'rain-heavy' },
66: { label: 'Freezing Rain', icon: 'rain-light' },
67: { label: 'Freezing Rain', icon: 'rain' },
71: { label: 'Light Snow', icon: 'snow-light' },
73: { label: 'Snow', icon: 'snow' },
75: { label: 'Heavy Snow', icon: 'snow-heavy' },
77: { label: 'Snow Grains', icon: 'snow-light' },
80: { label: 'Light Showers', icon: 'rain-light' },
81: { label: 'Showers', icon: 'rain' },
82: { label: 'Heavy Showers', icon: 'rain-heavy' },
85: { label: 'Snow Showers', icon: 'snow-light' },
86: { label: 'Snow Showers', icon: 'snow' },
95: { label: 'Thunderstorm', icon: 'thunder' },
96: { label: 'Thunderstorm & Hail', icon: 'thunder' },
99: { label: 'Thunderstorm & Hail', icon: 'thunder' }
};
const ICONS = {
sun: `<svg viewBox="0 0 64 64" fill="none">
<circle cx="32" cy="32" r="14" fill="currentColor" opacity="0.9"/>
<g stroke="currentColor" stroke-width="3" stroke-linecap="round" opacity="0.7">
<line x1="32" y1="6" x2="32" y2="12"/>
<line x1="32" y1="52" x2="32" y2="58"/>
<line x1="6" y1="32" x2="12" y2="32"/>
<line x1="52" y1="32" x2="58" y2="32"/>
<line x1="13.6" y1="13.6" x2="17.8" y2="17.8"/>
<line x1="46.2" y1="46.2" x2="50.4" y2="50.4"/>
<line x1="13.6" y1="50.4" x2="17.8" y2="46.2"/>
<line x1="46.2" y1="17.8" x2="50.4" y2="13.6"/>
</g>
</svg>`,
'sun-cloud': `<svg viewBox="0 0 64 64" fill="none">
<circle cx="24" cy="24" r="10" fill="currentColor" opacity="0.6"/>
<path d="M20 44h28a12 12 0 0 0 0-24 11 11 0 0 0-20.5 4A10 10 0 0 0 20 44z" fill="currentColor" opacity="0.85"/>
</svg>`,
'cloud-sun': `<svg viewBox="0 0 64 64" fill="none">
<circle cx="20" cy="20" r="8" fill="currentColor" opacity="0.4"/>
<path d="M16 46h32a10 10 0 0 0 0-20 9 9 0 0 0-16.5 3A8 8 0 0 0 16 46z" fill="currentColor" opacity="0.8"/>
</svg>`,
cloud: `<svg viewBox="0 0 64 64" fill="none">
<path d="M16 42h32a10 10 0 0 0 0-20 9 9 0 0 0-16.5 3A8 8 0 0 0 16 42z" fill="currentColor" opacity="0.8"/>
</svg>`,
fog: `<svg viewBox="0 0 64 64" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" opacity="0.6">
<line x1="10" y1="24" x2="54" y2="24"/>
<line x1="14" y1="34" x2="50" y2="34"/>
<line x1="10" y1="44" x2="54" y2="44"/>
</svg>`,
'rain-light': `<svg viewBox="0 0 64 64" fill="none">
<path d="M16 34h32a10 10 0 0 0 0-20 9 9 0 0 0-16.5 3A8 8 0 0 0 16 34z" fill="currentColor" opacity="0.7"/>
<g stroke="currentColor" stroke-width="2.5" stroke-linecap="round" opacity="0.6">
<line x1="24" y1="40" x2="22" y2="48"/>
<line x1="32" y1="40" x2="30" y2="48"/>
<line x1="40" y1="40" x2="38" y2="48"/>
</g>
</svg>`,
rain: `<svg viewBox="0 0 64 64" fill="none">
<path d="M16 32h32a10 10 0 0 0 0-20 9 9 0 0 0-16.5 3A8 8 0 0 0 16 32z" fill="currentColor" opacity="0.7"/>
<g stroke="currentColor" stroke-width="2.5" stroke-linecap="round" opacity="0.7">
<line x1="22" y1="38" x2="19" y2="48"/>
<line x1="30" y1="38" x2="27" y2="48"/>
<line x1="38" y1="38" x2="35" y2="48"/>
<line x1="26" y1="42" x2="23" y2="52"/>
<line x1="34" y1="42" x2="31" y2="52"/>
</g>
</svg>`,
'rain-heavy': `<svg viewBox="0 0 64 64" fill="none">
<path d="M14 30h36a11 11 0 0 0 0-22 10 10 0 0 0-18 3A9 9 0 0 0 14 30z" fill="currentColor" opacity="0.7"/>
<g stroke="currentColor" stroke-width="2.5" stroke-linecap="round" opacity="0.8">
<line x1="20" y1="36" x2="17" y2="48"/>
<line x1="28" y1="36" x2="25" y2="48"/>
<line x1="36" y1="36" x2="33" y2="48"/>
<line x1="44" y1="36" x2="41" y2="48"/>
<line x1="24" y1="40" x2="21" y2="52"/>
<line x1="32" y1="40" x2="29" y2="52"/>
<line x1="40" y1="40" x2="37" y2="52"/>
</g>
</svg>`,
'snow-light': `<svg viewBox="0 0 64 64" fill="none">
<path d="M16 34h32a10 10 0 0 0 0-20 9 9 0 0 0-16.5 3A8 8 0 0 0 16 34z" fill="currentColor" opacity="0.7"/>
<g fill="currentColor" opacity="0.7">
<circle cx="24" cy="44" r="2.5"/>
<circle cx="32" cy="48" r="2.5"/>
<circle cx="40" cy="44" r="2.5"/>
</g>
</svg>`,
snow: `<svg viewBox="0 0 64 64" fill="none">
<path d="M16 32h32a10 10 0 0 0 0-20 9 9 0 0 0-16.5 3A8 8 0 0 0 16 32z" fill="currentColor" opacity="0.7"/>
<g fill="currentColor" opacity="0.7">
<circle cx="22" cy="40" r="2.5"/>
<circle cx="30" cy="44" r="2.5"/>
<circle cx="38" cy="40" r="2.5"/>
<circle cx="26" cy="50" r="2"/>
<circle cx="34" cy="50" r="2"/>
</g>
</svg>`,
'snow-heavy': `<svg viewBox="0 0 64 64" fill="none">
<path d="M14 30h36a11 11 0 0 0 0-22 10 10 0 0 0-18 3A9 9 0 0 0 14 30z" fill="currentColor" opacity="0.7"/>
<g fill="currentColor" opacity="0.7">
<circle cx="20" cy="38" r="2.5"/>
<circle cx="28" cy="42" r="2.5"/>
<circle cx="36" cy="42" r="2.5"/>
<circle cx="44" cy="38" r="2.5"/>
<circle cx="24" cy="48" r="2"/>
<circle cx="32" cy="52" r="2"/>
<circle cx="40" cy="48" r="2"/>
</g>
</svg>`,
thunder: `<svg viewBox="0 0 64 64" fill="none">
<path d="M14 30h36a11 11 0 0 0 0-22 10 10 0 0 0-18 3A9 9 0 0 0 14 30z" fill="currentColor" opacity="0.6"/>
<path d="M30 32l-4 14h6l-2 12 10-16h-6l4-10z" fill="currentColor" opacity="0.85"/>
</svg>`
};
// --- URL params ---
function getParams() {
const p = new URLSearchParams(window.location.search);
const today = new Date();
const yyyy = today.getUTCFullYear();
const mm = String(today.getUTCMonth() + 1).padStart(2, '0');
const dd = String(today.getUTCDate()).padStart(2, '0');
return {
lat: parseFloat(p.get('lat')) || 52.52,
lon: parseFloat(p.get('lon')) || 13.405,
name: p.get('name') || 'Berlin',
date: p.get('date') || `${yyyy}-${mm}-${dd}`
};
}
// --- Date formatting ---
function formatDate(dateStr) {
const d = new Date(dateStr + 'T00:00:00Z');
const opts = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', timeZone: 'UTC' };
return d.toLocaleDateString('en-US', opts);
}
function formatShortDate(dateStr) {
const d = new Date(dateStr + 'T00:00:00Z');
const opts = { month: 'short', day: 'numeric', timeZone: 'UTC' };
return d.toLocaleDateString('en-US', opts);
}
// --- Determine which API to use ---
function getApiEndpoint(dateStr) {
const requested = new Date(dateStr + 'T00:00:00Z');
const now = new Date();
const today = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
return requested < today ? '/api/om/archive' : '/api/om/forecast';
}
// --- Build API URL ---
function buildApiUrl(endpoint, lat, lon, dateStr) {
const base = endpoint;
const params = new URLSearchParams({
latitude: String(lat),
longitude: String(lon),
daily: 'temperature_2m_max,temperature_2m_min,weather_code',
hourly: 'temperature_2m',
timezone: 'UTC',
temperature_unit: 'celsius',
start_date: dateStr,
end_date: dateStr
});
return `${base}?${params.toString()}`;
}
// --- DOM refs ---
const els = {
loading: document.getElementById('loading'),
error: document.getElementById('error'),
content: document.getElementById('content'),
locationName: document.getElementById('location-name'),
displayDate: document.getElementById('display-date'),
conditionBadge: document.getElementById('condition-badge'),
conditionIcon: document.getElementById('condition-icon'),
conditionText: document.getElementById('condition-text'),
currentTemp: document.getElementById('current-temp'),
maxTemp: document.getElementById('max-temp'),
minTemp: document.getElementById('min-temp'),
rangeHint: document.getElementById('range-hint'),
canvas: document.getElementById('hourly-chart'),
tooltip: document.getElementById('tooltip'),
errorMessage: document.getElementById('error-message')
};
// --- State ---
let chartData = null;
let canvasRect = null;
// --- Main load function ---
window.loadWeather = async function() {
els.loading.classList.remove('hidden');
els.error.classList.add('hidden');
els.content.classList.add('hidden');
const params = getParams();
const endpoint = getApiEndpoint(params.date);
const url = buildApiUrl(endpoint, params.lat, params.lon, params.date);
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
if (!data.daily || !data.daily.temperature_2m_max || !data.daily.temperature_2m_max.length) {
throw new Error('No daily data available');
}
if (!data.hourly || !data.hourly.temperature_2m || !data.hourly.temperature_2m.length) {
throw new Error('No hourly data available');
}
render(params, data);
} catch (err) {
showError(err.message || 'Failed to load weather data');
}
};
function showError(msg) {
els.loading.classList.add('hidden');
els.error.classList.remove('hidden');
els.content.classList.add('hidden');
els.errorMessage.textContent = msg;
}
function render(params, data) {
// Location & date
els.locationName.textContent = params.name;
els.displayDate.textContent = formatDate(params.date);
// Daily values
const maxTemp = data.daily.temperature_2m_max[0];
const minTemp = data.daily.temperature_2m_min[0];
const wmoCode = data.daily.weather_code[0];
const wmoInfo = WMO_CODES[wmoCode] || { label: 'Unknown', icon: 'cloud' };
// Current temp: use noon (12:00 UTC) if available, otherwise first hour
const hourly = data.hourly;
const hourlyTimes = hourly.time;
const hourlyTemps = hourly.temperature_2m;
let currentTemp = hourlyTemps[0];
for (let i = 0; i < hourlyTimes.length; i++) {
if (hourlyTimes[i].endsWith('12:00')) {
currentTemp = hourlyTemps[i];
break;
}
}
els.currentTemp.textContent = Math.round(currentTemp);
els.maxTemp.textContent = Math.round(maxTemp);
els.minTemp.textContent = Math.round(minTemp);
// Condition
els.conditionText.textContent = wmoInfo.label;
els.conditionIcon.innerHTML = ICONS[wmoInfo.icon] || ICONS.cloud;
els.conditionBadge.className = `condition-badge condition-${wmoCode}`;
// Hourly range hint
if (hourlyTimes.length > 0) {
const first = hourlyTimes[0].slice(11, 16);
const last = hourlyTimes[hourlyTimes.length - 1].slice(11, 16);
els.rangeHint.textContent = `${first} — ${last}`;
}
// Prepare chart data
const chartPoints = [];
for (let i = 0; i < hourlyTimes.length; i++) {
const timeStr = hourlyTimes[i];
const hour = parseInt(timeStr.slice(11, 13), 10);
chartPoints.push({
hour: hour,
label: timeStr.slice(11, 16),
temp: hourlyTemps[i]
});
}
chartData = {
points: chartPoints,
min: Math.min(...hourlyTemps),
max: Math.max(...hourlyTemps)
};
// Show content
els.loading.classList.add('hidden');
els.error.classList.add('hidden');
els.content.classList.remove('hidden');
// Draw chart
requestAnimationFrame(() => {
drawChart();
});
}
// --- Canvas chart ---
function drawChart() {
const canvas = els.canvas;
const container = document.getElementById('chart-container');
const dpr = window.devicePixelRatio || 1;
const width = container.clientWidth;
const height = container.clientHeight;
canvas.width = width * dpr;
canvas.height = height * dpr;
canvas.style.width = width + 'px';
canvas.style.height = height + 'px';
const ctx = canvas.getContext('2d');
ctx.scale(dpr, dpr);
if (!chartData || chartData.points.length === 0) return;
const points = chartData.points;
const padLeft = 36;
const padRight = 24;
const padTop = 32;
const padBottom = 40;
const chartW = width - padLeft - padRight;
const chartH = height - padTop - padBottom;
// Y range with padding
const yMin = Math.floor(chartData.min - 2);
const yMax = Math.ceil(chartData.max + 2);
const yRange = yMax - yMin || 1;
function xFor(i) {
if (points.length === 1) return padLeft + chartW / 2;
return padLeft + (i / (points.length - 1)) * chartW;
}
function yFor(temp) {
return padTop + chartH - ((temp - yMin) / yRange) * chartH;
}
// Clear
ctx.clearRect(0, 0, width, height);
// Grid lines (horizontal)
const gridSteps = 4;
ctx.strokeStyle = 'rgba(138, 154, 168, 0.15)';
ctx.lineWidth = 1;
ctx.setLineDash([4, 4]);
for (let s = 0; s <= gridSteps; s++) {
const y = padTop + (s / gridSteps) * chartH;
ctx.beginPath();
ctx.moveTo(padLeft, y);
ctx.lineTo(padLeft + chartW, y);
ctx.stroke();
}
ctx.setLineDash([]);
// Y-axis labels
ctx.fillStyle = '#8a9aa8';
ctx.font = '500 11px -apple-system, BlinkMacSystemFont, sans-serif';
ctx.textAlign = 'right';
ctx.textBaseline = 'middle';
for (let s = 0; s <= gridSteps; s++) {
const tempVal = yMax - (s / gridSteps) * yRange;
const y = padTop + (s / gridSteps) * chartH;
ctx.fillText(Math.round(tempVal) + '°', padLeft - 10, y);
}
// X-axis labels (show every 4th or 3rd label depending on density)
const showEvery = points.length > 16 ? 4 : (points.length > 8 ? 3 : 2);
ctx.textAlign = 'center';
ctx.textBaseline = 'top';
for (let i = 0; i < points.length; i += showEvery) {
const x = xFor(i);
const y = padTop + chartH + 12;
ctx.fillText(points[i].label, x, y);
}
// Area fill under the curve
const areaPath = new Path2D();
areaPath.moveTo(xFor(0), padTop + chartH);
for (let i = 0; i < points.length; i++) {
areaPath.lineTo(xFor(i), yFor(points[i].temp));
}
areaPath.lineTo(xFor(points.length - 1), padTop + chartH);
areaPath.closePath();
const areaGrad = ctx.createLinearGradient(0, padTop, 0, padTop + chartH);
areaGrad.addColorStop(0, 'rgba(74, 157, 184, 0.18)');
areaGrad.addColorStop(1, 'rgba(74, 157, 184, 0.01)');
ctx.fillStyle = areaGrad;
ctx.fill(areaPath);
// Smooth curve
ctx.strokeStyle = 'rgba(74, 157, 184, 0.9)';
ctx.lineWidth = 2.5;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
// Catmull-Rom to cubic Bezier for smooth curves
ctx.beginPath();
if (points.length === 1) {
ctx.moveTo(xFor(0), yFor(points[0].temp));
} else {
ctx.moveTo(xFor(0), yFor(points[0].temp));
for (let i = 0; i < points.length - 1; i++) {
const p0 = points[Math.max(0, i - 1)];
const p1 = points[i];
const p2 = points[i + 1];
const p3 = points[Math.min(points.length - 1, i + 2)];
const x0 = xFor(Math.max(0, i - 1));
const x1 = xFor(i);
const x2 = xFor(i + 1);
const x3 = xFor(Math.min(points.length - 1, i + 2));
const tension = 0.4;
const cp1x = x1 + (x2 - x0) * tension / 3;
const cp1y = yFor(p1.temp) + (yFor(p2.temp) - yFor(p0.temp)) * tension / 3;
const cp2x = x2 - (x3 - x1) * tension / 3;
const cp2y = yFor(p2.temp) - (yFor(p3.temp) - yFor(p1.temp)) * tension / 3;
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x2, yFor(p2.temp));
}
}
ctx.stroke();
// Data points
for (let i = 0; i < points.length; i++) {
const x = xFor(i);
const y = yFor(points[i].temp);
// Outer glow
ctx.beginPath();
ctx.arc(x, y, 6, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(74, 157, 184, 0.12)';
ctx.fill();
// Point
ctx.beginPath();
ctx.arc(x, y, 3.5, 0, Math.PI * 2);
ctx.fillStyle = '#fff';
ctx.fill();
ctx.strokeStyle = 'rgba(74, 157, 184, 0.9)';
ctx.lineWidth = 2;
ctx.stroke();
}
// Store for tooltip
canvasRect = canvas.getBoundingClientRect();
}
// --- Tooltip ---
function updateTooltip(e) {
if (!chartData) return;
const canvas = els.canvas;
const rect = canvas.getBoundingClientRect();
const dpr = window.devicePixelRatio || 1;
// Mouse X relative to canvas content
const mouseX = (e.clientX - rect.left) * dpr;
const mouseY = (e.clientY - rect.top) * dpr;
const width = canvas.width;
const height = canvas.height;
const padLeft = 36 * dpr;
const padRight = 24 * dpr;
const padTop = 32 * dpr;
const padBottom = 40 * dpr;
const chartW = width - padLeft - padRight;
const chartH = height - padTop - padBottom;
// Find nearest point
const points = chartData.points;
let nearestIdx = 0;
let nearestDist = Infinity;
for (let i = 0; i < points.length; i++) {
const px = padLeft + (i / (points.length - 1 || 1)) * chartW;
const dist = Math.abs(mouseX - px);
if (dist < nearestDist) {
nearestDist = dist;
nearestIdx = i;
}
}
// Only show if close enough (within 30px)
if (nearestDist > 30 * dpr) {
els.tooltip.classList.remove('visible');
return;
}
const point = points[nearestIdx];
const px = padLeft + (nearestIdx / (points.length - 1 || 1)) * chartW;
const yMin = Math.floor(chartData.min - 2);
const yMax = Math.ceil(chartData.max + 2);
const yRange = yMax - yMin || 1;
const py = padTop + chartH - ((point.temp - yMin) / yRange) * chartH;
// Convert back to CSS pixels for tooltip positioning
const cssX = px / dpr;
const cssY = py / dpr;
els.tooltip.textContent = `${point.label} • ${Math.round(point.temp)}°C`;
els.tooltip.style.left = cssX + 'px';
els.tooltip.style.top = (cssY - 14) + 'px';
els.tooltip.style.transform = 'translateX(-50%) translateY(-100%)';
els.tooltip.classList.add('visible');
}
function hideTooltip() {
els.tooltip.classList.remove('visible');
}
els.canvas.addEventListener('mousemove', updateTooltip);
els.canvas.addEventListener('mouseleave', hideTooltip);
els.canvas.addEventListener('touchstart', (e) => {
e.preventDefault();
updateTooltip(e.touches[0]);
}, { passive: false });
els.canvas.addEventListener('touchend', hideTooltip);
// Redraw on resize
let resizeTimeout;
window.addEventListener('resize', () => {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(() => {
if (chartData) drawChart();
}, 100);
});
// Init
loadWeather();
})();
</script>
</body>
</html>L1 — cheap deterministic descriptors
| bytes total | 34,260 | html / css / js | 2,756 / 9,974 / 21,530 |
|---|---|---|---|
| dom nodes | 51 | dom depth | 9 |
| css rules | 58 | js present | yes |
| brightness | 237.2 | contrast | 12.3 |
| colorfulness | 13.1 | whitespace | 98.7% |
This slot's telemetry
| prompt tokens | 313 | completion tokens | 11,182 |
|---|---|---|---|
| total tokens | 11,495 | wall | 53.9 s |
| cost | — | request id | — |
config.json537 B
{
"N": 2,
"auth": {
"credential_ref": "WCB_OPENCODE_KEY",
"method": "oauth"
},
"billing": "plan",
"config_id": "kimi-k2.6--opencode-go--dev",
"effort": null,
"family": "kimi",
"m": {
"min": 2,
"q": 2
},
"model_id": "kimi-k2.6",
"protocol": "api",
"served_model": "kimi-k2.6",
"telemetry": {
"completion_tokens": 39604,
"cost_usd": null,
"prompt_tokens": 1236,
"total_tokens": 40840,
"wall_ms": 204097
},
"transport": "opencode-go-gateway",
"vendor_sanction_ref": null
}send-log2.4 KB
{
"N": 2,
"batch_id": "2026-07-19--go-requeue2",
"config_id": "kimi-k2.6--opencode-go--dev",
"positions": [
{
"attempts": [
{
"attempt_index": 0,
"backoff_ms": null,
"charged": true,
"ended_at": "2026-07-18T20:02:03.358075+00:00",
"http_status": 200,
"outcome": "valid",
"reached_model": true,
"reason": "ok",
"request_id": null,
"started_at": "2026-07-18T20:01:27.820269+00:00"
}
],
"block_index": 0,
"model_reaching_attempt_index": 0,
"slot_index": 0,
"terminal_state": "valid",
"variant": "P-min"
},
{
"attempts": [
{
"attempt_index": 0,
"backoff_ms": null,
"charged": true,
"ended_at": "2026-07-18T20:03:00.374693+00:00",
"http_status": 200,
"outcome": "valid",
"reached_model": true,
"reason": "ok",
"request_id": null,
"started_at": "2026-07-18T20:02:06.492774+00:00"
}
],
"block_index": 0,
"model_reaching_attempt_index": 0,
"slot_index": 0,
"terminal_state": "valid",
"variant": "P-q"
},
{
"attempts": [
{
"attempt_index": 0,
"backoff_ms": null,
"charged": true,
"ended_at": "2026-07-18T20:04:17.235104+00:00",
"http_status": 200,
"outcome": "valid",
"reached_model": true,
"reason": "ok",
"request_id": null,
"started_at": "2026-07-18T20:03:04.536665+00:00"
}
],
"block_index": 1,
"model_reaching_attempt_index": 0,
"slot_index": 1,
"terminal_state": "valid",
"variant": "P-q"
},
{
"attempts": [
{
"attempt_index": 0,
"backoff_ms": null,
"charged": true,
"ended_at": "2026-07-18T20:05:02.222045+00:00",
"http_status": 200,
"outcome": "valid",
"reached_model": true,
"reason": "ok",
"request_id": null,
"started_at": "2026-07-18T20:04:20.240252+00:00"
}
],
"block_index": 1,
"model_reaching_attempt_index": 0,
"slot_index": 1,
"terminal_state": "valid",
"variant": "P-min"
}
],
"retry_policy": {
"max_unreachable_retries": 5,
"rate_limit_backoff": {
"max_attempts": 4,
"max_total_ms": 60000
}
}
}