@giamme/forge 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -0
- package/README.md +106 -1766
- package/SKILL.md +22 -3
- package/VERSION +1 -1
- package/docs/README.md +26 -0
- package/docs/add-ons.md +33 -0
- package/docs/artifacts.md +84 -0
- package/docs/commands.md +365 -0
- package/docs/dataset.md +29 -0
- package/docs/decomposed-runs.md +310 -0
- package/docs/development.md +95 -0
- package/docs/forge-ui.md +128 -0
- package/docs/fractal.md +181 -0
- package/docs/install.md +160 -0
- package/docs/memory.md +156 -0
- package/docs/models.md +118 -0
- package/docs/reviews.md +245 -0
- package/docs/safety.md +55 -0
- package/docs/troubleshooting.md +54 -0
- package/forge +20 -0
- package/package.json +3 -2
- package/references/dataset.md +18 -1
- package/references/decompose.md +20 -2
- package/references/fractal.md +1 -1
- package/references/harnesses.md +18 -6
- package/references/solo.md +1 -1
- package/references/ui.md +27 -5
- package/scripts/forge-config.py +347 -0
- package/scripts/forge-control.py +645 -0
- package/scripts/forge-dispatch.sh +7 -1
- package/scripts/forge-issues.py +806 -0
- package/scripts/forge-lib-task.sh +234 -35
- package/scripts/forge-parallel-options.sh +3 -1
- package/scripts/forge-parallel.sh +3 -2
- package/scripts/forge-route.py +184 -27
- package/scripts/forge-runs-rm.py +840 -0
- package/scripts/forge-runs.py +139 -0
- package/scripts/forge-runtime.py +59 -6
- package/scripts/forge-schedule.py +227 -11
- package/scripts/forge-ui.py +85 -19
- package/scripts/forge-version.py +54 -12
- package/scripts/forge_dataset/__init__.py +77 -6
- package/scripts/forge_dataset/capture.py +5 -2
- package/scripts/forge_dataset/harvest.py +23 -1
- package/scripts/forge_dataset/importer.py +420 -18
- package/scripts/forge_fractal/__init__.py +2 -1
- package/scripts/forge_registry.py +168 -16
- package/ui/README.md +116 -23
- package/ui/THIRD_PARTY_NOTICES.md +108 -0
- package/ui/docs/api.md +283 -23
- package/ui/forge_ui/__main__.py +10 -2
- package/ui/forge_ui/control.py +575 -0
- package/ui/forge_ui/export.py +33 -9
- package/ui/forge_ui/fractal.py +1 -2
- package/ui/forge_ui/reader.py +121 -8
- package/ui/forge_ui/server.py +135 -5
- package/ui/forge_ui/static/D-DINCondensed-Bold.woff2 +0 -0
- package/ui/forge_ui/static/app.js +358 -131
- package/ui/forge_ui/static/cloud3d.js +3 -2
- package/ui/forge_ui/static/cloudui.js +8 -5
- package/ui/forge_ui/static/controls.js +619 -0
- package/ui/forge_ui/static/convo.js +295 -0
- package/ui/forge_ui/static/datatable.js +232 -0
- package/ui/forge_ui/static/diffview.js +29 -10
- package/ui/forge_ui/static/findings3d.js +134 -0
- package/ui/forge_ui/static/findingsview.js +412 -0
- package/ui/forge_ui/static/graph.js +3 -3
- package/ui/forge_ui/static/icons.js +94 -0
- package/ui/forge_ui/static/index.html +31 -13
- package/ui/forge_ui/static/iso.js +70 -19
- package/ui/forge_ui/static/leaderboard.js +44 -48
- package/ui/forge_ui/static/panel.js +92 -36
- package/ui/forge_ui/static/rundelete.js +388 -0
- package/ui/forge_ui/static/runmodel.js +17 -1
- package/ui/forge_ui/static/scene3d.js +518 -43
- package/ui/forge_ui/static/search.js +2 -1
- package/ui/forge_ui/static/settings.js +399 -0
- package/ui/forge_ui/static/shell.js +137 -0
- package/ui/forge_ui/static/stagectl.js +150 -0
- package/ui/forge_ui/static/style.css +1099 -469
- package/ui/forge_ui/static/theme.js +25 -0
- package/ui/forge_ui/static/uikit.js +161 -0
- package/ui/forge_ui/transcripts.py +608 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/* forge-ui icons: one hairline set drawn for forge-ui on a 24-unit grid, 1.5 stroke, round joins, currentColor.
|
|
2
|
+
States come from CSS (colour, opacity); an icon never carries its own colour. Built with createElementNS: the page CSP
|
|
3
|
+
refuses markup parsed from strings. Also the model name plate, the one way a model is shown everywhere.
|
|
4
|
+
window.ForgeIcons = { icon(name, title?) → <svg class="icon icon-<name>">, modelPlate(spec) → <span class="model-plate">, names } */
|
|
5
|
+
window.ForgeIcons = (() => {
|
|
6
|
+
'use strict';
|
|
7
|
+
const NS = 'http://www.w3.org/2000/svg';
|
|
8
|
+
const P = d => ['path', { d }], C = (cx, cy, r) => ['circle', { cx, cy, r }], R = (x, y, width, height, rx = 1.5) => ['rect', { x, y, width, height, rx }];
|
|
9
|
+
const SET = {
|
|
10
|
+
map: [R(3, 4, 18, 16, 2), P('M7 9h4v3H7zM13 9h4v6h-4zM7 14h4v2H7')],
|
|
11
|
+
cloud: [C(12, 12, 2.2), P('M3.5 12c0-2.5 3.8-4.5 8.5-4.5s8.5 2 8.5 4.5-3.8 4.5-8.5 4.5S3.5 14.5 3.5 12Z'), C(18.5, 9.2, 1)],
|
|
12
|
+
models: [P('M5 20V11M10 20V5M15 20v-7M20 20V8M3 20h18')],
|
|
13
|
+
search: [C(10.5, 10.5, 6), P('m15 15 5.5 5.5')],
|
|
14
|
+
bell: [P('M6 16.5V11a6 6 0 0 1 12 0v5.5l1.5 1.5h-15Z'), P('M10 20.5a2.2 2.2 0 0 0 4 0')],
|
|
15
|
+
live: [C(12, 12, 2), P('M7.8 7.8a6 6 0 0 0 0 8.4M16.2 7.8a6 6 0 0 1 0 8.4M5 5a10 10 0 0 0 0 14M19 5a10 10 0 0 1 0 14')],
|
|
16
|
+
sun: [C(12, 12, 3.8), P('M12 3v2M12 19v2M3 12h2M19 12h2M5.6 5.6 7 7M17 17l1.4 1.4M5.6 18.4 7 17M17 7l1.4-1.4')],
|
|
17
|
+
moon: [P('M19.5 14.5A7.5 7.5 0 0 1 9.5 4.5a7.5 7.5 0 1 0 10 10Z')],
|
|
18
|
+
system: [R(3, 4.5, 18, 12, 1.5), P('M9 20h6M12 16.5V20')],
|
|
19
|
+
immersive: [R(3, 4, 18, 16, 2), R(5.5, 6.5, 4, 8, 1), R(14.5, 6.5, 4, 11, 1)],
|
|
20
|
+
workbench: [R(3, 4, 18, 16, 2), P('M8 4v16M16 4v16M8 15h8')],
|
|
21
|
+
'panel-left': [R(3, 4, 18, 16, 2), P('M9 4v16')],
|
|
22
|
+
'panel-right': [R(3, 4, 18, 16, 2), P('M15 4v16')],
|
|
23
|
+
'panel-bottom': [R(3, 4, 18, 16, 2), P('M3 14h18')],
|
|
24
|
+
close: [P('M6 6l12 12M18 6 6 18')],
|
|
25
|
+
'chevron-down': [P('m6 9 6 6 6-6')],
|
|
26
|
+
'chevron-up': [P('m6 15 6-6 6 6')],
|
|
27
|
+
'chevron-right': [P('m9 6 6 6-6 6')],
|
|
28
|
+
'chevron-left': [P('m15 6-6 6 6 6')],
|
|
29
|
+
play: [P('M8 5.5v13l10-6.5Z')],
|
|
30
|
+
pause: [P('M8 5.5v13M16 5.5v13')],
|
|
31
|
+
plan: [P('M4 5v14M10 5v14M16 5v14'), R(5.5, 7, 3, 3, .8), R(11.5, 9, 3, 3, .8), R(11.5, 14, 3, 3, .8), R(17.5, 11, 3, 3, .8)],
|
|
32
|
+
exec: [P('M3 5v14'), R(5, 6, 8, 3, 1), R(9, 10.5, 10, 3, 1), R(6, 15, 6, 3, 1)],
|
|
33
|
+
files: [P('M6 3.5h8l4 4v13H6Z'), P('M14 3.5v4h4M9 12h6M9 15.5h6')],
|
|
34
|
+
cube: [P('M12 3 20 7.5v9L12 21l-8-4.5v-9Z'), P('M4 7.5 12 12l8-4.5M12 12v9')],
|
|
35
|
+
square: [R(5, 5, 14, 14, 1.5), P('M5 12h14M12 5v14')],
|
|
36
|
+
layers: [P('m12 4 9 4.5-9 4.5-9-4.5Z'), P('m3 12.5 9 4.5 9-4.5'), P('m3 16.5 9 4.5 9-4.5')],
|
|
37
|
+
fit: [P('M4 9V4h5M15 4h5v5M20 15v5h-5M9 20H4v-5'), R(9, 9, 6, 6, 1)],
|
|
38
|
+
persp: [P('M3.5 19 8.5 5h7l5 14Z'), P('M10.5 5 9.5 19M13.5 5l1 14M6 12h12')],
|
|
39
|
+
iso: [P('M12 4.5 18.5 8v8L12 19.5 5.5 16V8Z'), P('M5.5 8 12 11.5 18.5 8M12 11.5v8')],
|
|
40
|
+
top: [R(4, 4, 16, 16, 2), R(8.5, 8.5, 7, 7, 1)],
|
|
41
|
+
reset: [P('M4.5 12a7.5 7.5 0 1 0 2.2-5.3'), P('M4.5 4.5v3.5H8')],
|
|
42
|
+
'zoom-in': [C(11, 11, 6.5), P('m16 16 4.5 4.5M8.5 11h5M11 8.5v5')],
|
|
43
|
+
'zoom-out': [C(11, 11, 6.5), P('m16 16 4.5 4.5M8.5 11h5')],
|
|
44
|
+
fullscreen: [P('M4 9V4h5M15 4h5v5M20 15v5h-5M9 20H4v-5')],
|
|
45
|
+
legend: [C(6, 7, 1.5), C(6, 12, 1.5), C(6, 17, 1.5), P('M10 7h9M10 12h9M10 17h6')],
|
|
46
|
+
help: [C(12, 12, 9), P('M9.5 9.5a2.6 2.6 0 0 1 5 1c0 1.8-2.5 2.2-2.5 3.8'), C(12, 17, .6)],
|
|
47
|
+
sort: [P('M8 5v14M5 8l3-3 3 3M16 19V5M13 16l3 3 3-3')],
|
|
48
|
+
'sort-asc': [P('M12 19V5M7 10l5-5 5 5')],
|
|
49
|
+
'sort-desc': [P('M12 5v14M7 14l5 5 5-5')],
|
|
50
|
+
columns: [R(3, 4, 18, 16, 2), P('M9 4v16M15 4v16')],
|
|
51
|
+
grip: [C(9, 7, .9), C(15, 7, .9), C(9, 12, .9), C(15, 12, .9), C(9, 17, .9), C(15, 17, .9)],
|
|
52
|
+
download: [P('M12 4v11M7.5 10.5 12 15l4.5-4.5M5 19.5h14')],
|
|
53
|
+
flag: [P('M6 21V4M6 4.5h11l-2.5 4 2.5 4H6')],
|
|
54
|
+
check: [P('m5 12.5 4.5 4.5L19 7.5')],
|
|
55
|
+
cross: [P('M6 6l12 12M18 6 6 18')],
|
|
56
|
+
filter: [P('M4 5h16l-6 7.5V19l-4-2v-4.5Z')],
|
|
57
|
+
arrow: [P('M5 12h14M13 6l6 6-6 6')],
|
|
58
|
+
eye: [P('M2.5 12S6 5.5 12 5.5 21.5 12 21.5 12 18 18.5 12 18.5 2.5 12 2.5 12Z'), C(12, 12, 3)],
|
|
59
|
+
route: [C(5, 17, 2), C(19, 7, 2), P('M7 17h4l2-10h4')],
|
|
60
|
+
keyboard: [R(2.5, 6, 19, 12, 2), P('M6 10h1M9.5 10h1M13 10h1M16.5 10h1M7 14h10')],
|
|
61
|
+
chat: [P('M4 5.5h16v10.5H10l-4.5 3.5V16H4Z'), P('M8 9.5h8M8 12.5h5')],
|
|
62
|
+
terminal: [R(3, 4.5, 18, 15, 2), P('m7 9.5 3 2.5-3 2.5M12.5 15H17')],
|
|
63
|
+
pencil: [P('M4.5 19.5 5.5 15 15.5 5a2.1 2.1 0 0 1 3 3l-10 10Z'), P('m13.5 7 3 3')],
|
|
64
|
+
tool: [P('M14.5 5.5a4 4 0 0 0-5 5L4.5 15.5a2.1 2.1 0 0 0 3 3l5-5a4 4 0 0 0 5-5l-2.4 2.4-2.4-.6-.6-2.4Z')],
|
|
65
|
+
thought: [P('M7 15.5a5 5 0 1 1 8.5-3.5 4 4 0 0 1 2 7.5H8a3 3 0 0 1-1-4Z'), C(6, 20.5, .8)],
|
|
66
|
+
};
|
|
67
|
+
function icon(name, title) {
|
|
68
|
+
const svg = document.createElementNS(NS, 'svg');
|
|
69
|
+
svg.setAttribute('viewBox', '0 0 24 24');
|
|
70
|
+
svg.setAttribute('class', 'icon icon-' + name);
|
|
71
|
+
svg.setAttribute('focusable', 'false');
|
|
72
|
+
if (title) { const t = document.createElementNS(NS, 'title'); t.textContent = title; svg.append(t); svg.setAttribute('role', 'img'); }
|
|
73
|
+
else svg.setAttribute('aria-hidden', 'true');
|
|
74
|
+
for (const [tag, attrs] of SET[name] || SET.square) {
|
|
75
|
+
const n = document.createElementNS(NS, tag);
|
|
76
|
+
for (const [k, v] of Object.entries(attrs)) n.setAttribute(k, String(v));
|
|
77
|
+
svg.append(n);
|
|
78
|
+
}
|
|
79
|
+
return svg;
|
|
80
|
+
}
|
|
81
|
+
// a model's name plate: the alias engraved, the effort beside it ("luna:high" → LUNA | high); a bare id stays whole
|
|
82
|
+
function modelPlate(spec) {
|
|
83
|
+
const s = String(spec || '').trim(), i = s.lastIndexOf(':'), out = document.createElement('span');
|
|
84
|
+
out.className = 'model-plate'; out.title = s;
|
|
85
|
+
if (!s) { out.textContent = '—'; out.className = 'muted'; return out; }
|
|
86
|
+
const name = document.createElement('span'); name.className = 'mp-name'; name.textContent = i > 0 ? s.slice(0, i) : s; out.append(name);
|
|
87
|
+
if (i > 0 && i < s.length - 1) { // the colon stays in the text, for copying and screen readers
|
|
88
|
+
const c = document.createElement('span'); c.className = 'visually-hidden'; c.textContent = ':';
|
|
89
|
+
const e = document.createElement('span'); e.className = 'mp-effort'; e.textContent = s.slice(i + 1); out.append(c, e);
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
return { icon, modelPlate, names: Object.keys(SET) };
|
|
94
|
+
})();
|
|
@@ -13,39 +13,57 @@
|
|
|
13
13
|
} }
|
|
14
14
|
</script>
|
|
15
15
|
<link rel="icon" href="favicon.svg" type="image/svg+xml">
|
|
16
|
+
<link rel="preload" href="D-DINCondensed-Bold.woff2" as="font" type="font/woff2" crossorigin>
|
|
16
17
|
<link rel="stylesheet" href="style.css">
|
|
18
|
+
<script src="theme.js"></script>
|
|
19
|
+
<script src="icons.js" defer></script>
|
|
20
|
+
<script src="uikit.js" defer></script>
|
|
21
|
+
<script src="datatable.js" defer></script>
|
|
17
22
|
<script src="runmodel.js" defer></script>
|
|
18
23
|
<script src="cloudmodel.js" defer></script>
|
|
19
24
|
<script src="modelstats.js" defer></script>
|
|
20
25
|
<script src="graph.js" defer></script>
|
|
21
26
|
<script src="gantt.js" defer></script>
|
|
27
|
+
<script src="controls.js" defer></script>
|
|
22
28
|
<script src="panel.js" defer></script>
|
|
23
29
|
<script src="diffview.js" defer></script>
|
|
30
|
+
<script src="convo.js" defer></script>
|
|
24
31
|
<script src="files.js" defer></script>
|
|
25
32
|
<script src="map3d.js" defer></script>
|
|
26
33
|
<script src="cloudui.js" defer></script>
|
|
27
34
|
<script src="leaderboard.js" defer></script>
|
|
35
|
+
<script src="findingsview.js" defer></script>
|
|
36
|
+
<script src="rundelete.js" defer></script>
|
|
37
|
+
<script src="settings.js" defer></script>
|
|
28
38
|
<script src="search.js" defer></script>
|
|
29
39
|
<script src="notify.js" defer></script>
|
|
30
40
|
<script src="app.js" defer></script>
|
|
31
41
|
</head>
|
|
32
|
-
<body>
|
|
42
|
+
<body data-shell="immersive" data-runs="open" data-insp="open" data-tasks="open">
|
|
43
|
+
<script src="shell.js"></script>
|
|
33
44
|
<header class="topbar">
|
|
34
|
-
<a class="brand" href="#/">forge-ui</a>
|
|
35
|
-
<
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
<
|
|
45
|
+
<a class="brand" href="#/" title="forge-ui · local run viewer"><span class="brand-plate">forge</span><span class="brand-sub">ui</span></a>
|
|
46
|
+
<nav class="desk-nav" aria-label="Views">
|
|
47
|
+
<a class="top-link" href="#/">Map</a>
|
|
48
|
+
<a class="top-link" href="#/cloud">Cloud</a>
|
|
49
|
+
<a class="top-link" href="#/models">Models</a>
|
|
50
|
+
<a class="top-link" href="#/settings">Settings</a>
|
|
51
|
+
</nav>
|
|
52
|
+
<button class="top-search" type="button" data-jump title="Jump to a run, task, project, model or command (/)"><span class="top-search-label"><span>Search</span></span> <kbd>Ctrl K</kbd></button>
|
|
53
|
+
<div class="desk-tools">
|
|
54
|
+
<button id="bell" class="bell" type="button" aria-label="Notifications"><span class="bell-count" hidden></span></button>
|
|
55
|
+
<label class="refresh-control" title="Poll the runs for changes"><input id="refresh" type="checkbox" checked> Live</label>
|
|
56
|
+
</div>
|
|
42
57
|
</header>
|
|
43
58
|
<div id="errors" role="status" aria-live="polite" hidden></div>
|
|
44
59
|
<div class="layout">
|
|
45
|
-
<aside class="sidebar" aria-label="Runs">
|
|
46
|
-
<div class="
|
|
47
|
-
<
|
|
48
|
-
|
|
60
|
+
<aside class="sidebar module" aria-label="Runs" data-overlay>
|
|
61
|
+
<div class="module-head"><h1 class="module-title">Runs</h1><span id="run-count" class="module-count"></span><span class="module-tools"></span></div>
|
|
62
|
+
<div class="runs-tools">
|
|
63
|
+
<label class="visually-hidden" for="filter">Filter by goal, repo, ID, path or model</label>
|
|
64
|
+
<input id="filter" type="search" placeholder="Filter runs…" autocomplete="off">
|
|
65
|
+
<select id="run-sort" aria-label="Sort the runs"><option value="updated">Latest</option><option value="state">State</option><option value="repo">Repo</option></select>
|
|
66
|
+
</div>
|
|
49
67
|
<nav id="run-list" aria-label="Choose a run"><p class="empty">Loading runs…</p></nav>
|
|
50
68
|
</aside>
|
|
51
69
|
<main id="main" tabindex="-1"><p class="empty">Loading…</p></main>
|
|
@@ -41,14 +41,14 @@ const W = (x, y, z) => new THREE.Vector3(x * TS, z * TS, y * TS); // plan x ri
|
|
|
41
41
|
const MONO3 = 'ui-monospace, "SF Mono", Menlo, Consolas, monospace';
|
|
42
42
|
const NOISE = `float hash(vec2 p) { return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453); }`;
|
|
43
43
|
const CW = .61; // monospace advance, in em
|
|
44
|
-
//
|
|
44
|
+
// the iso figures' light and dark themes, tinted to forge-ui's enamel panel; style.css's --stage-* tokens win when set
|
|
45
45
|
const THEMES = {
|
|
46
|
-
dark: { bg:
|
|
47
|
-
light: { bg:
|
|
46
|
+
dark: { bg: 0x0e1210, body: 0x161c19, line: 0x4f5b54, major: 0x36413b, edge: 0x242c28, recess: 0x0b0e0c, sky: 0xe4efe6, ground: 0x070908, hemi: .7, amb: 0, sun: .7, fill: .2, fadeMin: .45 },
|
|
47
|
+
light: { bg: 0xc6cec4, body: 0xf3f5f0, line: 0x454f48, major: 0x6f7a72, edge: 0x9aa49b, recess: 0xdce1d9, sky: 0xffffff, ground: 0xdfe5dc, hemi: .9, amb: 1.6, sun: .35, fill: .15, fadeMin: .85 },
|
|
48
48
|
};
|
|
49
49
|
const PAL = {
|
|
50
|
-
light: { line: '#
|
|
51
|
-
dark: { line: '#
|
|
50
|
+
light: { line: '#6f7a72', detail: '#a9b2a8', recess: '#dce1d9', ink: '#2c342f', inkHi: '#111613', glassOff: '#e3e7df', glassOn: '#f6ead0', bg: '#f3f5f0' },
|
|
51
|
+
dark: { line: '#4f5b54', detail: '#28302c', recess: '#0b0e0c', ink: '#86928b', inkHi: '#e4e8e2', glassOff: '#121714', glassOn: '#221d0e', bg: '#0e1210' },
|
|
52
52
|
};
|
|
53
53
|
|
|
54
54
|
/* ---------- text: wrap and fit monospace print into a box (archivision's figure.js) ---------- */
|
|
@@ -287,7 +287,7 @@ export async function createEngine(host, { reduceMotion = false } = {}) {
|
|
|
287
287
|
function palette() {
|
|
288
288
|
const C = PAL[theme], light = theme === 'light', hi = TMPC.copy(accent).lerp(new THREE.Color(light ? 0x000000 : 0xffffff), light ? .3 : .42);
|
|
289
289
|
PALC = { ...C, acc: '#' + accent.getHexString(), accHi: '#' + hi.getHexString(), light };
|
|
290
|
-
INKHI.set(C.inkHi); DETAIL.set(
|
|
290
|
+
INKHI.set(C.inkHi); DETAIL.set(C.detail); GLASSON.set(C.glassOn);
|
|
291
291
|
}
|
|
292
292
|
palette();
|
|
293
293
|
const setOp = (m, o) => { m.opacity = o; const t = o < .999; if (m.transparent !== t) { m.transparent = t; m.needsUpdate = true; } };
|
|
@@ -391,18 +391,25 @@ export async function createEngine(host, { reduceMotion = false } = {}) {
|
|
|
391
391
|
const probe = document.createElement('span');
|
|
392
392
|
probe.className = 'stage-probe'; probe.setAttribute('aria-hidden', 'true'); host.append(probe);
|
|
393
393
|
const COLORS = new Map();
|
|
394
|
+
// a custom property's colour, resolved by the browser (tokens are light-dark() pairs, so the raw value is not a colour)
|
|
394
395
|
function cssColor(className, prop) {
|
|
395
396
|
const key = className + '|' + prop;
|
|
396
397
|
if (!COLORS.has(key)) {
|
|
397
398
|
probe.className = 'stage-probe ' + className;
|
|
398
|
-
|
|
399
|
+
probe.style.setProperty('color', `var(${prop})`);
|
|
400
|
+
const cs = getComputedStyle(probe), v = cs.getPropertyValue(prop).trim() ? cs.color : '';
|
|
399
401
|
let c = null; try { if (v) c = new THREE.Color(v); } catch (_) { c = null; }
|
|
400
402
|
COLORS.set(key, c);
|
|
401
403
|
}
|
|
402
404
|
return COLORS.get(key);
|
|
403
405
|
}
|
|
404
|
-
|
|
405
|
-
const
|
|
406
|
+
// a state's lamp as lit on the plate (--st-lit), and as ink for print on it (--st)
|
|
407
|
+
const stClass = s => 'st-' + String(s || 'unknown').toLowerCase().replace(/[^a-z0-9_-]/g, '');
|
|
408
|
+
const statusColor = s => cssColor(stClass(s), '--st-lit') || cssColor(stClass(s), '--st') || new THREE.Color('#888888');
|
|
409
|
+
const statusHex = s => '#' + (cssColor(stClass(s), '--st') || statusColor(s)).getHexString();
|
|
410
|
+
// a finding severity's colour (--sev on .sev-P0…P3): a ramp of its own, never a status lamp
|
|
411
|
+
const sevColor = p => cssColor('sev-' + String(p).replace(/[^A-Za-z0-9]/g, ''), '--sev') || statusColor('unknown');
|
|
412
|
+
const sevHex = p => '#' + sevColor(p).getHexString();
|
|
406
413
|
|
|
407
414
|
/* ---------- theme: follows the page (prefers-color-scheme), colours from style.css ---------- */
|
|
408
415
|
const themeHooks = new Set();
|
|
@@ -413,9 +420,10 @@ export async function createEngine(host, { reduceMotion = false } = {}) {
|
|
|
413
420
|
const bg = cssColor('', '--stage-bg'), acc = cssColor('', '--stage-accent');
|
|
414
421
|
BG_COL.set(bg || T.bg); scene.background.copy(BG_COL);
|
|
415
422
|
if (acc) accent.copy(acc);
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
423
|
+
const tok = (name, d) => cssColor('', name) || new THREE.Color(d);
|
|
424
|
+
bodyMat.color.copy(tok('--stage-body', T.body));
|
|
425
|
+
LINE_COL.copy(tok('--stage-line', T.line)); MAJOR_COL.copy(tok('--stage-major', T.major)); EDGE_COL.copy(tok('--stage-edge', T.edge)); appliedKey = '';
|
|
426
|
+
recessMat.color.copy(tok('--stage-recess', T.recess));
|
|
419
427
|
accentLineMat.color.copy(accent);
|
|
420
428
|
hemi.color.set(T.sky); hemi.groundColor.set(T.ground); hemi.intensity = T.hemi; ambient.intensity = T.amb; sun.intensity = T.sun; fill.intensity = T.fill;
|
|
421
429
|
for (const m of glowMats) { m.blending = light ? THREE.NormalBlending : THREE.AdditiveBlending; m.uniforms.light.value = light ? 1 : 0; m.needsUpdate = true; m.visible = !light; }
|
|
@@ -425,16 +433,20 @@ export async function createEngine(host, { reduceMotion = false } = {}) {
|
|
|
425
433
|
for (const t of TXT) if (!t.gone) t.redraw();
|
|
426
434
|
invalidate();
|
|
427
435
|
}
|
|
436
|
+
// the page's theme: forge-ui's own switch (theme.js) when it is there, else the system setting
|
|
428
437
|
const media = matchMedia('(prefers-color-scheme: light)');
|
|
429
|
-
const
|
|
438
|
+
const resolved = () => window.ForgeTheme ? window.ForgeTheme.resolved() : media.matches ? 'light' : 'dark';
|
|
439
|
+
const onScheme = () => setTheme(resolved());
|
|
430
440
|
media.addEventListener('change', onScheme);
|
|
441
|
+
window.addEventListener('forge-theme', onScheme);
|
|
431
442
|
|
|
432
443
|
function applyWeight() {
|
|
433
444
|
const dist = camera.position.distanceTo(controls.target);
|
|
434
445
|
const ppm = camera === ortho ? VH * ortho.zoom / orthoH : VH / (dist * fovK());
|
|
435
446
|
const k = THREE.MathUtils.clamp((ppm / 40 - .35) / .65, 0, 1);
|
|
436
447
|
const fm = THEMES[theme].fadeMin, fade = fm + (1 - fm) * k, dpr = renderer.getPixelRatio();
|
|
437
|
-
|
|
448
|
+
// orthographic zoom too: a screen sprite's world size follows it
|
|
449
|
+
const key = fade.toFixed(2) + '|' + dpr + '|' + theme + '|' + (camera === ortho ? ortho.zoom.toFixed(4) : 'p'); if (key === appliedKey) return; appliedKey = key;
|
|
438
450
|
lineMat.linewidth = majorMat.linewidth = dpr; accentLineMat.linewidth = 1.6 * dpr;
|
|
439
451
|
lineMat.color.copy(BG_COL).lerp(EDGE_COL, fade);
|
|
440
452
|
majorMat.color.copy(BG_COL).lerp(MAJOR_COL, fade);
|
|
@@ -443,6 +455,8 @@ export async function createEngine(host, { reduceMotion = false } = {}) {
|
|
|
443
455
|
for (const f of weightHooks) f();
|
|
444
456
|
}
|
|
445
457
|
const weightHooks = new Set();
|
|
458
|
+
// screen pixels per plan unit at the point the camera looks at
|
|
459
|
+
const pxPerUnit = () => (camera === ortho ? VH * ortho.zoom / orthoH : VH / (camera.position.distanceTo(controls.target) * fovK())) * TS;
|
|
446
460
|
|
|
447
461
|
/* ---------- camera: frame a box, keeping the user's angle ---------- */
|
|
448
462
|
const fovK = () => 2 * Math.tan(THREE.MathUtils.degToRad(persp.fov / 2));
|
|
@@ -484,14 +498,33 @@ export async function createEngine(host, { reduceMotion = false } = {}) {
|
|
|
484
498
|
let D = .5;
|
|
485
499
|
for (const p0 of pts) {
|
|
486
500
|
const p = p0.clone().sub(c), px = p.dot(r), py = p.dot(u), pz = p.dot(f);
|
|
487
|
-
D = Math.max(D, Math.abs(px) / (th * fw * .
|
|
501
|
+
D = Math.max(D, Math.abs(px) / (th * fw * .97) - pz, Math.abs(py) / (tv * fh * .95) - pz);
|
|
488
502
|
}
|
|
489
503
|
// what is framed sits in the middle of the uncovered part of the stage
|
|
490
504
|
c.addScaledVector(r, -(I.l - I.r) / VW * D * th).addScaledVector(u, (I.t - I.b) / VH * D * tv);
|
|
491
505
|
return { c, D };
|
|
492
506
|
}
|
|
507
|
+
// the stage covered by modules floating over it (marked data-overlay): a tall one covers a side, a wide one a top or bottom
|
|
508
|
+
function overlayInset() {
|
|
509
|
+
const H = host.getBoundingClientRect(), I = { l: 0, r: 0, t: 0, b: 0 };
|
|
510
|
+
if (!H.width || !H.height) return I;
|
|
511
|
+
const full = document.fullscreenElement;
|
|
512
|
+
for (const n of document.querySelectorAll('[data-overlay]')) {
|
|
513
|
+
if (full && !full.contains(n)) continue; // full screen: the modules are behind the stage
|
|
514
|
+
const R = n.getBoundingClientRect();
|
|
515
|
+
if (!R.width || !R.height || R.right <= H.left || R.left >= H.right || R.bottom <= H.top || R.top >= H.bottom) continue;
|
|
516
|
+
const cl = Math.max(R.left, H.left), cr = Math.min(R.right, H.right), ct = Math.max(R.top, H.top), cb = Math.min(R.bottom, H.bottom);
|
|
517
|
+
if (cb - ct > H.height * .5) { if (cl - H.left < H.right - cr) I.l = Math.max(I.l, cr - H.left); else I.r = Math.max(I.r, H.right - cl); }
|
|
518
|
+
else if (ct - H.top < H.bottom - cb) I.t = Math.max(I.t, cb - H.top); else I.b = Math.max(I.b, H.bottom - ct);
|
|
519
|
+
}
|
|
520
|
+
return I;
|
|
521
|
+
}
|
|
522
|
+
// the last framing, redone when the stage changes size, until the user moves the camera themselves
|
|
523
|
+
let autoFrame = null;
|
|
493
524
|
function frameBox(bx, dur = 900, minEl = 26, dirOverride = null, inset = null) {
|
|
494
525
|
resize(); // the host may have just been shown or moved: fit its real size
|
|
526
|
+
autoFrame = { bx, minEl, dirOverride, inset };
|
|
527
|
+
if (!inset) inset = overlayInset();
|
|
495
528
|
let dir = dirOverride ? dirOverride.clone().normalize() : camera.position.clone().sub(controls.target);
|
|
496
529
|
dir = dir.lengthSq() > 1e-8 ? dir.normalize() : DIR_ISO.clone();
|
|
497
530
|
const el = Math.asin(THREE.MathUtils.clamp(dir.y, -1, 1)), lo = THREE.MathUtils.degToRad(minEl);
|
|
@@ -515,6 +548,22 @@ export async function createEngine(host, { reduceMotion = false } = {}) {
|
|
|
515
548
|
if (k >= 1) { controls.enableDamping = true; CAMT = null; }
|
|
516
549
|
return true;
|
|
517
550
|
}
|
|
551
|
+
// closer (k > 1) or farther (k < 1) toward what the camera looks at, as a short glide; the angle stays
|
|
552
|
+
function zoomBy(k, dur = 260) {
|
|
553
|
+
settleCam();
|
|
554
|
+
const off = camera.position.clone().sub(controls.target);
|
|
555
|
+
let zoom = null;
|
|
556
|
+
if (camera === ortho) zoom = THREE.MathUtils.clamp(ortho.zoom * k, .05, 40);
|
|
557
|
+
else off.setLength(THREE.MathUtils.clamp(off.length() / k, controls.minDistance || .01, controls.maxDistance));
|
|
558
|
+
autoFrame = null;
|
|
559
|
+
if (reduceMotion || dur <= 1) {
|
|
560
|
+
camera.position.copy(controls.target).add(off);
|
|
561
|
+
if (zoom) { ortho.zoom = zoom; ortho.updateProjectionMatrix(); }
|
|
562
|
+
invalidate(); return;
|
|
563
|
+
}
|
|
564
|
+
CAMT = { t0: performance.now(), dur, fromT: controls.target.clone(), fromO: camera.position.clone().sub(controls.target), fromZ: zoom ? ortho.zoom : null, to: { t: controls.target.clone(), off, zoom } };
|
|
565
|
+
controls.enableDamping = false; invalidate();
|
|
566
|
+
}
|
|
518
567
|
const cancelCam = () => { if (CAMT) { controls.enableDamping = true; CAMT = null; } };
|
|
519
568
|
const settleCam = () => { if (CAMT) { camStep(CAMT.t0 + CAMT.dur + 1); controls.update(); } }; // jump to where a glide ends (tests)
|
|
520
569
|
|
|
@@ -544,6 +593,7 @@ export async function createEngine(host, { reduceMotion = false } = {}) {
|
|
|
544
593
|
else if (!raf) last = 0;
|
|
545
594
|
}
|
|
546
595
|
controls.addEventListener('change', () => invalidate());
|
|
596
|
+
controls.addEventListener('start', () => { autoFrame = null; });
|
|
547
597
|
const onVisibility = () => { visible = document.visibilityState === 'visible'; if (visible) invalidate(); };
|
|
548
598
|
document.addEventListener('visibilitychange', onVisibility);
|
|
549
599
|
const io = typeof IntersectionObserver === 'function' ? new IntersectionObserver(es => { onScreen = es.some(e => e.isIntersecting); if (onScreen) invalidate(); }) : null;
|
|
@@ -556,6 +606,7 @@ export async function createEngine(host, { reduceMotion = false } = {}) {
|
|
|
556
606
|
const res = devRes();
|
|
557
607
|
outlineMat.uniforms.res.value.copy(res); lineMat.resolution.copy(res); majorMat.resolution.copy(res); accentLineMat.resolution.copy(res);
|
|
558
608
|
appliedKey = ''; invalidate();
|
|
609
|
+
if (autoFrame) { const f = autoFrame; frameBox(f.bx, 0, f.minEl, f.dirOverride, f.inset); }
|
|
559
610
|
}
|
|
560
611
|
const ro = typeof ResizeObserver === 'function' ? new ResizeObserver(resize) : null;
|
|
561
612
|
if (ro) ro.observe(host);
|
|
@@ -740,7 +791,7 @@ export async function createEngine(host, { reduceMotion = false } = {}) {
|
|
|
740
791
|
}
|
|
741
792
|
function dispose() {
|
|
742
793
|
disposed = true; if (raf) cancelAnimationFrame(raf); raf = 0;
|
|
743
|
-
media.removeEventListener('change', onScheme); document.removeEventListener('visibilitychange', onVisibility);
|
|
794
|
+
media.removeEventListener('change', onScheme); window.removeEventListener('forge-theme', onScheme); document.removeEventListener('visibilitychange', onVisibility);
|
|
744
795
|
if (io) io.disconnect(); if (ro) ro.disconnect();
|
|
745
796
|
controls.dispose(); disposeGroup(scene); target.dispose(); composer.dispose && composer.dispose(); renderer.dispose();
|
|
746
797
|
canvas.remove(); probe.remove();
|
|
@@ -756,9 +807,9 @@ export async function createEngine(host, { reduceMotion = false } = {}) {
|
|
|
756
807
|
glowPoints, softMat, fat, loopL, noRay, rbox, inset, seg, polyLine, cumLen, alongPts, moveGlow, roundRect,
|
|
757
808
|
mats, lmat, paint, lineCol, setOp, solid, loopAt, roofPlate, cone,
|
|
758
809
|
tplane, release, roofAt, frontAt, lines, rr, font, fit, wrap, ellipsis, pxIn,
|
|
759
|
-
ends, curve, arc, statusColor, statusHex, cssColor,
|
|
760
|
-
frameBox, fitFor, cancelCam, settleCam, setOrtho, isOrtho: () => camera === ortho, invalidate, tickers, themeHooks, weightHooks,
|
|
761
|
-
pick, pickHit, rayAt, project, handlers, disposeGroup, dispose, reduceMotion, now: () => NOW,
|
|
810
|
+
ends, curve, arc, statusColor, statusHex, sevColor, sevHex, cssColor,
|
|
811
|
+
frameBox, fitFor, zoomBy, cancelCam, settleCam, setOrtho, overlayInset, isOrtho: () => camera === ortho, invalidate, tickers, themeHooks, weightHooks,
|
|
812
|
+
pick, pickHit, rayAt, project, pxPerUnit, handlers, disposeGroup, dispose, reduceMotion, now: () => NOW,
|
|
762
813
|
setProfile, get profile() { return profile; }, unitSolid, solidFrom, disposeShared, sprite, outlineInstanced, mixColor,
|
|
763
814
|
LineSegments2, LineSegmentsGeometry, creaseLines,
|
|
764
815
|
};
|
|
@@ -63,8 +63,9 @@ window.ForgeLeaderboard = (() => {
|
|
|
63
63
|
function modelCell(alias, ctx, few) {
|
|
64
64
|
const td = el('td', undefined, 'lb-model');
|
|
65
65
|
const href = ctx.modelHref ? ctx.modelHref(alias) : null;
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
const plate = window.ForgeIcons ? window.ForgeIcons.modelPlate(alias) : el('span', alias, 'mono');
|
|
67
|
+
if (href) { const a = el('a', undefined, 'lb-alias'); a.href = href; a.title = 'show its runs in the cloud'; a.append(plate); td.append(a); }
|
|
68
|
+
else td.append(plate);
|
|
68
69
|
if (few) { const s = el('span', 'few', 'lb-few'); s.title = few; td.append(s); }
|
|
69
70
|
return td;
|
|
70
71
|
}
|
|
@@ -96,11 +97,6 @@ window.ForgeLeaderboard = (() => {
|
|
|
96
97
|
td.title = `input ${count(t.input)} (cached ${count(t.cached)}) · output ${count(t.output)} · recorded by ${count(used)} of ${plural(total, unit)}`;
|
|
97
98
|
return td;
|
|
98
99
|
}
|
|
99
|
-
function head(table, cols) {
|
|
100
|
-
const thead = el('thead'), tr = el('tr');
|
|
101
|
-
for (const [label, cls, title] of cols) { const th = el('th', label, cls || ''); th.scope = 'col'; if (title) th.title = title; tr.append(th); }
|
|
102
|
-
thead.append(tr); table.append(thead);
|
|
103
|
-
}
|
|
104
100
|
|
|
105
101
|
/* ---------- implementers ---------- */
|
|
106
102
|
const IMPL_COLS = [
|
|
@@ -116,31 +112,31 @@ window.ForgeLeaderboard = (() => {
|
|
|
116
112
|
['Median model time', 'lb-num', 'Median time in the model phase per attempt'],
|
|
117
113
|
['Tokens in / out', 'lb-num', 'Summed over the attempts that recorded tokens'],
|
|
118
114
|
];
|
|
115
|
+
const colsOf = (spec, cells) => spec.map(([label, cls, title], i) => ({ ...cells[i], label, cls, title }));
|
|
116
|
+
// a sortable, reorderable table (datatable.js); one set of preferences per kind of table, shared by its copies
|
|
117
|
+
function table(id, label, columns, rows, ctx, rowClass) {
|
|
118
|
+
const host = el('div', undefined, 'lb-dt');
|
|
119
|
+
window.ForgeTable.render(host, { id, label, columns, rows, className: 'lb-table', rowClass,
|
|
120
|
+
prefs: ctx.tablePrefs ? ctx.tablePrefs(id) : null, savePrefs: p => ctx.saveTablePrefs && ctx.saveTablePrefs(id, p) });
|
|
121
|
+
return host;
|
|
122
|
+
}
|
|
119
123
|
function implementerTable(rows, ctx, minN, caption) {
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
numCell(r.gateAccepts),
|
|
137
|
-
numCell(r.humanAccepts),
|
|
138
|
-
numCell(dur(r.medianModelS), `median wall time per attempt ${dur(r.medianDurationS)}`),
|
|
139
|
-
tokenCell(r.tokens, r.attempts.total, 'attempt'));
|
|
140
|
-
body.append(tr);
|
|
141
|
-
}
|
|
142
|
-
table.append(body); wrap.append(table);
|
|
143
|
-
return wrap;
|
|
124
|
+
const tk = r => r.timeoutKinds;
|
|
125
|
+
const columns = colsOf(IMPL_COLS, [
|
|
126
|
+
{ id: 'model', value: r => r.alias, cell: r => modelCell(r.alias, ctx, r.small ? `Fewer than ${plural(minN, 'first-attempt outcome')}: too few to rank` : '') },
|
|
127
|
+
{ id: 'tasks', value: r => r.tasks, cell: r => numCell(r.tasks) },
|
|
128
|
+
{ id: 'pass', value: r => r.first.n ? r.first.lo : null, cell: r => rateCell(r.first) },
|
|
129
|
+
{ id: 'censored', value: r => r.first.censored, cell: r => numCell(r.first.censored, Object.entries(r.first.reasons).map(([k, v]) => `${v} ${k.replace(/_/g, ' ')}`).join(', ') || undefined) },
|
|
130
|
+
{ id: 'retries', value: r => r.retries, cell: r => numCell(r.retries) },
|
|
131
|
+
{ id: 'escalated', value: r => r.escalationsAway, cell: r => numCell(r.escalationsAway) },
|
|
132
|
+
{ id: 'timeouts', value: r => r.timeouts, cell: r => numCell(r.timeouts, r.timeouts ? `implementer ${tk(r).implementer} · reviewer ${tk(r).reviewer} · forge ${tk(r).forge}` : undefined) },
|
|
133
|
+
{ id: 'gate', value: r => r.gateAccepts, cell: r => numCell(r.gateAccepts) },
|
|
134
|
+
{ id: 'human', value: r => r.humanAccepts, cell: r => numCell(r.humanAccepts) },
|
|
135
|
+
{ id: 'model-time', value: r => num(r.medianModelS), cell: r => numCell(dur(r.medianModelS), `median wall time per attempt ${dur(r.medianDurationS)}`) },
|
|
136
|
+
{ id: 'tokens', value: r => (r.tokens.attempts ?? r.tokens.reviews) ? (r.tokens.input || 0) + (r.tokens.output || 0) : null, cell: r => tokenCell(r.tokens, r.attempts.total, 'attempt') },
|
|
137
|
+
]);
|
|
138
|
+
columns[2].title += '. Sorts by the lower end of the interval, as the ranking does';
|
|
139
|
+
return table('implementers', caption, columns, rows, ctx, r => r.small ? 'lb-small' : '');
|
|
144
140
|
}
|
|
145
141
|
|
|
146
142
|
/* ---------- reviewers ---------- */
|
|
@@ -176,7 +172,7 @@ window.ForgeLeaderboard = (() => {
|
|
|
176
172
|
const avg = r.findings[s];
|
|
177
173
|
if (avg === null) return numCell('—', 'No review reply could be read');
|
|
178
174
|
const td = numCell(avg === 0 ? 0 : avg.toFixed(avg < 10 ? 2 : 1), `${count(r.findingTotals[s])} confirmed ${s} over ${plural(r.findings.n, 'review')}`);
|
|
179
|
-
if (s === 'P0' && avg > 0) td.classList.add('lb-p0'
|
|
175
|
+
if (s === 'P0' && avg > 0) td.classList.add('lb-p0');
|
|
180
176
|
return td;
|
|
181
177
|
}
|
|
182
178
|
function agreementCell(a) {
|
|
@@ -187,25 +183,25 @@ window.ForgeLeaderboard = (() => {
|
|
|
187
183
|
return td;
|
|
188
184
|
}
|
|
189
185
|
function reviewerTable(rows, ctx) {
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
return
|
|
186
|
+
const columns = colsOf(REV_COLS, [
|
|
187
|
+
{ id: 'model', value: r => r.alias, cell: r => modelCell(r.alias, ctx, '') },
|
|
188
|
+
{ id: 'primary', value: r => r.reviews.primary, cell: r => numCell(r.reviews.primary) },
|
|
189
|
+
{ id: 'shadow', value: r => r.reviews.shadow, cell: r => numCell(r.reviews.shadow) },
|
|
190
|
+
{ id: 'final', value: r => r.reviews.final, cell: r => numCell(r.reviews.final) },
|
|
191
|
+
{ id: 'verdicts', value: r => { const v = r.verdicts, t = v.PASS + v.FAIL + v.other; return t ? v.PASS / t : null; }, cell: r => verdictCell(r.verdicts) },
|
|
192
|
+
...MS.SEVERITIES.map(sv => ({ id: 'sev-' + sv, value: r => r.findings[sv], cell: r => findingCell(r, sv) })),
|
|
193
|
+
{ id: 'agreement', value: r => r.agreement.n ? r.agreement.p : null, cell: r => agreementCell(r.agreement) },
|
|
194
|
+
{ id: 'time', value: r => num(r.medianDurationS), cell: r => numCell(dur(r.medianDurationS)) },
|
|
195
|
+
{ id: 'tokens', value: r => (r.tokens.attempts ?? r.tokens.reviews) ? (r.tokens.input || 0) + (r.tokens.output || 0) : null, cell: r => tokenCell(r.tokens, r.reviews.total, 'review') },
|
|
196
|
+
]);
|
|
197
|
+
return table('reviewers', 'Reviewers', columns, rows, ctx);
|
|
202
198
|
}
|
|
203
199
|
|
|
204
200
|
/* ---------- header, caveat and filters ---------- */
|
|
205
201
|
function header(L, O, f, update, withNote) {
|
|
206
202
|
const t = L.totals, frag = document.createDocumentFragment();
|
|
207
203
|
const top = el('header', undefined, 'lb-head');
|
|
208
|
-
top.append(el('h1', 'Models'), el('p', `${plural(t.runs, 'run')} · ${plural(t.tasks, 'task')} · ${plural(t.attempts, 'attempt')} · ${plural(t.reviews, 'review')} counted`, 'muted lb-summary'));
|
|
204
|
+
top.append(el('h1', 'Models', 'module-title'), el('p', `${plural(t.runs, 'run')} · ${plural(t.tasks, 'task')} · ${plural(t.attempts, 'attempt')} · ${plural(t.reviews, 'review')} counted`, 'muted lb-summary'));
|
|
209
205
|
const note = el('div', undefined, 'lb-caveat'); note.setAttribute('role', 'note');
|
|
210
206
|
const p = el('p');
|
|
211
207
|
p.append(el('strong', 'Observational, not causal. '), document.createTextNode('Forge routes each task to a model by its difficulty and by how models did before, so a model sent the hard tasks looks worse here than it is. Exploratory picks, the tasks where forge drew the model at random, are the fairest comparison.'));
|
|
@@ -276,12 +272,12 @@ window.ForgeLeaderboard = (() => {
|
|
|
276
272
|
root.append(emptyNote('No tasks or reviews match these filters.', [], reset));
|
|
277
273
|
} else {
|
|
278
274
|
const impl = el('section', undefined, 'lb-section');
|
|
279
|
-
impl.append(el('h2', 'Implementers'), el('p', 'Each task counts once, for the model that ran its first attempt; retries, escalations and accepts show what happened after. Ranked by the lower end of the 95% interval, so a high rate on few tasks does not jump the table.', 'lb-lead'));
|
|
275
|
+
impl.append(el('h2', 'Implementers', 'plate'), el('p', 'Each task counts once, for the model that ran its first attempt; retries, escalations and accepts show what happened after. Ranked by the lower end of the 95% interval, so a high rate on few tasks does not jump the table.', 'lb-lead'));
|
|
280
276
|
impl.append(L.implementers.length ? implementerTable(L.implementers, ctx, f.minN, 'Implementers') : el('p', 'No attempts with a recorded model match these filters.', 'muted'));
|
|
281
277
|
root.append(impl);
|
|
282
278
|
if (!f.difficulty && L.implementers.length) {
|
|
283
279
|
const by = el('section', undefined, 'lb-section');
|
|
284
|
-
by.append(el('h2', 'By difficulty'), el('p', 'The same table for the tasks of one declared difficulty: the comparison routing distorts least.', 'lb-lead'));
|
|
280
|
+
by.append(el('h2', 'By difficulty', 'plate'), el('p', 'The same table for the tasks of one declared difficulty: the comparison routing distorts least.', 'lb-lead'));
|
|
285
281
|
for (const d of MS.DIFFS) {
|
|
286
282
|
const rows = L.byDifficulty[d];
|
|
287
283
|
if (!rows.length) continue;
|
|
@@ -299,7 +295,7 @@ window.ForgeLeaderboard = (() => {
|
|
|
299
295
|
root.append(by);
|
|
300
296
|
}
|
|
301
297
|
const rev = el('section', undefined, 'lb-section');
|
|
302
|
-
rev.append(el('h2', 'Reviewers'), el('p', 'Primary reviews of each attempt, shadow reviews run beside them and final reviews of a whole run. Findings are confirmed findings per review.', 'lb-lead'));
|
|
298
|
+
rev.append(el('h2', 'Reviewers', 'plate'), el('p', 'Primary reviews of each attempt, shadow reviews run beside them and final reviews of a whole run. Findings are confirmed findings per review.', 'lb-lead'));
|
|
303
299
|
rev.append(L.reviewers.length ? reviewerTable(L.reviewers, ctx) : el('p', 'No reviews with a recorded model match these filters.', 'muted'));
|
|
304
300
|
root.append(rev);
|
|
305
301
|
}
|