v0.36.9
This commit is contained in:
1 parent
88728bdd5a
commit
358a4518d9
4 files changed
+55
-8
No files matched your search
+2
-2
@@ -6,8 +6,8 @@ Release history focused on **features, settings, default behavior, compatibility
|
||||
|
||||
- **Memory breakdown (Overview card, hidden by default):** a new optional `memory_breakdown` dashboard card splits the process RSS into cache data (object bodies), cache bookkeeping (index, keys, headers), cache buffers (fills, COLD queue/IO), free memory held by the allocator (fragmentation, an upper bound), code and mapped files, and the rest of the process. Chips show RSS / cache ratio, cache overhead per object, HOT object count and heap fragmentation; an allocator section lists heap, in-use, free-held and large mmap blocks. An "Outside cache" chart tracks RSS minus accounted cache and, after 10 minutes of samples, labels the trend as stable, rising or falling (with the slope in MiB/h) to tell a leak apart from flat overhead. Enable it in the layout editor.
|
||||
- **Status API:** `GET /api/status` (full and `view=live`) adds an additive `memory_allocator` object with glibc `mallinfo2` statistics (`heap_bytes`, `in_use_bytes`, `free_bytes`, `releasable_bytes`, `mmap_bytes`, `mmap_blocks`), sampled at most every 2 seconds; it is `null` on musl, non-Linux and the optional mimalloc build.
|
||||
- **Fit to screen (auto-arrange):** a new **Fit to screen** switch in the unlocked layout toolbar (account preference `dashboard_auto_fit`, **on by default**) re-arranges a custom Overview for the current screen width, so the layout designed on a wide screen stays tidy on laptops and tablets. Each card keeps its saved width (but at least a readable minimum for its type), cards are placed in saved order into the first free spot from the top, stat tiles that start on one row get one height, a card whose side neighbour ends slightly lower is extended to that edge (only while the dashboard grows by at most two stat tiles), remaining holes are filled by widening a neighbouring card sideways or growing it into free space above or below. The saved widths, heights and order are never changed; switch it off to see the layout exactly as saved. While a block is dragged the normal flow is shown so the drop placeholder stays accurate.
|
||||
- **No layout jump on reload:** the Overview grid stays invisible until the account's layout from the server has been applied and the first live data has been rendered, then appears already arranged. Nothing about the layout is stored in the browser (an older cached layout key is removed).
|
||||
- **Fit to screen (auto-arrange):** a new **Fit to screen** switch in the unlocked layout toolbar (account preference `dashboard_auto_fit`, **on by default**) re-arranges a custom Overview for the current screen width, so the layout designed on a wide screen stays tidy on laptops and tablets. Each card keeps its saved width (but at least a readable minimum for its type), cards are placed in saved order into the first free spot from the top, stat tiles that start on one row get one height, a card whose side neighbour ends slightly lower is extended to that edge (only while the dashboard grows by at most two stat tiles), remaining holes are filled by widening a neighbouring card sideways or growing it into free space above or below. When a card is placed, a free strip beside it that is narrower than every card still to be placed is taken by that card at once, and at the end of the grid a chart or panel reaches the last row instead of leaving an empty area. The saved widths, heights and order are never changed; switch it off to see the layout exactly as saved. While a block is dragged the normal flow is shown so the drop placeholder stays accurate.
|
||||
- **No layout jump on reload:** the Overview grid stays invisible until the account's layout from the server has been applied and the first live data has been rendered, then appears already arranged; meanwhile the panel's startup screen (logo, spinner, "Loading RustProxy…") is shown in its place. Nothing about the layout is stored in the browser (an older cached layout key is removed).
|
||||
- **Overview layout with row heights:** in a custom (unlocked/saved) layout, card heights now snap to a shared row unit — half the height of the tallest compact stat card, so stat tiles always span two rows and line up — instead of every card in a row stretching to the tallest one. A tall panel (Process, RAM cache, Network…) can sit beside several rows of small cards or charts with aligned edges. Each card automatically gets the rows its content needs; in the layout editor, the new **↕ − / +** control next to the width makes a card taller (`A` marks automatic height, lowering it to the content height returns to automatic). Charts (also with chart scales enabled), chart stacks and tables grow into the extra height, and a card is stretched over the empty cells below it down to the next card or the end of the grid, so rows end on straight edges. **Fill gaps** packs blocks with their real heights. Small content changes (for example a growing table) cannot make the grid jump: a card grows at once but shrinks only after the smaller size is seen several times. Below a 700 px wide dashboard, cards stack by content height as before.
|
||||
- **Preferences API:** additive optional `dashboard_widget_heights` (widget ID → 1–32 rows) in `/api/preferences` and dashboard presets, and `dashboard_auto_fit` (boolean, default `true`) in `/api/preferences`. Older documents and presets without it keep automatic heights.
|
||||
- **Upgrade notes:** no configuration or database migration is required; existing saved layouts receive the new card hidden at the end. Legacy (never customized) layouts keep their grouped rows until the layout is edited.
|
||||
|
||||
@@ -134,7 +134,8 @@ window.rustProxyOverviewRows = (() => {
|
||||
const columns = geometry.starts.length;
|
||||
const cardsModel = items.map(node => {
|
||||
const flow = place(node, geometry);
|
||||
return { node, stat: isStat(node), w: Math.min(columns, spanOf(flow)), design: spanOf(flow), h: rowsOf(node), r: 0, c: 0 };
|
||||
const w = Math.min(columns, spanOf(flow));
|
||||
return { node, stat: isStat(node), w, base: w, design: spanOf(flow), h: rowsOf(node), r: 0, c: 0 };
|
||||
});
|
||||
let occupancy = [];
|
||||
const free = (r, c, w, h) => {
|
||||
@@ -151,16 +152,32 @@ window.rustProxyOverviewRows = (() => {
|
||||
for (let x = c; x < c + w; x++) row[x] = value;
|
||||
}
|
||||
};
|
||||
const stripFree = (x, r, h) => {
|
||||
for (let y = r; y < r + h; y++) if (occupancy[y]?.[x]) return false;
|
||||
return true;
|
||||
};
|
||||
const pack = () => {
|
||||
occupancy = [];
|
||||
for (const card of cardsModel) {
|
||||
cardsModel.forEach((card, index) => {
|
||||
card.w = card.base;
|
||||
let placed = false;
|
||||
for (let r = 0; !placed; r++) {
|
||||
for (let c = 0; c + card.w <= columns; c++) {
|
||||
if (free(r, c, card.w, card.h)) { card.r = r; card.c = c; mark(r, c, card.w, card.h); placed = true; break; }
|
||||
}
|
||||
}
|
||||
}
|
||||
// A free strip beside the new card (over its whole height) that is
|
||||
// narrower than every card still to be placed could never be filled:
|
||||
// the card takes it now instead of leaving a hole.
|
||||
let narrowest = Infinity;
|
||||
for (let next = index + 1; next < cardsModel.length; next++) narrowest = Math.min(narrowest, cardsModel[next].base);
|
||||
let right = 0;
|
||||
while (card.c + card.w + right < columns && stripFree(card.c + card.w + right, card.r, card.h)) right++;
|
||||
if (right > 0 && right < narrowest) { mark(card.r, card.c + card.w, right, card.h); card.w += right; }
|
||||
let left = 0;
|
||||
while (card.c - left - 1 >= 0 && stripFree(card.c - left - 1, card.r, card.h)) left++;
|
||||
if (left > 0 && left < narrowest) { card.c -= left; card.w += left; mark(card.r, card.c, left, card.h); }
|
||||
});
|
||||
};
|
||||
// Stat tiles starting on the same row share the tallest height; re-pack until
|
||||
// stable (raising a row can move later cards).
|
||||
@@ -239,7 +256,10 @@ window.rustProxyOverviewRows = (() => {
|
||||
if (other !== card && other.c < card.c + card.w && other.c + other.w > card.c && other.r >= end) next = Math.min(next, other.r);
|
||||
}
|
||||
const extra = next - end;
|
||||
if (extra > 0 && extra <= Math.max(ROW_DIVISIONS * 2, card.h)) { mark(end, card.c, card.w, extra); card.h += extra; }
|
||||
// At the end of the grid a chart or panel always reaches the last row (its
|
||||
// chart grows) instead of leaving an empty area; stat tiles stay limited.
|
||||
const limit = next === bottom && !card.stat ? Infinity : Math.max(ROW_DIVISIONS * 2, card.h);
|
||||
if (extra > 0 && extra <= limit) { mark(end, card.c, card.w, extra); card.h += extra; }
|
||||
}
|
||||
for (const card of cardsModel) {
|
||||
card.node.style.setProperty("--overview-col", String(card.c + 1));
|
||||
|
||||
@@ -2279,12 +2279,34 @@ html:is([data-corner-style=square], [data-corner-style=subtle], [data-corner-sty
|
||||
grid-row: var(--overview-row, auto)/span var(--overview-rows, 1);
|
||||
}
|
||||
|
||||
/* Until the account's layout is applied the grid stays invisible, so the page
|
||||
never shows the factory arrangement first and then jumps (no browser storage). */
|
||||
/* Until the account's layout is applied the cards stay invisible, so the page
|
||||
never shows the factory arrangement first and then jumps (no browser storage).
|
||||
Meanwhile the panel's startup screen is shown in their place (see below). */
|
||||
#panel-overview .dashboard-overview-grid.is-layout-pending {
|
||||
position: relative;
|
||||
min-height: 280px;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
/* The panel's startup screen (logo, spinner, "Loading RustProxy…") is shown in
|
||||
place of the cards while the layout is pending: a zero-height block right
|
||||
before the grid whose content overlays the still invisible grid, so the cards'
|
||||
order and first-child styles are unaffected. */
|
||||
#panel-overview .overview-layout-loading {
|
||||
height: 0;
|
||||
margin-inline: auto;
|
||||
overflow: visible;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#panel-overview .overview-layout-loading>.app-startup-brand {
|
||||
margin-top: 56px;
|
||||
}
|
||||
|
||||
#panel-overview .overview-layout-loading:has(+ .dashboard-overview-grid:not(.is-layout-pending)) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#panel-overview .dashboard-overview-grid.is-measuring-rows>[data-dashboard-widget] {
|
||||
align-self: start !important;
|
||||
}
|
||||
|
||||
@@ -169,6 +169,11 @@
|
||||
id="overview-inline-hidden-count"></span></summary>
|
||||
<div class="overview-inline-hidden-list" id="overview-inline-hidden-list"></div>
|
||||
</details>
|
||||
<div class="app-startup-card overview-layout-loading" role="status" aria-live="polite">
|
||||
<div class="app-startup-brand" aria-hidden="true">r<span>/</span>p</div>
|
||||
<div class="app-startup-spinner" aria-hidden="true"></div>
|
||||
<p class="app-startup-message" data-i18n="startupLoading">Loading RustProxy…</p>
|
||||
</div>
|
||||
<div class="stats-grid dashboard-overview-grid is-layout-pending" id="dashboard-overview-grid">
|
||||
<!-- RP_METRICS_MAIN --><!-- RP_CHARTS_MAIN --><!-- RP_METRICS_WEB --><!-- RP_CHARTS_WEB -->
|
||||
<article class="card" data-dashboard-widget="cache_diagnostics">
|
||||
|
||||
Reference in new issue
Block a user