aboutsummaryrefslogtreecommitdiff
path: root/src/zenserver/frontend
diff options
context:
space:
mode:
authorMartin Ridgers <[email protected]>2024-11-28 15:07:16 +0100
committerGitHub Enterprise <[email protected]>2024-11-28 15:07:16 +0100
commite697b522f2d869f335aa8db074b43362aff1e29a (patch)
tree57ecb83feea76bdb3daeff598b062d5e952d433c /src/zenserver/frontend
parentset content type correctly for getchunkrange (#241) (diff)
downloadzen-e697b522f2d869f335aa8db074b43362aff1e29a.tar.xz
zen-e697b522f2d869f335aa8db074b43362aff1e29a.zip
Dashboard CSS fixes and archival of a partial treemap view (#242)
* Input boxes' text was unreadable when using the dark theme * Change from margins to padding top/bottom - easier to reason about vertical styling. * A treemap. Not used anywhere and not finished. Submitting so it isn't lost * Prevent tables' first content columns from collapsing * Dashboardk .zip archive update
Diffstat (limited to 'src/zenserver/frontend')
-rw-r--r--src/zenserver/frontend/html.zipbin150189 -> 154690 bytes
-rw-r--r--src/zenserver/frontend/html/pages/map.js166
-rw-r--r--src/zenserver/frontend/html/util/widgets.js6
-rw-r--r--src/zenserver/frontend/html/zen.css28
4 files changed, 194 insertions, 6 deletions
diff --git a/src/zenserver/frontend/html.zip b/src/zenserver/frontend/html.zip
index 5f53d9f38..6f077d6e7 100644
--- a/src/zenserver/frontend/html.zip
+++ b/src/zenserver/frontend/html.zip
Binary files differ
diff --git a/src/zenserver/frontend/html/pages/map.js b/src/zenserver/frontend/html/pages/map.js
new file mode 100644
index 000000000..58046b255
--- /dev/null
+++ b/src/zenserver/frontend/html/pages/map.js
@@ -0,0 +1,166 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+"use strict";
+
+import { ZenPage } from "./page.js"
+import { Friendly } from "../util/friendly.js"
+import { ProgressBar } from "../util/widgets.js"
+import { create_indexer } from "../indexer/indexer.js"
+
+
+////////////////////////////////////////////////////////////////////////////////
+function squarify(weights, callback, area_threshold=-1)
+{
+ const rect = [1.0, 1.0];
+ for (var start = 0; start < weights.length;)
+ {
+ const ri = +(rect[0] >= rect[1]);
+
+ const length = rect[ri];
+ var end = start;
+ var area = 0;
+ var prev_rd = Infinity;
+ for (; end < weights.length; ++end)
+ {
+ const w = (area + weights[end]) / length;
+ const r = weights[end] / (w * w);
+ const rd = Math.abs(1.0 - r);
+ if (prev_rd < rd)
+ break;
+ prev_rd = rd;
+ area += weights[end];
+ }
+ const v = area / length;
+
+ const tl = [1.0 - rect[0], 1.0 - rect[1]];
+ const wh = [undefined, undefined];
+ for (var i = start; i < end; ++i)
+ {
+ wh[ri ^ 0] = weights[i] / v;
+ wh[ri ^ 1] = v;
+ callback(i, tl[0], tl[1], wh[0], wh[1], ri);
+ tl[ri] += wh[ri];
+ }
+
+ start = end;
+ rect[ri ^ 1] -= v;
+
+ if (rect[0] * rect[1] < area_threshold)
+ break;
+ }
+}
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+export class Page extends ZenPage
+{
+ main()
+ {
+ const project = this.get_param("project");
+ const oplog = this.get_param("oplog");
+ this._indexer = this._load_indexer(project, oplog);
+
+ this.set_title("map");
+
+ const section = this.add_section(project + " - " + oplog);
+ this._build(section);
+ }
+
+ async _load_indexer(project, oplog)
+ {
+ const progress_bar = this.add_widget(ProgressBar);
+ progress_bar.set_progress("indexing");
+ var indexer = create_indexer(project, oplog, (...args) => {
+ progress_bar.set_progress(...args);
+ });
+ indexer = await indexer;
+ progress_bar.destroy();
+ return indexer;
+ }
+
+ async _build(section)
+ {
+ const indexer = await this._indexer;
+
+ var prefix = this.get_param("path", "/");
+ if (!prefix.endsWith("/"))
+ prefix += "/";
+
+ var total_size = 0;
+ var branch_size = 0;
+ const new_nodes = new Object();
+ for (var [name, size] of indexer.enum_all())
+ {
+ total_size += size;
+ if (!name.startsWith(prefix))
+ continue;
+
+ branch_size += size;
+
+ name = name.substr(prefix.length);
+ const slash = name.indexOf("/");
+ if (slash != -1)
+ name = name.substr(0, slash + 1);
+
+ if (new_nodes[name] !== undefined)
+ new_nodes[name] += size;
+ else
+ new_nodes[name] = size;
+ }
+
+ const sorted_keys = Object.keys(new_nodes).sort((l, r) => {
+ return new_nodes[r] - new_nodes[l];
+ });
+ const nodes = new Array();
+ for (const name of sorted_keys)
+ nodes.push(new_nodes[name] / branch_size);
+
+ var stats = Friendly.kib(branch_size);
+ stats += " / ";
+ stats += Friendly.kib(total_size);
+ stats += " (";
+ stats += 0|((branch_size * 100) / total_size);
+ stats += "%)";
+ section.tag().text(prefix + " : " + stats);
+ const treemap = section.tag().id("treemap");
+ const canvas = treemap.tag("canvas").inner();
+
+ const width = canvas.offsetWidth;
+ var height = window.visualViewport.height;
+ height -= treemap.inner().getBoundingClientRect().top + window.scrollY;
+ height -= 50;
+
+ canvas.width = canvas.offsetWidth;
+ canvas.height = height;
+ const context = canvas.getContext("2d");
+ context.textBaseline = "top";
+ context.imageSmoothingEnabled = false;
+ context.font = "13px sans-serif";
+ context.strokeStyle = "#666666";
+
+ const palette = [
+ "#8dd3c7", "#ffffb3", "#bebada", "#fb8072", "#80b1d3", "#fdb462",
+ "#b3de69", "#fccde5", "#d9d9d9", "#bc80bd", "#ccebc5",
+ ];
+
+ const callback = (i, x, y, w, h, d) => {
+ const r = function(u,v) { return Math.floor(u * (v - 1e-7)); };
+ x = r(x, width);
+ y = r(y, height);
+ w = r(w, width);
+ h = r(h, height);
+ context.save();
+ context.beginPath();
+ context.rect(x, y, w, h);
+ context.clip();
+ context.fillStyle = palette[(i * 0x493) % palette.length];
+ context.fill();
+ context.stroke();
+ context.fillStyle = "#000000";
+ context.fillText(sorted_keys[i], x + 4, y + 4);
+ context.restore();
+ };
+ squarify(nodes, callback, 0.01);
+ }
+}
diff --git a/src/zenserver/frontend/html/util/widgets.js b/src/zenserver/frontend/html/util/widgets.js
index e567a7a00..78998b7ff 100644
--- a/src/zenserver/frontend/html/util/widgets.js
+++ b/src/zenserver/frontend/html/util/widgets.js
@@ -60,10 +60,12 @@ export class Table extends Widget
var root = parent.tag().classify("zen_table");
super(root);
+ const column_width = 0 | (100 / column_names.length);
+
var column_style;
if (flags & Table.Flag_FitLeft) column_style = "max-content";
- else if (flags & Table.Flag_BiasLeft) column_style = "2fr";
- else column_style = "1fr";
+ else if (flags & Table.Flag_BiasLeft) column_style = `minmax(${column_width * 2}%, 2fr)`;
+ else column_style = `minmax(${column_width}%, 1fr)`;
for (var i = 1; i < column_names.length; ++i)
{
const style = (flags & Table.Flag_PackRight) ? " auto" : " 1fr";
diff --git a/src/zenserver/frontend/html/zen.css b/src/zenserver/frontend/html/zen.css
index c9dc0d83e..532b71571 100644
--- a/src/zenserver/frontend/html/zen.css
+++ b/src/zenserver/frontend/html/zen.css
@@ -59,6 +59,7 @@ pre {
}
input {
+ color: var(--theme_g0);
background-color: var(--theme_g3);
border: 1px solid var(--theme_g2);
}
@@ -68,12 +69,14 @@ input {
}
#container {
- max-width: 130em;
- min-width: 80em;
- margin: auto;
+ max-width: 130em;
+ min-width: 80em;
+ margin: auto;
> div {
- margin: 1.0em 2.2em 1.5em 2.2em;
+ margin: 0.0em 2.2em 0.0em 2.2em;
+ padding-top: 1.0em;
+ padding-bottom: 1.5em;
}
}
@@ -467,3 +470,20 @@ a {
content: "\\";
}
}
+
+/* map ---------------------------------------------------------------------- */
+
+html:has(#map) {
+ height: 100%;
+ body, #container, #map {
+ height: 100%;
+ }
+}
+#map {
+ #treemap {
+ position: relative;
+ canvas {
+ width: 100%;
+ }
+ }
+}