Restyled to the same row pattern (one line, details in an expandable body, one action plus a menu, tokens for light and dark): - hardware coverage, audit log (family chips, destructive actions in red, client-side filters, first 300 rows), Clonezilla backups, enrollment packages (active package marked and undeletable, MCL flags with a visible legend) - image Overview, OS and packages, Unattend (sticky save bar), Import - Dell, iPXE, Blancco and BPRT update pages; Dell history links to the rollback review instead of rolling back on one click - boot menu editor (tabs, rows with a menu, lint chips that jump to the line, tokens instead of hard-coded dark colours) and the imaging session detail page Fixes found on the way: - BPRT batch Activate never asked to confirm: a hidden input named 'confirm' shadowed window.confirm in the inline handler, so the form submitted without a prompt. It now calls window.confirm. - Backup upload refused when a backup with that name exists, unless 'Replace the existing backup' is ticked (it overwrote silently). - app.js: em dashes in comments replaced (plain ASCII).
314 lines
19 KiB
HTML
314 lines
19 KiB
HTML
{% extends "base.html" %}
|
|
{% import '_ui.html' as ui %}
|
|
{% block title %}{{ friendly_name }} - Overview{% endblock %}
|
|
|
|
{# Overview of one image: what needs attention first (each item links to the
|
|
tab that fixes it), a calm row of counts, the control files one line each
|
|
(description in the row body), then compact lists of hardware gaps, newer
|
|
Dell packs and missing zips. Deep links into the Drivers tab use its
|
|
?q=, ?os=, ?missing=1 and ?findings=1 filters. #}
|
|
|
|
{% block extra_head %}
|
|
<style>
|
|
/* Needs attention */
|
|
.ov-attn { list-style: none; margin: 0; padding: 0; }
|
|
.ov-attn li { display: grid; grid-template-columns: 74px minmax(0, 1fr) auto; gap: 4px 14px; align-items: start;
|
|
padding: 12px 24px; border-top: 1px solid var(--line); font-size: 14px; line-height: 1.5; }
|
|
.ov-attn li .chip { justify-self: start; margin-top: 1px; }
|
|
.ov-attn li a { white-space: nowrap; font-size: 13px; font-weight: 600; text-decoration: none; }
|
|
.ov-attn li a:hover { text-decoration: underline; }
|
|
.ov-clear { padding: 12px 24px 16px; border-top: 1px solid var(--line); font-size: 14px; color: var(--text-2); display: flex; gap: 10px; align-items: center; }
|
|
.ov-phead { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; padding: 16px 24px 12px; }
|
|
.ov-phead h2 { margin: 0; font-size: 16px; font-weight: 600; }
|
|
.ov-phead .sp { margin-left: auto; font-size: 13px; }
|
|
|
|
/* Summary counts */
|
|
.ov-stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); }
|
|
@container (max-width: 760px) { .ov-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
|
|
.ov-stat { display: flex; flex-direction: column; gap: 4px; padding: 16px 20px; text-decoration: none; color: var(--text);
|
|
border-top: 1px solid var(--line); border-left: 1px solid var(--line); margin: 0 0 -1px -1px; min-width: 0; }
|
|
a.ov-stat:hover { background: var(--surface-2); color: var(--text); }
|
|
.ov-stat .lbl { font-size: 13px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
.ov-stat .st { display: flex; flex-wrap: wrap; gap: 6px; }
|
|
.ov-stat .val { font-size: 24px; font-weight: 600; line-height: 1.2; }
|
|
.ov-stat .val.txt { font-size: 15px; line-height: 1.35; }
|
|
.ov-stat .det { font-size: 12px; color: var(--muted); }
|
|
.ov-stats-wrap { overflow: hidden; border-radius: 0 0 14px 14px; container-type: inline-size; }
|
|
|
|
/* Control files: one line each */
|
|
.cf-grid { display: grid; grid-template-columns: minmax(0, 1fr) 190px 80px 72px 14px; align-items: center; gap: 0 14px;
|
|
padding: 11px 100px 11px 24px; font-size: 14px; min-height: 24px; }
|
|
.cf-head { font-size: 12px; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); padding-top: 6px; padding-bottom: 6px; }
|
|
.cf-grid .r { text-align: right; white-space: nowrap; }
|
|
.cf-grid .when { white-space: nowrap; font-size: 13px; color: var(--text-2); }
|
|
.cf-body { padding: 0 24px 14px 24px; font-size: 13px; color: var(--text-2); display: flex; flex-direction: column; gap: 4px; }
|
|
.cf-body .mono { overflow-wrap: anywhere; color: var(--muted); }
|
|
.ui-row > .act { top: 8px; }
|
|
|
|
/* Compact lists */
|
|
.ov-cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(420px, 100%), 1fr)); gap: 22px; align-items: start; }
|
|
.cl { list-style: none; margin: 0; padding: 0; }
|
|
.cl li { display: grid; grid-template-columns: minmax(0, 1fr) auto auto; gap: 0 12px; align-items: center; padding: 10px 24px; border-top: 1px solid var(--line); font-size: 14px; }
|
|
.cl li .when { font-size: 12px; color: var(--muted); white-space: nowrap; }
|
|
.cl li a.rowlink { color: var(--text); text-decoration: none; min-width: 0; }
|
|
.cl li a.rowlink:hover { color: var(--accent); }
|
|
.cl .pos { color: var(--muted); font-size: 12px; }
|
|
.cl-more { padding: 10px 24px 14px; border-top: 1px solid var(--line); font-size: 13px; }
|
|
.cl-empty { padding: 12px 24px 16px; border-top: 1px solid var(--line); font-size: 13px; color: var(--muted); }
|
|
|
|
@container (max-width: 760px) {
|
|
.cf-grid { grid-template-columns: minmax(0, 1fr) auto 14px; padding: 11px 90px 11px 16px; }
|
|
.cf-grid .c-size, .cf-grid .c-bak { display: none; }
|
|
.cf-head { display: none; }
|
|
.cf-body { padding: 0 16px 14px; }
|
|
}
|
|
@container (max-width: 480px) {
|
|
.cf-grid { grid-template-columns: minmax(0, 1fr) 14px; }
|
|
.cf-grid .when { grid-column: 1; grid-row: 2; }
|
|
.cf-grid .chev { grid-column: 2; grid-row: 1; }
|
|
}
|
|
@media (max-width: 600px) {
|
|
.ov-attn li { grid-template-columns: auto minmax(0, 1fr); padding: 12px 16px; }
|
|
.ov-attn li a { grid-column: 2; }
|
|
.ov-phead, .ov-clear, .cl li, .cl-more, .cl-empty { padding-left: 16px; padding-right: 16px; }
|
|
.cl li { grid-template-columns: minmax(0, 1fr) auto; }
|
|
.cl li .when { grid-column: 1; }
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
{% set header_actions %}
|
|
<a href="{{ url_for('hardware_page') }}" class="ui-btn">Hardware coverage</a>
|
|
<a href="{{ url_for('dell_updates.page') }}" class="ui-btn">Dell updates</a>
|
|
{% endset %}
|
|
{% include "_image_header.html" %}
|
|
|
|
{% set d = ov.drivers %}
|
|
{% set lint = ov.lint %}
|
|
{# missing-zip findings are counted on their own; bad_lint is what is wrong in the map itself #}
|
|
{% set bad_lint = lint.map_errors if lint.available else 0 %}
|
|
{% set os_row = ov.os.current %}
|
|
{% set drv_url = image_tab_url(image_type, 'drivers') %}
|
|
{% set os_url = image_tab_url(image_type, 'os') %}
|
|
|
|
{# -------- needs attention: (kind, text, link, label) -------- #}
|
|
{% set attn = [] %}
|
|
{% if d.missing %}{% set _ = attn.append(('err', d.missing | length ~ ' driver entr' ~ ('ies point' if d.missing | length != 1 else 'y points') ~ ' at a zip that is not on the share. PESetup only warns, and the PC comes up without NIC or WiFi drivers.', image_tab_url(image_type, 'drivers', missing=1), 'Drivers')) %}{% endif %}
|
|
{% if bad_lint %}{% set _ = attn.append(('err', bad_lint ~ ' driver map lint error' ~ ('s' if bad_lint != 1) ~ ' in the entries themselves (a token or order PESetup will mis-match; see the findings on the Drivers tab).', image_tab_url(image_type, 'drivers', findings=1), 'Drivers')) %}{% endif %}
|
|
{% if not os_row %}{% set _ = attn.append(('err', 'OperatingSystem.json has no entry: PESetup has no Windows image to apply.', os_url, 'OS and Packages')) %}
|
|
{% elif not os_row.wim.exists %}{% set _ = attn.append(('err', 'install.wim is not on disk in ' ~ (os_row.dest or 'the configured folder (DestinationDir is empty)') ~ '.', os_url, 'OS and Packages')) %}{% endif %}
|
|
{% if ov.packages.missing %}{% set _ = attn.append(('warn', ov.packages.missing | length ~ ' enabled package file' ~ ('s are' if ov.packages.missing | length != 1 else ' is') ~ ' not on disk.', os_url, 'OS and Packages')) %}{% endif %}
|
|
{% if ov.gaps %}{% set _ = attn.append(('warn', ov.gaps | length ~ ' model' ~ ('s' if ov.gaps | length != 1) ~ ' booted WinPE here with no working driver entry in this image.', url_for('hardware_page'), 'Hardware coverage')) %}{% endif %}
|
|
{% if ov.unattend_override %}{% set _ = attn.append(('warn', 'Deploy/Tools/GE/XML/FlatUnattendW10.xml exists. PESetup loads that file first, so the Unattend tab (which edits Deploy/FlatUnattendW10.xml) is not what imaging uses.', image_tab_url(image_type, 'unattend'), 'Unattend')) %}{% endif %}
|
|
{% if ov.hw_drivers_extra %}{% set _ = attn.append(('neutral', 'Deploy/Control/hw_drivers.json exists. PESetup never reads it; only HardwareDriver.json decides drivers.', drv_url, 'Drivers')) %}{% endif %}
|
|
{% if ov.dell.rows %}{% set _ = attn.append(('neutral', ov.dell.rows | length ~ ' driver pack' ~ ('s have' if ov.dell.rows | length != 1 else ' has') ~ ' a newer Dell release (last check ' ~ (ov.dell.checked | local_dt) ~ ').', url_for('dell_updates.page'), 'Dell updates')) %}{% endif %}
|
|
{% if ov.models.no_entry %}{% set _ = attn.append(('neutral', ov.models.no_entry | length ~ ' hardware model selection' ~ ('s name' if ov.models.no_entry | length != 1 else ' names') ~ ' a driver family with no entry in HardwareDriver.json (media build selection only; imaging does not use it).', os_url, 'OS and Packages')) %}{% endif %}
|
|
{% set kind_label = {'err': 'Error', 'warn': 'Warning', 'neutral': 'Note'} %}
|
|
{% set n_err = attn | selectattr(0, 'equalto', 'err') | list | length %}
|
|
|
|
<div class="ui-stack">
|
|
|
|
<section class="panel" aria-labelledby="ov-attn-h">
|
|
<div class="ov-phead">
|
|
<h2 id="ov-attn-h">Needs attention</h2>
|
|
{% if n_err %}{{ ui.chip(n_err ~ ' error' ~ ('s' if n_err != 1), 'err') }}{% endif %}
|
|
{% if attn | length > n_err %}{{ ui.chip((attn | length - n_err) ~ ' other', 'neutral') }}{% endif %}
|
|
</div>
|
|
{% if attn %}
|
|
<ul class="ov-attn">
|
|
{% for kind, text, link, label in attn %}
|
|
<li>{{ ui.chip(kind_label[kind], kind) }}<span>{{ text }}</span><a href="{{ link }}">Open {{ label }}</a></li>
|
|
{% endfor %}
|
|
</ul>
|
|
{% else %}
|
|
<div class="ov-clear">{{ ui.chip('All clear', 'ok') }} Driver zips, install.wim and packages are all on disk and no seen model is missing drivers.</div>
|
|
{% endif %}
|
|
</section>
|
|
|
|
{# -------- summary counts -------- #}
|
|
<section class="panel" aria-labelledby="ov-sum-h">
|
|
<div class="ov-phead"><h2 id="ov-sum-h">At a glance</h2></div>
|
|
<div class="ov-stats-wrap"><div class="ov-stats">
|
|
{% for osn in ('Win11', 'Win10') %}
|
|
<a class="ov-stat" href="{{ image_tab_url(image_type, 'drivers', os=osn) }}">
|
|
<span class="lbl">{{ osn }} driver entries</span>
|
|
<span class="val">{{ d.by_os[osn] }}</span>
|
|
<span class="st">{{ ui.chip(osn, 'w11' if osn == 'Win11' else 'w10') }}{% if not d.total and osn == 'Win11' %} {{ ui.chip('Map empty', 'err') }}{% endif %}</span>
|
|
<span class="det">of {{ d.total }} entries{% if d.by_os.Other and osn == 'Win10' %}, {{ d.by_os.Other }} other{% endif %}</span>
|
|
</a>
|
|
{% endfor %}
|
|
<a class="ov-stat" href="{{ image_tab_url(image_type, 'drivers', missing=1) if d.missing else drv_url }}">
|
|
<span class="lbl">Zips missing</span>
|
|
<span class="val">{{ d.missing | length }}</span>
|
|
<span class="st">{{ ui.chip('Missing', 'err') if d.missing else ui.chip('OK', 'ok') }}</span>
|
|
<span class="det">{% if d.missing %}{{ d.missing | selectattr('os', 'equalto', 'Win11') | list | length }} of them Win11{% else %}every zip is on the share{% endif %}</span>
|
|
</a>
|
|
<a class="ov-stat" href="{{ image_tab_url(image_type, 'drivers', findings=1) if lint.available and lint.total else drv_url }}">
|
|
{% if lint.available %}
|
|
<span class="lbl">Map lint</span>
|
|
<span class="val">{{ lint.total }}</span>
|
|
<span class="st">{{ ui.chip('Errors', 'err') if bad_lint else (ui.chip('Findings', 'warn') if lint.total else ui.chip('Clean', 'ok')) }}</span>
|
|
<span class="det">{% for lv, n in lint.by_level | dictsort %}{{ n }} {{ lv | lower }}{% if not loop.last %}, {% endif %}{% else %}no findings{% endfor %}</span>
|
|
{% else %}
|
|
<span class="lbl">Map lint</span>
|
|
<span class="st">{{ ui.chip('Not available', 'off') }}</span>
|
|
<span class="det">Shown once the Drivers tab lint is installed</span>
|
|
{% endif %}
|
|
</a>
|
|
<a class="ov-stat" href="{{ os_url }}">
|
|
{% if os_row %}
|
|
<span class="lbl">Operating system</span>
|
|
<span class="val txt">{{ os_row.osv.productName or os_row.osv.marketingName }} {{ os_row.osv.versionNumber }}</span>
|
|
<span class="st">{{ ui.chip('On disk', 'ok') if os_row.wim.exists else ui.chip('wim missing', 'err') }}</span>
|
|
<span class="det">osId {{ os_row.osv.id }}{% if os_row.wim.exists %}, install.wim {{ (os_row.wim.size / 1073741824) | round(1) }} GB{% endif %}</span>
|
|
{% else %}
|
|
<span class="lbl">Operating system</span>
|
|
<span class="st">{{ ui.chip('None', 'err') }}</span>
|
|
<span class="det">OperatingSystem.json is empty or missing</span>
|
|
{% endif %}
|
|
</a>
|
|
<a class="ov-stat" href="{{ os_url }}">
|
|
<span class="lbl">Packages</span>
|
|
<span class="val">{{ ov.packages.enabled }} <span class="det">of {{ ov.packages.total }} enabled</span></span>
|
|
<span class="st">{{ ui.chip('File missing', 'warn') if ov.packages.missing else ui.chip('OK', 'ok') }}</span>
|
|
<span class="det">{% if ov.packages.missing %}{{ ov.packages.missing | length }} enabled file{{ 's' if ov.packages.missing | length != 1 }} missing{% else %}all enabled files on disk{% endif %}</span>
|
|
</a>
|
|
<a class="ov-stat" href="{{ url_for('hardware_page') }}">
|
|
{% if ov.gaps_checked %}
|
|
<span class="lbl">Hardware gaps</span>
|
|
<span class="val">{{ ov.gaps | length }}</span>
|
|
<span class="st">{{ ui.chip('Gaps', 'warn') if ov.gaps else ui.chip('OK', 'ok') }}</span>
|
|
<span class="det">seen models with no working driver entry</span>
|
|
{% else %}
|
|
<span class="lbl">Hardware gaps</span>
|
|
<span class="st">{{ ui.chip('Not tracked', 'off') }}</span>
|
|
<span class="det">Hardware coverage checks the three GE images only</span>
|
|
{% endif %}
|
|
</a>
|
|
<a class="ov-stat" href="{{ url_for('dell_updates.page') }}">
|
|
{% if ov.dell.checked %}
|
|
<span class="lbl">Dell updates</span>
|
|
<span class="val">{{ ov.dell.rows | length }}</span>
|
|
<span class="st">{{ ui.chip('Newer packs', 'neutral') if ov.dell.rows else ui.chip('Current', 'ok') }}</span>
|
|
<span class="det">checked {{ ov.dell.checked | local_dt }}</span>
|
|
{% else %}
|
|
<span class="lbl">Dell updates</span>
|
|
<span class="st">{{ ui.chip('Not checked', 'off') }}</span>
|
|
<span class="det">Run a check on Updates, Dell drivers and BIOS</span>
|
|
{% endif %}
|
|
</a>
|
|
</div></div>
|
|
</section>
|
|
|
|
{# -------- control files -------- #}
|
|
<section class="panel ui-list" aria-labelledby="ov-cf-h">
|
|
<div class="ov-phead"><h2 id="ov-cf-h">Control files</h2></div>
|
|
<div class="cf-grid cf-head" aria-hidden="true"><span>File</span><span>Last modified</span><span class="r c-size">Size</span><span class="r c-bak">Backups</span><span></span></div>
|
|
{% for f in ov.files %}
|
|
<div class="ui-row">
|
|
<details>
|
|
<summary class="cf-grid">
|
|
<span class="tip" data-tip="{{ f.rel }}"><span class="trunc mono">{{ f.name }}</span></span>
|
|
<span class="when">{% if f.exists %}{{ f.mtime | local_dt }}{% else %}{{ ui.chip('Missing', 'err') }}{% endif %}</span>
|
|
<span class="r mono c-size">{% if f.exists %}{{ f.size | filesizeformat }}{% else %}-{% endif %}</span>
|
|
<span class="r mono c-bak">{{ f.backups or '-' }}</span>
|
|
<span class="chev" aria-hidden="true">▾</span>
|
|
</summary>
|
|
<div class="cf-body">
|
|
<span>{{ f.what }}</span>
|
|
<span class="mono">{{ f.rel }}</span>
|
|
<span>{% if f.exists %}{{ f.size | filesizeformat }}, {% endif %}{{ f.backups }} backup{{ 's' if f.backups != 1 }} next to the file</span>
|
|
</div>
|
|
</details>
|
|
<div class="act"><a class="ui-btn sm" href="{{ image_tab_url(image_type, f.tab) }}" aria-label="Open {{ f.name }}">Open</a></div>
|
|
</div>
|
|
{% endfor %}
|
|
{% set saves = ov.last_saves | dictsort | selectattr(1) | list %}
|
|
{% if saves %}
|
|
<div class="hidden-bar">
|
|
<span>Last saves from this app:
|
|
{% for scope, rec in saves %}{{ {'drivers': 'Drivers', 'unattend': 'Unattend', 'os': 'OS and Packages'}[scope] }} {{ rec.ts | local_dt }} ({{ rec.files | map(attribute='name') | join(', ') }}){% if not loop.last %}; {% endif %}{% endfor %}</span>
|
|
</div>
|
|
{% endif %}
|
|
</section>
|
|
|
|
<div class="ov-cols">
|
|
{# -------- coverage gaps -------- #}
|
|
<section class="panel" aria-labelledby="ov-gap-h">
|
|
<div class="ov-phead">
|
|
<h2 id="ov-gap-h">Hardware coverage gaps</h2>
|
|
{% if ov.gaps %}{{ ui.chip(ov.gaps | length, 'warn') }}{% endif %}
|
|
<a class="sp" href="{{ url_for('hardware_page') }}">Hardware coverage</a>
|
|
</div>
|
|
{% if not ov.gaps_checked %}
|
|
<div class="cl-empty">This image is not one of the images Hardware coverage checks.</div>
|
|
{% elif ov.gaps %}
|
|
<ul class="cl">
|
|
{% for g in ov.gaps[:8] %}
|
|
<li>
|
|
<a class="rowlink tip" href="{{ image_tab_url(image_type, 'drivers', q=g.model) }}" data-tip="{{ g.model }}{% if g.skus %} (SKU {{ g.skus | join(', ') }}){% endif %}{% if g.file %}: {{ g.file }}{% endif %}">
|
|
<span class="trunc mono">{{ g.model }}</span></a>
|
|
{% if g.status == 'no-entry' %}{{ ui.chip('No entry', 'err') }}{% else %}{{ ui.chip('Zip missing', 'err') }}{% endif %}
|
|
<span class="when">{{ g.last_seen | local_dt }}</span>
|
|
</li>
|
|
{% endfor %}
|
|
</ul>
|
|
{% if ov.gaps | length > 8 %}<div class="cl-more"><a href="{{ url_for('hardware_page') }}">{{ ov.gaps | length - 8 }} more on Hardware coverage</a></div>{% endif %}
|
|
{% else %}
|
|
<div class="cl-empty">Every model that has booted WinPE here matches a driver entry with its zip on disk.</div>
|
|
{% endif %}
|
|
</section>
|
|
|
|
{# -------- dell hints -------- #}
|
|
<section class="panel" aria-labelledby="ov-dell-h">
|
|
<div class="ov-phead">
|
|
<h2 id="ov-dell-h">Newer Dell driver packs</h2>
|
|
{% if ov.dell.rows %}{{ ui.chip(ov.dell.rows | length, 'neutral') }}{% endif %}
|
|
<a class="sp" href="{{ url_for('dell_updates.page') }}">Dell updates</a>
|
|
</div>
|
|
{% if ov.dell.rows %}
|
|
<ul class="cl">
|
|
{% for r in ov.dell.rows[:8] %}
|
|
<li>
|
|
<a class="rowlink tip" href="{{ image_tab_url(image_type, 'drivers', q=r.file) }}" data-tip="Entry {{ r.pos }}: {{ r.file }}{% if r.models %} ({{ r.models }}){% endif %}">
|
|
<span class="trunc"><span class="pos">{{ r.pos }}</span> <span class="mono">{{ r.file }}</span></span></a>
|
|
{{ ui.chip(r.ver or 'newer', 'neutral') }}
|
|
<span></span>
|
|
</li>
|
|
{% endfor %}
|
|
</ul>
|
|
{% if ov.dell.rows | length > 8 %}<div class="cl-more"><a href="{{ url_for('dell_updates.page') }}">{{ ov.dell.rows | length - 8 }} more on Dell updates</a></div>{% endif %}
|
|
{% elif ov.dell.checked %}
|
|
<div class="cl-empty">Every driver pack in this map is the newest Dell has (checked {{ ov.dell.checked | local_dt }}).</div>
|
|
{% else %}
|
|
<div class="cl-empty">Not checked yet. Run a check on Updates, Dell drivers and BIOS.</div>
|
|
{% endif %}
|
|
</section>
|
|
|
|
{# -------- missing zips -------- #}
|
|
{% if d.missing %}
|
|
<section class="panel" aria-labelledby="ov-miss-h">
|
|
<div class="ov-phead">
|
|
<h2 id="ov-miss-h">Zips not on the share</h2>
|
|
{{ ui.chip(d.missing | length, 'err') }}
|
|
<a class="sp" href="{{ image_tab_url(image_type, 'drivers', missing=1) }}">Drivers</a>
|
|
</div>
|
|
<ul class="cl">
|
|
{% for m in d.missing[:8] %}
|
|
<li>
|
|
<a class="rowlink tip" href="{{ image_tab_url(image_type, 'drivers', q=m.file) if m.file else image_tab_url(image_type, 'drivers', missing=1) }}" data-tip="Entry {{ m.pos }}: {{ m.file or '(no file name)' }}{% if m.models %} ({{ m.models }}){% endif %}">
|
|
<span class="trunc"><span class="pos">{{ m.pos }}</span> <span class="mono">{{ m.file or '(no file name)' }}</span></span></a>
|
|
{{ ui.chip(m.os, 'w11' if m.os == 'Win11' else 'w10') }}
|
|
<span></span>
|
|
</li>
|
|
{% endfor %}
|
|
</ul>
|
|
{% if d.missing | length > 8 %}<div class="cl-more"><a href="{{ image_tab_url(image_type, 'drivers', missing=1) }}">{{ d.missing | length - 8 }} more on the Drivers tab</a></div>{% endif %}
|
|
</section>
|
|
{% endif %}
|
|
</div>
|
|
|
|
</div>
|
|
{% endblock %}
|