Browse Source

fix: loosen inspector detail spacing

codex/vue-opencloud-design-system
Rain Mark 2 months ago
parent
commit
93f8b8e10d
  1. 2
      app/web/dist/assets/app.css
  2. 6
      app/web/dist/assets/app.js
  3. 5
      frontend/src/App.vue
  4. 54
      frontend/src/styles/opencloud-app.css
  5. 20
      scripts/ui_playwright_smoke.py

2
app/web/dist/assets/app.css

File diff suppressed because one or more lines are too long

6
app/web/dist/assets/app.js

File diff suppressed because one or more lines are too long

5
frontend/src/App.vue

@ -948,7 +948,10 @@ function getPaginationItems(current: number, total: number): Array<number | "ell
<div v-if="!selectedJob" class="detail-empty"><span class="eyebrow">Inspector</span><h3>Select a job</h3></div>
<template v-else>
<div class="section-heading"><span>Job Detail</span><h3>{{ titleCase(selectedJob.kind) }}</h3></div>
<div class="inspector-storage-summary"><div><span>Status</span><strong>{{ titleCase(selectedJob.status) }}</strong></div><span class="status-chip">{{ selectedJob.attempt_count }}/{{ selectedJob.max_attempts }}</span></div>
<div class="jobs-status-card inspector-card">
<div class="jobs-status-main"><span>Status</span><strong>{{ titleCase(selectedJob.status) }}</strong></div>
<div class="jobs-status-attempts"><span>Attempts</span><strong>{{ selectedJob.attempt_count }}/{{ selectedJob.max_attempts }}</strong></div>
</div>
<div class="meta-list inspector-card jobs-detail-meta">
<div><span>Run after</span><strong>{{ formatDate(selectedJob.run_after) }}</strong></div>
<div><span>Created</span><strong>{{ formatDate(selectedJob.created_at) }}</strong></div>

54
frontend/src/styles/opencloud-app.css

@ -666,6 +666,7 @@ textarea {
.inspector-storage-summary {
margin-top: 0.8rem;
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
padding: 0.62rem 0.68rem;
@ -674,6 +675,22 @@ textarea {
background: color-mix(in srgb, var(--oc-role-surface-container) 48%, var(--oc-role-surface));
}
.inspector-storage-summary > div {
min-width: 0;
display: grid;
gap: 0.22rem;
}
.inspector-storage-summary > div > span {
color: var(--oc-role-on-surface-variant);
font-size: 0.78rem;
}
.inspector-storage-summary > div > strong {
min-width: 0;
overflow-wrap: anywhere;
}
.inspector-info-button {
width: 100%;
margin-top: 0.85rem;
@ -865,8 +882,9 @@ textarea {
}
.jobs-detail-meta > div {
grid-template-columns: 72px minmax(0, 1fr);
grid-template-columns: 76px minmax(0, 1fr);
align-items: start;
column-gap: 0.65rem;
}
.jobs-detail-meta strong {
@ -875,6 +893,40 @@ textarea {
text-align: left;
}
.jobs-status-card {
grid-template-columns: minmax(0, 1fr) auto;
align-items: center;
gap: 0.75rem;
border-style: solid;
}
.jobs-status-main,
.jobs-status-attempts {
min-width: 0;
display: grid;
gap: 0.22rem;
}
.jobs-status-main span,
.jobs-status-attempts span,
.jobs-detail-meta span {
color: var(--oc-role-on-surface-variant);
font-size: 0.78rem;
}
.jobs-status-main strong,
.jobs-status-attempts strong {
min-width: 0;
overflow-wrap: anywhere;
}
.jobs-status-attempts {
min-width: 54px;
justify-items: end;
padding-left: 0.75rem;
border-left: 1px solid color-mix(in srgb, var(--oc-role-outline-variant) 46%, transparent);
}
.notice-text {
color: #008b63;
}

20
scripts/ui_playwright_smoke.py

@ -191,6 +191,7 @@ def run_browser_flow(base_url: str) -> None:
page.locator(".drive-item strong", has_text="playwright-preview.png").first.click()
page.locator("img.preview-frame").wait_for(timeout=15000)
page.locator(".drive-item", has_text="playwright-preview.png").locator(".replica-ratio", has_text="1/1").wait_for()
assert_label_value_layout(page, ".detail-panel .inspector-storage-summary > div", "File inspector summary", failures)
page.screenshot(path=str(OUTPUT_DIR / "files-preview-selected.png"), full_page=True)
page.locator(".drive-item", has_text="playwright-preview.png").get_by_role("button", name="More").click()
page.get_by_role("menu").wait_for()
@ -287,7 +288,7 @@ def run_browser_flow(base_url: str) -> None:
page.locator(".jobs-table-row").first.click()
page.locator(".jobs-detail-panel", has_text="Job Detail").wait_for()
page.locator(".payload-preview").wait_for()
assert_jobs_id_layout(page, failures)
assert_label_value_layout(page, ".jobs-detail-meta > div, .jobs-status-main, .jobs-status-attempts", "Jobs detail", failures)
page.screenshot(path=str(OUTPUT_DIR / "jobs-page.png"), full_page=True)
mobile_context = browser.new_context(viewport={"width": 390, "height": 844})
@ -377,22 +378,23 @@ def assert_pagination_controls(page, label: str, failures: list[str]) -> None:
failures.append(f"{label} page size selector missing")
def assert_jobs_id_layout(page, failures: list[str]) -> None: # type: ignore[no-untyped-def]
overlaps = page.locator(".jobs-detail-meta > div").evaluate_all(
def assert_label_value_layout(page, selector: str, label: str, failures: list[str]) -> None: # type: ignore[no-untyped-def]
layout_errors = page.locator(selector).evaluate_all(
"""(nodes) => nodes
.filter((node) => node.textContent && node.textContent.includes("ID"))
.map((node) => {
const label = node.querySelector("span");
const value = node.querySelector("strong");
if (!label || !value) return false;
if (!label || !value) return "";
const left = label.getBoundingClientRect();
const right = value.getBoundingClientRect();
return !(left.right <= right.left || right.right <= left.left || left.bottom <= right.top || right.bottom <= left.top);
const overlaps = !(left.right <= right.left || right.right <= left.left || left.bottom <= right.top || right.bottom <= left.top);
const sameLineTooTight = Math.abs(left.top - right.top) < 4 && right.left - left.right < 8;
return overlaps || sameLineTooTight ? (node.textContent || "unknown").trim() : "";
})
.some(Boolean)"""
.filter(Boolean)"""
)
if overlaps:
failures.append("Jobs detail ID overlaps its label")
if layout_errors:
failures.append(f"{label} label/value layout is too tight: {layout_errors}")
if __name__ == "__main__":

Loading…
Cancel
Save