kalfa 3.0.0.dev0__py3-none-any.whl
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.
- kalfa/__init__.py +19 -0
- kalfa/api.py +284 -0
- kalfa/board/__init__.py +1 -0
- kalfa/board/answers.py +419 -0
- kalfa/board/server.py +536 -0
- kalfa/board/static/api.js +176 -0
- kalfa/board/static/app.js +122 -0
- kalfa/board/static/board.css +228 -0
- kalfa/board/static/charts.css +46 -0
- kalfa/board/static/components/badge.js +14 -0
- kalfa/board/static/components/chart.js +204 -0
- kalfa/board/static/components/diagram.js +857 -0
- kalfa/board/static/components/error.js +17 -0
- kalfa/board/static/components/loading.js +7 -0
- kalfa/board/static/components/progress.js +25 -0
- kalfa/board/static/components/settings.js +418 -0
- kalfa/board/static/components/spark.js +43 -0
- kalfa/board/static/components/table.js +156 -0
- kalfa/board/static/diagram.css +76 -0
- kalfa/board/static/format.js +138 -0
- kalfa/board/static/index.html +34 -0
- kalfa/board/static/plot.js +594 -0
- kalfa/board/static/predictions.css +20 -0
- kalfa/board/static/store.js +215 -0
- kalfa/board/static/views/compare.js +185 -0
- kalfa/board/static/views/home.js +90 -0
- kalfa/board/static/views/record/config.js +81 -0
- kalfa/board/static/views/record/curves.js +137 -0
- kalfa/board/static/views/record/data.js +378 -0
- kalfa/board/static/views/record/files.js +128 -0
- kalfa/board/static/views/record/log.js +164 -0
- kalfa/board/static/views/record/model.js +286 -0
- kalfa/board/static/views/record/overview.js +216 -0
- kalfa/board/static/views/record/plots.js +103 -0
- kalfa/board/static/views/record/predictions.js +834 -0
- kalfa/board/static/views/record.js +179 -0
- kalfa/board/static/views/runs.js +124 -0
- kalfa/board/static/views/sweep.js +282 -0
- kalfa/board/watch.py +87 -0
- kalfa/cli.py +371 -0
- kalfa/commands/__init__.py +0 -0
- kalfa/commands/common.py +8 -0
- kalfa/commands/export.py +117 -0
- kalfa/commands/generate.py +31 -0
- kalfa/commands/guide/__init__.py +101 -0
- kalfa/commands/guide/about.py +93 -0
- kalfa/commands/guide/calls.py +102 -0
- kalfa/commands/guide/sections.py +186 -0
- kalfa/commands/guide/seen.py +307 -0
- kalfa/commands/guide/show.py +106 -0
- kalfa/commands/guide/training.py +333 -0
- kalfa/commands/guide/words.py +97 -0
- kalfa/commands/legos.py +508 -0
- kalfa/commands/plots.py +27 -0
- kalfa/commands/report/__init__.py +64 -0
- kalfa/commands/report/page.py +112 -0
- kalfa/commands/report/predictions.py +216 -0
- kalfa/commands/report/sections.py +356 -0
- kalfa/commands/stop.py +60 -0
- kalfa/commands/sweep/__init__.py +202 -0
- kalfa/commands/sweep/checks.py +98 -0
- kalfa/commands/sweep/plan.py +183 -0
- kalfa/commands/sweep/points.py +123 -0
- kalfa/commands/sweep/runner.py +100 -0
- kalfa/commands/sweep/space.py +67 -0
- kalfa/commands/sweep/strategies.py +228 -0
- kalfa/core/__init__.py +0 -0
- kalfa/core/aliases.py +229 -0
- kalfa/core/build.py +727 -0
- kalfa/core/check.py +550 -0
- kalfa/core/coerce.py +36 -0
- kalfa/core/context.py +149 -0
- kalfa/core/device.py +63 -0
- kalfa/core/errors.py +45 -0
- kalfa/core/graph.py +304 -0
- kalfa/core/history.py +70 -0
- kalfa/core/loop.py +710 -0
- kalfa/core/make.py +121 -0
- kalfa/core/plan.py +156 -0
- kalfa/core/predict.py +290 -0
- kalfa/core/protocols.py +122 -0
- kalfa/core/record.py +347 -0
- kalfa/core/refs.py +88 -0
- kalfa/core/rng.py +100 -0
- kalfa/core/rules.py +513 -0
- kalfa/core/run.py +568 -0
- kalfa/core/spec.py +413 -0
- kalfa/core/state.py +100 -0
- kalfa/core/wrap.py +671 -0
- kalfa/records/__init__.py +24 -0
- kalfa/records/compare.py +86 -0
- kalfa/records/reader.py +561 -0
- kalfa/records/stats/__init__.py +36 -0
- kalfa/records/stats/choices.py +282 -0
- kalfa/records/stats/classes.py +236 -0
- kalfa/records/stats/measures.py +144 -0
- kalfa/records/stats/pairs.py +225 -0
- kalfa/records/stats/tables.py +153 -0
- kalfa/records/sweep.py +244 -0
- kalfa/std/__init__.py +17 -0
- kalfa/std/actions.py +35 -0
- kalfa/std/common.py +56 -0
- kalfa/std/data/__init__.py +1 -0
- kalfa/std/data/base.py +45 -0
- kalfa/std/data/columns.py +190 -0
- kalfa/std/data/flow.py +317 -0
- kalfa/std/data/images.py +443 -0
- kalfa/std/data/options.py +147 -0
- kalfa/std/data/pre.py +84 -0
- kalfa/std/data/splits.py +140 -0
- kalfa/std/data/stream.py +294 -0
- kalfa/std/data/table.py +642 -0
- kalfa/std/data/text.py +436 -0
- kalfa/std/data/transforms.py +327 -0
- kalfa/std/data/weights.py +113 -0
- kalfa/std/data/window.py +135 -0
- kalfa/std/figure.py +37 -0
- kalfa/std/init.py +72 -0
- kalfa/std/loggers.py +105 -0
- kalfa/std/losses.py +161 -0
- kalfa/std/metrics.py +463 -0
- kalfa/std/models.py +122 -0
- kalfa/std/notes.py +1553 -0
- kalfa/std/objectives.py +360 -0
- kalfa/std/plots/__init__.py +75 -0
- kalfa/std/plots/classification.py +207 -0
- kalfa/std/plots/common.py +44 -0
- kalfa/std/plots/curves.py +56 -0
- kalfa/std/plots/images.py +188 -0
- kalfa/std/plots/pairs.py +66 -0
- kalfa/std/plots/predictions.py +154 -0
- kalfa/std/samplers.py +261 -0
- kalfa/std/samples.py +72 -0
- kalfa/std/schedules.py +74 -0
- kalfa/std/steps.py +133 -0
- kalfa/std/triggers.py +135 -0
- kalfa/summary.py +296 -0
- kalfa/tui/__init__.py +4 -0
- kalfa/tui/app.py +107 -0
- kalfa/tui/config.py +61 -0
- kalfa/tui/guide.py +100 -0
- kalfa/tui/jobs.py +97 -0
- kalfa/tui/legos.py +62 -0
- kalfa/tui/records.py +439 -0
- kalfa-3.0.0.dev0.dist-info/METADATA +261 -0
- kalfa-3.0.0.dev0.dist-info/RECORD +148 -0
- kalfa-3.0.0.dev0.dist-info/WHEEL +4 -0
- kalfa-3.0.0.dev0.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
.kdiagram { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
|
|
2
|
+
.kdiagram-tools { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
|
|
3
|
+
.kdiagram-tools .tool:disabled { opacity: 0.45; cursor: default; pointer-events: none; }
|
|
4
|
+
.kdiagram-hint { margin-right: 8px; }
|
|
5
|
+
.kdiagram-canvas { display: block; width: 100%; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); cursor: grab; user-select: none; touch-action: none; }
|
|
6
|
+
.kdiagram-canvas.panning { cursor: grabbing; }
|
|
7
|
+
|
|
8
|
+
.kdiagram-group .group-box { fill: color-mix(in srgb, var(--ink) 2.5%, transparent); stroke: var(--line-2); stroke-width: 1; }
|
|
9
|
+
.kdiagram-group.depth-2 .group-box { fill: color-mix(in srgb, var(--ink) 3%, transparent); stroke-dasharray: 5 3; }
|
|
10
|
+
.kdiagram-group.depth-3 .group-box, .kdiagram-group.depth-4 .group-box { fill: color-mix(in srgb, var(--ink) 3.5%, transparent); stroke-dasharray: 2 3; }
|
|
11
|
+
.kdiagram-group .group-head { cursor: pointer; }
|
|
12
|
+
.kdiagram-group .group-hit { fill: transparent; stroke: none; }
|
|
13
|
+
.kdiagram-group .group-head:hover .group-hit { fill: color-mix(in srgb, var(--accent) 8%, transparent); }
|
|
14
|
+
.kdiagram-group .group-label { font: 600 11px var(--font); fill: var(--ink-2); }
|
|
15
|
+
.kdiagram-group .group-detail { font-weight: 400; fill: var(--muted); }
|
|
16
|
+
.kdiagram-group .group-corner { font: 10px var(--mono); fill: var(--muted); }
|
|
17
|
+
.kdiagram-group .group-head:hover .group-label { fill: var(--accent); }
|
|
18
|
+
|
|
19
|
+
.kdiagram-edge .edge-line { fill: none; stroke: color-mix(in srgb, var(--muted) 75%, transparent); stroke-width: 1.3; }
|
|
20
|
+
.kdiagram-edge .edge-hit { fill: none; stroke: transparent; stroke-width: 10; pointer-events: stroke; }
|
|
21
|
+
.kdiagram-edge:hover .edge-line { stroke: var(--ink-2); }
|
|
22
|
+
.kdiagram-edge.on .edge-line { stroke: var(--accent); stroke-width: 2; }
|
|
23
|
+
.kdiagram-arrow { fill: color-mix(in srgb, var(--muted) 75%, transparent); stroke: none; }
|
|
24
|
+
.kdiagram-arrow.on { fill: var(--accent); }
|
|
25
|
+
.kdiagram .edge-label { font: 10px var(--mono); fill: var(--ink-2); paint-order: stroke; stroke: var(--surface); stroke-width: 3px; stroke-linejoin: round; pointer-events: none; }
|
|
26
|
+
|
|
27
|
+
.kdiagram-node { cursor: pointer; --kind: var(--line-2); }
|
|
28
|
+
.kdiagram-node .node-box { fill: color-mix(in srgb, var(--kind) 9%, var(--surface)); stroke: color-mix(in srgb, var(--kind) 65%, var(--line-2)); stroke-width: 1; }
|
|
29
|
+
.kdiagram-node:hover .node-box { stroke: var(--kind); stroke-width: 1.6; }
|
|
30
|
+
.kdiagram-node.selected .node-box { stroke: var(--accent); stroke-width: 2.2; }
|
|
31
|
+
.kdiagram-node.collapsed .node-box { stroke-dasharray: 4 3; stroke-width: 1.4; }
|
|
32
|
+
.kdiagram-node .node-label { font: 600 12px var(--font); fill: var(--ink); }
|
|
33
|
+
.kdiagram-node .node-detail { font: 11px var(--font); fill: var(--muted); }
|
|
34
|
+
.kdiagram-node .node-shape { font: 11px var(--mono); fill: var(--ink-2); }
|
|
35
|
+
.kdiagram-node .node-corner { font: 10px var(--mono); fill: var(--muted); }
|
|
36
|
+
|
|
37
|
+
.kdiagram-node.kind-input { --kind: var(--series-1); }
|
|
38
|
+
.kdiagram-node.kind-module { --kind: var(--ink-2); }
|
|
39
|
+
.kdiagram-node.kind-op { --kind: var(--series-4); }
|
|
40
|
+
.kdiagram-node.kind-output { --kind: var(--series-3); }
|
|
41
|
+
.kdiagram-node.kind-group { --kind: var(--muted); }
|
|
42
|
+
.kdiagram-node.kind-source { --kind: var(--series-1); }
|
|
43
|
+
.kdiagram-node.kind-stream { --kind: var(--series-1); }
|
|
44
|
+
.kdiagram-node.kind-filter { --kind: var(--series-4); }
|
|
45
|
+
.kdiagram-node.kind-drop { --kind: var(--series-8); }
|
|
46
|
+
.kdiagram-node.kind-split { --kind: var(--series-7); }
|
|
47
|
+
.kdiagram-node.kind-set { --kind: var(--series-3); }
|
|
48
|
+
.kdiagram-node.kind-classes { --kind: var(--series-5); }
|
|
49
|
+
.kdiagram-node.kind-features { --kind: var(--series-2); }
|
|
50
|
+
.kdiagram-node.kind-targets { --kind: var(--series-5); }
|
|
51
|
+
.kdiagram-node.kind-weights { --kind: var(--series-6); }
|
|
52
|
+
.kdiagram-node.kind-chain { --kind: var(--ink-2); }
|
|
53
|
+
.kdiagram-node.kind-transform { --kind: var(--ink-2); }
|
|
54
|
+
.kdiagram-node.kind-tokenizer { --kind: var(--series-2); }
|
|
55
|
+
.kdiagram-node.kind-windows { --kind: var(--series-6); }
|
|
56
|
+
.kdiagram-node.kind-feed { --kind: var(--series-6); }
|
|
57
|
+
.kdiagram-node.kind-tensor { --kind: var(--series-7); }
|
|
58
|
+
.kdiagram-node.kind-batch { --kind: var(--ink); }
|
|
59
|
+
|
|
60
|
+
.model-views { display: flex; gap: 2px; border-bottom: 1px solid var(--line); margin: 8px 0; overflow-x: auto; }
|
|
61
|
+
.model-views .tab { background: none; border: 0; border-bottom: 2px solid transparent; cursor: pointer; font: inherit; }
|
|
62
|
+
.model-views .tab.active { color: var(--accent); border-bottom-color: var(--accent); font-weight: 600; }
|
|
63
|
+
.model-views .tab:disabled { opacity: 0.45; cursor: default; }
|
|
64
|
+
.model-diagram { display: grid; grid-template-columns: minmax(0, 1fr) minmax(220px, 280px); gap: 12px; align-items: start; }
|
|
65
|
+
.model-detail { display: flex; flex-direction: column; gap: 6px; padding: 10px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface-2); min-width: 0; }
|
|
66
|
+
.model-detail-title { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; font-weight: 600; overflow-wrap: anywhere; }
|
|
67
|
+
.model-detail dl { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 3px 10px; margin: 0; }
|
|
68
|
+
.model-detail dt { color: var(--muted); font-size: 12px; }
|
|
69
|
+
.model-detail dd { margin: 0; overflow-wrap: anywhere; font-size: 12px; }
|
|
70
|
+
.model-detail ul { margin: 0; padding-left: 16px; }
|
|
71
|
+
.model-detail li { font-size: 12px; overflow-wrap: anywhere; }
|
|
72
|
+
.model-note { margin: 4px 0; }
|
|
73
|
+
|
|
74
|
+
@media (max-width: 860px) {
|
|
75
|
+
.model-diagram { grid-template-columns: 1fr; }
|
|
76
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
export const EMPTY = "·";
|
|
2
|
+
|
|
3
|
+
export function isNumber(value) {
|
|
4
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function compact(text) {
|
|
8
|
+
const [mantissa, power] = text.split("e");
|
|
9
|
+
const trimmed = mantissa.indexOf(".") < 0 ? mantissa : mantissa.replace(/0+$/, "").replace(/\.$/, "");
|
|
10
|
+
return power === undefined ? trimmed : trimmed + "e" + power;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function number(value, digits = 4) {
|
|
14
|
+
if (value === null || value === undefined || value === "") return EMPTY;
|
|
15
|
+
if (typeof value === "boolean") return String(value);
|
|
16
|
+
if (typeof value !== "number") return String(value);
|
|
17
|
+
if (!Number.isFinite(value)) return String(value);
|
|
18
|
+
if (value === 0) return "0";
|
|
19
|
+
if (Number.isInteger(value) && Math.abs(value) < 1e6) return String(value);
|
|
20
|
+
const exponent = Math.floor(Math.log10(Math.abs(value)));
|
|
21
|
+
if (exponent < -4 || exponent >= digits) return compact(value.toExponential(digits - 1));
|
|
22
|
+
return compact(value.toPrecision(digits));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function integer(value) {
|
|
26
|
+
return isNumber(value) ? Math.round(value).toLocaleString() : EMPTY;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function pad(value) {
|
|
30
|
+
return String(Math.floor(value)).padStart(2, "0");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function seconds(value) {
|
|
34
|
+
if (!isNumber(value)) return EMPTY;
|
|
35
|
+
if (value < 60) return value.toFixed(1) + "s";
|
|
36
|
+
if (value < 3600) return Math.floor(value / 60) + "m " + pad(value % 60) + "s";
|
|
37
|
+
return Math.floor(value / 3600) + "h " + pad((value % 3600) / 60) + "m";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function moment(iso) {
|
|
41
|
+
if (!iso) return null;
|
|
42
|
+
const date = new Date(iso);
|
|
43
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function when(iso) {
|
|
47
|
+
const date = moment(iso);
|
|
48
|
+
if (!date) return iso ? String(iso) : EMPTY;
|
|
49
|
+
const today = new Date();
|
|
50
|
+
const time = date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
|
|
51
|
+
if (date.toDateString() === today.toDateString()) return time;
|
|
52
|
+
const day = date.toLocaleDateString([], { month: "short", day: "numeric" });
|
|
53
|
+
if (date.getFullYear() === today.getFullYear()) return day + " " + time;
|
|
54
|
+
return date.toLocaleDateString() + " " + time;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function ago(iso) {
|
|
58
|
+
const date = moment(iso);
|
|
59
|
+
if (!date) return EMPTY;
|
|
60
|
+
const elapsed = Math.max(0, (Date.now() - date.getTime()) / 1000);
|
|
61
|
+
if (elapsed < 60) return Math.round(elapsed) + "s ago";
|
|
62
|
+
if (elapsed < 3600) return Math.round(elapsed / 60) + "m ago";
|
|
63
|
+
if (elapsed < 86400) return Math.round(elapsed / 3600) + "h ago";
|
|
64
|
+
return Math.round(elapsed / 86400) + "d ago";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function percent(value, digits = 0) {
|
|
68
|
+
return isNumber(value) ? (100 * value).toFixed(digits) + "%" : EMPTY;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function bytes(value) {
|
|
72
|
+
if (!isNumber(value)) return EMPTY;
|
|
73
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
74
|
+
let size = value;
|
|
75
|
+
let unit = 0;
|
|
76
|
+
while (size >= 1024 && unit < units.length - 1) {
|
|
77
|
+
size /= 1024;
|
|
78
|
+
unit += 1;
|
|
79
|
+
}
|
|
80
|
+
return (unit === 0 ? String(size) : size.toFixed(size < 10 ? 1 : 0)) + " " + units[unit];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function fraction(turn, total) {
|
|
84
|
+
const done = isNumber(turn) ? String(turn) : "0";
|
|
85
|
+
return isNumber(total) && total > 0 ? done + "/" + total : done;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function share(turn, total) {
|
|
89
|
+
if (!isNumber(turn) || !isNumber(total) || total <= 0) return null;
|
|
90
|
+
return Math.max(0, Math.min(1, turn / total));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function text(value) {
|
|
94
|
+
if (value === null || value === undefined || value === "") return EMPTY;
|
|
95
|
+
if (typeof value === "number") return number(value);
|
|
96
|
+
if (typeof value === "object") {
|
|
97
|
+
try {
|
|
98
|
+
return JSON.stringify(value);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
return String(value);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return String(value);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function bestText(best) {
|
|
107
|
+
if (!best || typeof best !== "object" || !best.series) return EMPTY;
|
|
108
|
+
return best.series + " " + number(best.value);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function left(value) {
|
|
112
|
+
if (!isNumber(value)) return "";
|
|
113
|
+
if (value < 60) return "under a minute left";
|
|
114
|
+
return "about " + seconds(value) + " left";
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function withinText(within) {
|
|
118
|
+
if (!within || typeof within !== "object") return "";
|
|
119
|
+
const phase = within.phase && within.phase !== "train" ? String(within.phase) + " " : "";
|
|
120
|
+
const unit = String(within.unit || "batches");
|
|
121
|
+
const one = unit === "batches" ? "batch" : unit === "updates" ? "update" : unit;
|
|
122
|
+
if (isNumber(within.done) && isNumber(within.total) && within.total > 0) {
|
|
123
|
+
return phase + one + " " + within.done + " of " + within.total;
|
|
124
|
+
}
|
|
125
|
+
if (isNumber(within.done) && within.done > 0) return phase + within.done + " " + unit;
|
|
126
|
+
return phase.trim();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function nonfiniteText(found) {
|
|
130
|
+
if (!found || !Array.isArray(found.series) || !found.series.length) return "";
|
|
131
|
+
const more = found.series.length > 1 ? " and " + (found.series.length - 1) + " more" : "";
|
|
132
|
+
return found.series[0] + more + " is " + (found.value || "not finite") + " from turn " + found.turn;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function liveNote(summary) {
|
|
136
|
+
if (!summary || typeof summary !== "object") return "";
|
|
137
|
+
return [withinText(summary.within), left(summary.eta)].filter(Boolean).join(", ");
|
|
138
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>kalfa board</title>
|
|
7
|
+
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2032%2032'%3E%3Crect%20width='32'%20height='32'%20rx='7'%20fill='%232a78d6'/%3E%3Crect%20x='5'%20y='17'%20width='9'%20height='9'%20rx='2'%20fill='white'/%3E%3Crect%20x='11.5'%20y='10.5'%20width='9'%20height='9'%20rx='2'%20fill='white'%20opacity='.8'/%3E%3Crect%20x='18'%20y='4'%20width='9'%20height='9'%20rx='2'%20fill='white'%20opacity='.6'/%3E%3C/svg%3E">
|
|
8
|
+
<script>
|
|
9
|
+
(function () {
|
|
10
|
+
window.setTimeout(function () {
|
|
11
|
+
if (window.kalfaMounted) return;
|
|
12
|
+
var app = document.getElementById("app");
|
|
13
|
+
if (app) app.textContent = "the board did not start within ten seconds: it needs the Vue module from cdn.jsdelivr.net, which this browser could not load";
|
|
14
|
+
}, 10000);
|
|
15
|
+
var theme = null;
|
|
16
|
+
try {
|
|
17
|
+
theme = window.localStorage.getItem("kalfa.theme");
|
|
18
|
+
} catch (error) {
|
|
19
|
+
theme = null;
|
|
20
|
+
}
|
|
21
|
+
if (theme === "light" || theme === "dark") document.documentElement.setAttribute("data-theme", theme);
|
|
22
|
+
})();
|
|
23
|
+
</script>
|
|
24
|
+
<link rel="stylesheet" href="/static/board.css">
|
|
25
|
+
<link rel="stylesheet" href="/static/charts.css">
|
|
26
|
+
<link rel="stylesheet" href="/static/diagram.css">
|
|
27
|
+
<link rel="stylesheet" href="/static/predictions.css">
|
|
28
|
+
</head>
|
|
29
|
+
<body>
|
|
30
|
+
<div id="app"><p class="loading">loading the board</p><noscript>kalfa board needs JavaScript and the Vue and Plotly scripts from cdn.jsdelivr.net</noscript></div>
|
|
31
|
+
<script src="https://cdn.jsdelivr.net/npm/plotly.js-dist-min@2.35.2/plotly.min.js"></script>
|
|
32
|
+
<script type="module" src="/static/app.js"></script>
|
|
33
|
+
</body>
|
|
34
|
+
</html>
|