Spaces:
Running
Running
deploy: 同步 UI 驗收版本
Browse files- docs/assets/desktop.png +2 -2
- scripts/verify_ui_layout.py +136 -2
- src/doc_inspector/ui.py +102 -23
- tests/test_ui.py +33 -0
docs/assets/desktop.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
scripts/verify_ui_layout.py
CHANGED
|
@@ -52,13 +52,61 @@ def _metrics(locator: Locator) -> dict[str, float | str]:
|
|
| 52 |
width: Math.round(box.width),
|
| 53 |
height: Math.round(box.height),
|
| 54 |
fontSize: style.fontSize,
|
| 55 |
-
|
|
|
|
|
|
|
| 56 |
};
|
| 57 |
}"""
|
| 58 |
)
|
| 59 |
|
| 60 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
def _page_metrics(page: Page) -> dict:
|
|
|
|
| 62 |
metrics = {
|
| 63 |
"viewportWidth": page.evaluate("window.innerWidth"),
|
| 64 |
"documentWidth": page.evaluate("document.documentElement.scrollWidth"),
|
|
@@ -70,11 +118,18 @@ def _page_metrics(page: Page) -> dict:
|
|
| 70 |
"resultLegend": _metrics(page.locator(".result-legend").first),
|
| 71 |
"legendTitle": _metrics(page.locator(".result-legend h2").first),
|
| 72 |
"legendItem": _metrics(page.locator(".legend-items li").first),
|
|
|
|
| 73 |
"legendLabel": _metrics(page.locator(".legend-items strong").first),
|
|
|
|
|
|
|
| 74 |
"sourceHeading": _metrics(page.locator(".source-brief h3").first),
|
| 75 |
"sourceText": _metrics(page.locator(".source-brief p").first),
|
| 76 |
"demoHeading": _metrics(page.locator(".demo-heading h3").first),
|
| 77 |
"demoText": _metrics(page.locator(".demo-heading p").first),
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
"demoSelectorAccessible": page.get_by_label("範例文件").is_visible(),
|
| 79 |
"documentPickerLabelVisible": page.locator(
|
| 80 |
"#document-upload"
|
|
@@ -91,6 +146,7 @@ def _page_metrics(page: Page) -> dict:
|
|
| 91 |
"settingsPanel": _metrics(page.locator(".settings-section").first),
|
| 92 |
"actionPanel": _metrics(page.locator(".action-section").first),
|
| 93 |
"taskHeading": _metrics(page.locator(".task-heading").first),
|
|
|
|
| 94 |
"taskTitle": _metrics(page.locator(".task-heading h2").first),
|
| 95 |
"taskCopy": _metrics(page.locator(".task-heading p").first),
|
| 96 |
"fieldLabel": _metrics(
|
|
@@ -540,14 +596,62 @@ def main() -> None:
|
|
| 540 |
encoding="utf-8",
|
| 541 |
)
|
| 542 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 543 |
assert desktop["viewportWidth"] == desktop["documentWidth"]
|
| 544 |
assert mobile["viewportWidth"] == mobile["documentWidth"]
|
| 545 |
assert float(desktop["bodyText"]["fontSize"].removesuffix("px")) >= 20
|
| 546 |
assert float(desktop["legendTitle"]["fontSize"].removesuffix("px")) >= 22
|
| 547 |
assert float(desktop["legendItem"]["fontSize"].removesuffix("px")) >= 18
|
| 548 |
-
assert float(desktop["legendLabel"]["fontSize"].removesuffix("px")) >=
|
|
|
|
|
|
|
|
|
|
|
|
|
| 549 |
assert float(desktop["sourceText"]["fontSize"].removesuffix("px")) >= 19
|
| 550 |
assert float(desktop["demoText"]["fontSize"].removesuffix("px")) >= 18
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 551 |
assert float(desktop["taskTitle"]["fontSize"].removesuffix("px")) >= 28
|
| 552 |
assert float(desktop["taskCopy"]["fontSize"].removesuffix("px")) >= 20
|
| 553 |
assert float(desktop["fieldLabel"]["fontSize"].removesuffix("px")) >= 18
|
|
@@ -558,7 +662,27 @@ def main() -> None:
|
|
| 558 |
assert float(desktop["statusText"]["fontSize"].removesuffix("px")) >= 19
|
| 559 |
assert float(mobile["bodyText"]["fontSize"].removesuffix("px")) >= 19
|
| 560 |
assert float(mobile["legendItem"]["fontSize"].removesuffix("px")) >= 18
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 561 |
assert float(mobile["sourceText"]["fontSize"].removesuffix("px")) >= 19
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 562 |
assert float(mobile["fieldLabel"]["fontSize"].removesuffix("px")) >= 18
|
| 563 |
assert desktop["masthead"]["height"] <= 150
|
| 564 |
assert desktop["resultLegend"]["height"] <= 140
|
|
@@ -569,6 +693,16 @@ def main() -> None:
|
|
| 569 |
assert desktop["uploadPromptInsetTop"] >= 6
|
| 570 |
assert desktop["uploadPromptInsetBottom"] >= 6
|
| 571 |
assert desktop["documentHeight"] <= 1900
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 572 |
assert (
|
| 573 |
abs(
|
| 574 |
desktop["settingsPanel"]["y"]
|
|
|
|
| 52 |
width: Math.round(box.width),
|
| 53 |
height: Math.round(box.height),
|
| 54 |
fontSize: style.fontSize,
|
| 55 |
+
fontWeight: style.fontWeight,
|
| 56 |
+
lineHeight: style.lineHeight,
|
| 57 |
+
borderTopWidth: style.borderTopWidth
|
| 58 |
};
|
| 59 |
}"""
|
| 60 |
)
|
| 61 |
|
| 62 |
|
| 63 |
+
def _all_metrics(locator: Locator) -> list[dict[str, float | str]]:
|
| 64 |
+
return locator.evaluate_all(
|
| 65 |
+
"""elements => elements.map(element => {
|
| 66 |
+
const box = element.getBoundingClientRect();
|
| 67 |
+
const style = window.getComputedStyle(element);
|
| 68 |
+
return {
|
| 69 |
+
x: Math.round(box.x),
|
| 70 |
+
y: Math.round(box.y),
|
| 71 |
+
width: Math.round(box.width),
|
| 72 |
+
height: Math.round(box.height),
|
| 73 |
+
fontSize: style.fontSize,
|
| 74 |
+
fontWeight: style.fontWeight,
|
| 75 |
+
lineHeight: style.lineHeight,
|
| 76 |
+
borderTopWidth: style.borderTopWidth
|
| 77 |
+
};
|
| 78 |
+
})"""
|
| 79 |
+
)
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
def _dropdown_metrics(page: Page) -> dict:
|
| 83 |
+
combo = page.locator("#demo-selector [role='combobox']").first
|
| 84 |
+
combo.click()
|
| 85 |
+
listbox = page.locator('#demo-selector [role="listbox"]').first
|
| 86 |
+
option = page.locator(
|
| 87 |
+
'#demo-selector [role="listbox"] [role="option"]'
|
| 88 |
+
).first
|
| 89 |
+
option.wait_for(state="visible")
|
| 90 |
+
metrics = {
|
| 91 |
+
"control": _metrics(combo),
|
| 92 |
+
"listbox": _metrics(listbox),
|
| 93 |
+
"option": _metrics(option),
|
| 94 |
+
"frontmostRole": option.evaluate(
|
| 95 |
+
"""element => {
|
| 96 |
+
const box = element.getBoundingClientRect();
|
| 97 |
+
return document.elementFromPoint(
|
| 98 |
+
box.left + 16,
|
| 99 |
+
box.top + box.height / 2
|
| 100 |
+
)?.getAttribute("role") ?? "";
|
| 101 |
+
}"""
|
| 102 |
+
),
|
| 103 |
+
}
|
| 104 |
+
page.keyboard.press("Escape")
|
| 105 |
+
return metrics
|
| 106 |
+
|
| 107 |
+
|
| 108 |
def _page_metrics(page: Page) -> dict:
|
| 109 |
+
dropdown = _dropdown_metrics(page)
|
| 110 |
metrics = {
|
| 111 |
"viewportWidth": page.evaluate("window.innerWidth"),
|
| 112 |
"documentWidth": page.evaluate("document.documentElement.scrollWidth"),
|
|
|
|
| 118 |
"resultLegend": _metrics(page.locator(".result-legend").first),
|
| 119 |
"legendTitle": _metrics(page.locator(".result-legend h2").first),
|
| 120 |
"legendItem": _metrics(page.locator(".legend-items li").first),
|
| 121 |
+
"legendCards": _all_metrics(page.locator(".legend-items li")),
|
| 122 |
"legendLabel": _metrics(page.locator(".legend-items strong").first),
|
| 123 |
+
"legendAction": _metrics(page.locator(".legend-action").first),
|
| 124 |
+
"legendDots": _all_metrics(page.locator(".legend-dot")),
|
| 125 |
"sourceHeading": _metrics(page.locator(".source-brief h3").first),
|
| 126 |
"sourceText": _metrics(page.locator(".source-brief p").first),
|
| 127 |
"demoHeading": _metrics(page.locator(".demo-heading h3").first),
|
| 128 |
"demoText": _metrics(page.locator(".demo-heading p").first),
|
| 129 |
+
"demoDropdownControl": dropdown["control"],
|
| 130 |
+
"demoDropdownListbox": dropdown["listbox"],
|
| 131 |
+
"demoDropdownOption": dropdown["option"],
|
| 132 |
+
"demoDropdownFrontmostRole": dropdown["frontmostRole"],
|
| 133 |
"demoSelectorAccessible": page.get_by_label("範例文件").is_visible(),
|
| 134 |
"documentPickerLabelVisible": page.locator(
|
| 135 |
"#document-upload"
|
|
|
|
| 146 |
"settingsPanel": _metrics(page.locator(".settings-section").first),
|
| 147 |
"actionPanel": _metrics(page.locator(".action-section").first),
|
| 148 |
"taskHeading": _metrics(page.locator(".task-heading").first),
|
| 149 |
+
"stepMarkers": _all_metrics(page.locator(".task-step")),
|
| 150 |
"taskTitle": _metrics(page.locator(".task-heading h2").first),
|
| 151 |
"taskCopy": _metrics(page.locator(".task-heading p").first),
|
| 152 |
"fieldLabel": _metrics(
|
|
|
|
| 596 |
encoding="utf-8",
|
| 597 |
)
|
| 598 |
|
| 599 |
+
desktop_step_x = [marker["x"] for marker in desktop["stepMarkers"]]
|
| 600 |
+
mobile_step_x = [marker["x"] for marker in mobile["stepMarkers"]]
|
| 601 |
+
desktop_dot_widths = [dot["width"] for dot in desktop["legendDots"]]
|
| 602 |
+
desktop_dot_heights = [dot["height"] for dot in desktop["legendDots"]]
|
| 603 |
+
mobile_dot_widths = [dot["width"] for dot in mobile["legendDots"]]
|
| 604 |
+
mobile_dot_heights = [dot["height"] for dot in mobile["legendDots"]]
|
| 605 |
+
|
| 606 |
+
for viewport_name, metrics in (
|
| 607 |
+
("wide", wide),
|
| 608 |
+
("desktop", desktop),
|
| 609 |
+
("mobile", mobile),
|
| 610 |
+
):
|
| 611 |
+
legend_cards = metrics["legendCards"]
|
| 612 |
+
assert len(legend_cards) == 3, (
|
| 613 |
+
f"{viewport_name} viewport should render exactly three legend cards"
|
| 614 |
+
)
|
| 615 |
+
legend_widths = [card["width"] for card in legend_cards]
|
| 616 |
+
legend_heights = [card["height"] for card in legend_cards]
|
| 617 |
+
assert max(legend_widths) - min(legend_widths) <= 1, (
|
| 618 |
+
f"{viewport_name} legend cards should have equal widths: {legend_widths}"
|
| 619 |
+
)
|
| 620 |
+
assert max(legend_heights) - min(legend_heights) <= 1, (
|
| 621 |
+
f"{viewport_name} legend cards should have equal heights: {legend_heights}"
|
| 622 |
+
)
|
| 623 |
+
|
| 624 |
assert desktop["viewportWidth"] == desktop["documentWidth"]
|
| 625 |
assert mobile["viewportWidth"] == mobile["documentWidth"]
|
| 626 |
assert float(desktop["bodyText"]["fontSize"].removesuffix("px")) >= 20
|
| 627 |
assert float(desktop["legendTitle"]["fontSize"].removesuffix("px")) >= 22
|
| 628 |
assert float(desktop["legendItem"]["fontSize"].removesuffix("px")) >= 18
|
| 629 |
+
assert float(desktop["legendLabel"]["fontSize"].removesuffix("px")) >= 22
|
| 630 |
+
assert float(desktop["legendAction"]["fontSize"].removesuffix("px")) >= 20
|
| 631 |
+
assert len(desktop["legendDots"]) == 3
|
| 632 |
+
assert desktop_dot_widths == [18, 18, 18]
|
| 633 |
+
assert desktop_dot_heights == [18, 18, 18]
|
| 634 |
assert float(desktop["sourceText"]["fontSize"].removesuffix("px")) >= 19
|
| 635 |
assert float(desktop["demoText"]["fontSize"].removesuffix("px")) >= 18
|
| 636 |
+
assert (
|
| 637 |
+
float(desktop["demoDropdownControl"]["fontSize"].removesuffix("px"))
|
| 638 |
+
>= 21
|
| 639 |
+
)
|
| 640 |
+
assert (
|
| 641 |
+
float(desktop["demoDropdownOption"]["fontSize"].removesuffix("px"))
|
| 642 |
+
>= 20
|
| 643 |
+
)
|
| 644 |
+
assert desktop["demoDropdownOption"]["height"] >= 44
|
| 645 |
+
assert (
|
| 646 |
+
desktop["demoDropdownListbox"]["y"]
|
| 647 |
+
>= desktop["demoDropdownControl"]["y"]
|
| 648 |
+
+ desktop["demoDropdownControl"]["height"]
|
| 649 |
+
)
|
| 650 |
+
assert desktop["demoDropdownFrontmostRole"] == "option"
|
| 651 |
+
assert len(desktop["stepMarkers"]) == 4
|
| 652 |
+
assert all(marker["width"] >= 46 for marker in desktop["stepMarkers"])
|
| 653 |
+
assert all(marker["height"] >= 46 for marker in desktop["stepMarkers"])
|
| 654 |
+
assert max(desktop_step_x) - min(desktop_step_x) <= 1
|
| 655 |
assert float(desktop["taskTitle"]["fontSize"].removesuffix("px")) >= 28
|
| 656 |
assert float(desktop["taskCopy"]["fontSize"].removesuffix("px")) >= 20
|
| 657 |
assert float(desktop["fieldLabel"]["fontSize"].removesuffix("px")) >= 18
|
|
|
|
| 662 |
assert float(desktop["statusText"]["fontSize"].removesuffix("px")) >= 19
|
| 663 |
assert float(mobile["bodyText"]["fontSize"].removesuffix("px")) >= 19
|
| 664 |
assert float(mobile["legendItem"]["fontSize"].removesuffix("px")) >= 18
|
| 665 |
+
assert float(mobile["legendLabel"]["fontSize"].removesuffix("px")) >= 21
|
| 666 |
+
assert float(mobile["legendAction"]["fontSize"].removesuffix("px")) >= 19
|
| 667 |
+
assert len(mobile["legendDots"]) == 3
|
| 668 |
+
assert mobile_dot_widths == [18, 18, 18]
|
| 669 |
+
assert mobile_dot_heights == [18, 18, 18]
|
| 670 |
assert float(mobile["sourceText"]["fontSize"].removesuffix("px")) >= 19
|
| 671 |
+
assert (
|
| 672 |
+
float(mobile["demoDropdownOption"]["fontSize"].removesuffix("px"))
|
| 673 |
+
>= 20
|
| 674 |
+
)
|
| 675 |
+
assert mobile["demoDropdownOption"]["height"] >= 44
|
| 676 |
+
assert (
|
| 677 |
+
mobile["demoDropdownListbox"]["y"]
|
| 678 |
+
>= mobile["demoDropdownControl"]["y"]
|
| 679 |
+
+ mobile["demoDropdownControl"]["height"]
|
| 680 |
+
)
|
| 681 |
+
assert mobile["demoDropdownFrontmostRole"] == "option"
|
| 682 |
+
assert len(mobile["stepMarkers"]) == 4
|
| 683 |
+
assert all(marker["width"] >= 46 for marker in mobile["stepMarkers"])
|
| 684 |
+
assert all(marker["height"] >= 46 for marker in mobile["stepMarkers"])
|
| 685 |
+
assert max(mobile_step_x) - min(mobile_step_x) <= 1
|
| 686 |
assert float(mobile["fieldLabel"]["fontSize"].removesuffix("px")) >= 18
|
| 687 |
assert desktop["masthead"]["height"] <= 150
|
| 688 |
assert desktop["resultLegend"]["height"] <= 140
|
|
|
|
| 693 |
assert desktop["uploadPromptInsetTop"] >= 6
|
| 694 |
assert desktop["uploadPromptInsetBottom"] >= 6
|
| 695 |
assert desktop["documentHeight"] <= 1900
|
| 696 |
+
assert (
|
| 697 |
+
float(
|
| 698 |
+
desktop["settingsPanel"]["borderTopWidth"].removesuffix("px")
|
| 699 |
+
)
|
| 700 |
+
>= 8
|
| 701 |
+
)
|
| 702 |
+
assert (
|
| 703 |
+
float(desktop["actionPanel"]["borderTopWidth"].removesuffix("px"))
|
| 704 |
+
>= 8
|
| 705 |
+
)
|
| 706 |
assert (
|
| 707 |
abs(
|
| 708 |
desktop["settingsPanel"]["y"]
|
src/doc_inspector/ui.py
CHANGED
|
@@ -290,6 +290,8 @@ gradio-app {
|
|
| 290 |
.legend-items {
|
| 291 |
display: grid;
|
| 292 |
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
|
|
|
|
|
| 293 |
gap: 10px;
|
| 294 |
margin: 0;
|
| 295 |
padding: 0;
|
|
@@ -298,10 +300,13 @@ gradio-app {
|
|
| 298 |
|
| 299 |
.legend-items li {
|
| 300 |
display: flex;
|
|
|
|
| 301 |
min-width: 0;
|
| 302 |
-
min-height:
|
| 303 |
-
gap:
|
|
|
|
| 304 |
align-items: center;
|
|
|
|
| 305 |
padding: 9px 11px;
|
| 306 |
border: 1px solid;
|
| 307 |
border-radius: var(--radius-sm);
|
|
@@ -333,7 +338,7 @@ gradio-app {
|
|
| 333 |
|
| 334 |
.legend-items strong {
|
| 335 |
color: var(--ui-ink);
|
| 336 |
-
font-size:
|
| 337 |
font-weight: 800;
|
| 338 |
line-height: 1.2;
|
| 339 |
}
|
|
@@ -341,16 +346,22 @@ gradio-app {
|
|
| 341 |
.legend-action {
|
| 342 |
margin-top: 2px;
|
| 343 |
color: var(--ui-muted);
|
| 344 |
-
font-size:
|
| 345 |
line-height: 1.3;
|
| 346 |
}
|
| 347 |
|
| 348 |
.legend-dot {
|
| 349 |
-
width:
|
| 350 |
-
|
| 351 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 352 |
border-radius: 50%;
|
| 353 |
-
box-shadow:
|
| 354 |
}
|
| 355 |
|
| 356 |
.legend-dot-red {
|
|
@@ -376,26 +387,40 @@ gradio-app {
|
|
| 376 |
}
|
| 377 |
|
| 378 |
.workflow-section {
|
|
|
|
| 379 |
gap: 8px !important;
|
| 380 |
min-width: 0 !important;
|
| 381 |
padding: 16px 28px !important;
|
| 382 |
border: 0 !important;
|
| 383 |
border-radius: 0 !important;
|
| 384 |
-
background:
|
| 385 |
box-shadow: none !important;
|
| 386 |
}
|
| 387 |
|
| 388 |
.workflow-section + .workflow-section {
|
| 389 |
-
border-top:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 390 |
}
|
| 391 |
|
| 392 |
.action-section {
|
| 393 |
background: var(--ui-surface-soft) !important;
|
| 394 |
}
|
| 395 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 396 |
.task-heading {
|
| 397 |
display: flex;
|
| 398 |
-
gap:
|
| 399 |
align-items: flex-start;
|
| 400 |
margin: 0;
|
| 401 |
}
|
|
@@ -433,17 +458,26 @@ gradio-app {
|
|
| 433 |
|
| 434 |
.task-step {
|
| 435 |
display: inline-flex;
|
| 436 |
-
width:
|
| 437 |
-
height:
|
| 438 |
-
flex: 0 0
|
| 439 |
align-items: center;
|
| 440 |
justify-content: center;
|
| 441 |
border-radius: 50%;
|
| 442 |
background: var(--ui-primary);
|
| 443 |
color: #ffffff;
|
| 444 |
-
font-size:
|
| 445 |
font-weight: 800;
|
| 446 |
line-height: 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 447 |
}
|
| 448 |
|
| 449 |
.upload-control {
|
|
@@ -512,7 +546,8 @@ gradio-app {
|
|
| 512 |
.field-control [role="combobox"],
|
| 513 |
.field-control button {
|
| 514 |
min-height: 56px !important;
|
| 515 |
-
font-size:
|
|
|
|
| 516 |
}
|
| 517 |
|
| 518 |
.field-control,
|
|
@@ -651,13 +686,52 @@ gradio-app {
|
|
| 651 |
#demo-selector [role="combobox"],
|
| 652 |
#demo-selector button {
|
| 653 |
min-height: 56px !important;
|
| 654 |
-
font-size:
|
|
|
|
| 655 |
}
|
| 656 |
|
| 657 |
#demo-selector {
|
| 658 |
min-height: 56px !important;
|
| 659 |
}
|
| 660 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 661 |
.demo-btn {
|
| 662 |
min-height: 56px !important;
|
| 663 |
border: 1px solid var(--ui-primary) !important;
|
|
@@ -938,9 +1012,9 @@ gradio-app {
|
|
| 938 |
}
|
| 939 |
|
| 940 |
.results-heading {
|
| 941 |
-
margin-top:
|
| 942 |
margin-bottom: 10px;
|
| 943 |
-
padding:
|
| 944 |
}
|
| 945 |
|
| 946 |
.action-plan {
|
|
@@ -1278,7 +1352,7 @@ footer {
|
|
| 1278 |
|
| 1279 |
.result-legend {
|
| 1280 |
display: block;
|
| 1281 |
-
padding: 12px;
|
| 1282 |
}
|
| 1283 |
|
| 1284 |
.result-legend h2 {
|
|
@@ -1313,7 +1387,7 @@ footer {
|
|
| 1313 |
}
|
| 1314 |
|
| 1315 |
.legend-items strong {
|
| 1316 |
-
font-size:
|
| 1317 |
}
|
| 1318 |
|
| 1319 |
.legend-copy {
|
|
@@ -1324,7 +1398,7 @@ footer {
|
|
| 1324 |
|
| 1325 |
.legend-action {
|
| 1326 |
margin-top: 0;
|
| 1327 |
-
font-size:
|
| 1328 |
line-height: 1.25;
|
| 1329 |
}
|
| 1330 |
|
|
@@ -1358,6 +1432,10 @@ footer {
|
|
| 1358 |
padding: 14px 16px !important;
|
| 1359 |
}
|
| 1360 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1361 |
.task-heading {
|
| 1362 |
gap: 10px;
|
| 1363 |
}
|
|
@@ -1417,7 +1495,8 @@ footer {
|
|
| 1417 |
}
|
| 1418 |
|
| 1419 |
.results-heading {
|
| 1420 |
-
margin-top:
|
|
|
|
| 1421 |
}
|
| 1422 |
|
| 1423 |
.action-plan-header {
|
|
|
|
| 290 |
.legend-items {
|
| 291 |
display: grid;
|
| 292 |
grid-template-columns: repeat(3, minmax(0, 1fr));
|
| 293 |
+
grid-auto-rows: 1fr;
|
| 294 |
+
align-items: stretch;
|
| 295 |
gap: 10px;
|
| 296 |
margin: 0;
|
| 297 |
padding: 0;
|
|
|
|
| 300 |
|
| 301 |
.legend-items li {
|
| 302 |
display: flex;
|
| 303 |
+
height: 100%;
|
| 304 |
min-width: 0;
|
| 305 |
+
min-height: 68px;
|
| 306 |
+
gap: 10px;
|
| 307 |
+
align-self: stretch;
|
| 308 |
align-items: center;
|
| 309 |
+
box-sizing: border-box;
|
| 310 |
padding: 9px 11px;
|
| 311 |
border: 1px solid;
|
| 312 |
border-radius: var(--radius-sm);
|
|
|
|
| 338 |
|
| 339 |
.legend-items strong {
|
| 340 |
color: var(--ui-ink);
|
| 341 |
+
font-size: 22px;
|
| 342 |
font-weight: 800;
|
| 343 |
line-height: 1.2;
|
| 344 |
}
|
|
|
|
| 346 |
.legend-action {
|
| 347 |
margin-top: 2px;
|
| 348 |
color: var(--ui-muted);
|
| 349 |
+
font-size: 20px;
|
| 350 |
line-height: 1.3;
|
| 351 |
}
|
| 352 |
|
| 353 |
.legend-dot {
|
| 354 |
+
width: 18px;
|
| 355 |
+
min-width: 18px;
|
| 356 |
+
max-width: 18px;
|
| 357 |
+
height: 18px;
|
| 358 |
+
min-height: 18px;
|
| 359 |
+
max-height: 18px;
|
| 360 |
+
flex: 0 0 18px;
|
| 361 |
+
box-sizing: border-box;
|
| 362 |
+
border: 2px solid oklch(99% 0 0 / 0.84);
|
| 363 |
border-radius: 50%;
|
| 364 |
+
box-shadow: none;
|
| 365 |
}
|
| 366 |
|
| 367 |
.legend-dot-red {
|
|
|
|
| 387 |
}
|
| 388 |
|
| 389 |
.workflow-section {
|
| 390 |
+
position: relative;
|
| 391 |
gap: 8px !important;
|
| 392 |
min-width: 0 !important;
|
| 393 |
padding: 16px 28px !important;
|
| 394 |
border: 0 !important;
|
| 395 |
border-radius: 0 !important;
|
| 396 |
+
background: var(--ui-surface) !important;
|
| 397 |
box-shadow: none !important;
|
| 398 |
}
|
| 399 |
|
| 400 |
.workflow-section + .workflow-section {
|
| 401 |
+
border-top: 10px solid var(--ui-bg) !important;
|
| 402 |
+
box-shadow: inset 0 2px 0 var(--ui-border) !important;
|
| 403 |
+
}
|
| 404 |
+
|
| 405 |
+
.settings-section {
|
| 406 |
+
background: oklch(98.4% 0.007 205) !important;
|
| 407 |
}
|
| 408 |
|
| 409 |
.action-section {
|
| 410 |
background: var(--ui-surface-soft) !important;
|
| 411 |
}
|
| 412 |
|
| 413 |
+
.workbench .form {
|
| 414 |
+
overflow: visible !important;
|
| 415 |
+
}
|
| 416 |
+
|
| 417 |
+
.workflow-section:has([role="listbox"]) {
|
| 418 |
+
z-index: 20;
|
| 419 |
+
}
|
| 420 |
+
|
| 421 |
.task-heading {
|
| 422 |
display: flex;
|
| 423 |
+
gap: 15px;
|
| 424 |
align-items: flex-start;
|
| 425 |
margin: 0;
|
| 426 |
}
|
|
|
|
| 458 |
|
| 459 |
.task-step {
|
| 460 |
display: inline-flex;
|
| 461 |
+
width: 46px;
|
| 462 |
+
height: 46px;
|
| 463 |
+
flex: 0 0 46px;
|
| 464 |
align-items: center;
|
| 465 |
justify-content: center;
|
| 466 |
border-radius: 50%;
|
| 467 |
background: var(--ui-primary);
|
| 468 |
color: #ffffff;
|
| 469 |
+
font-size: 23px;
|
| 470 |
font-weight: 800;
|
| 471 |
line-height: 1;
|
| 472 |
+
box-shadow: 0 0 0 4px var(--ui-surface);
|
| 473 |
+
}
|
| 474 |
+
|
| 475 |
+
.settings-section .task-step {
|
| 476 |
+
box-shadow: 0 0 0 4px oklch(98.4% 0.007 205);
|
| 477 |
+
}
|
| 478 |
+
|
| 479 |
+
.action-section .task-step {
|
| 480 |
+
box-shadow: 0 0 0 4px var(--ui-surface-soft);
|
| 481 |
}
|
| 482 |
|
| 483 |
.upload-control {
|
|
|
|
| 546 |
.field-control [role="combobox"],
|
| 547 |
.field-control button {
|
| 548 |
min-height: 56px !important;
|
| 549 |
+
font-size: 21px !important;
|
| 550 |
+
font-weight: 620 !important;
|
| 551 |
}
|
| 552 |
|
| 553 |
.field-control,
|
|
|
|
| 686 |
#demo-selector [role="combobox"],
|
| 687 |
#demo-selector button {
|
| 688 |
min-height: 56px !important;
|
| 689 |
+
font-size: 21px !important;
|
| 690 |
+
font-weight: 620 !important;
|
| 691 |
}
|
| 692 |
|
| 693 |
#demo-selector {
|
| 694 |
min-height: 56px !important;
|
| 695 |
}
|
| 696 |
|
| 697 |
+
.gradio-container [role="listbox"] {
|
| 698 |
+
position: absolute !important;
|
| 699 |
+
top: calc(100% + 6px) !important;
|
| 700 |
+
right: 0 !important;
|
| 701 |
+
bottom: auto !important;
|
| 702 |
+
left: 0 !important;
|
| 703 |
+
width: 100% !important;
|
| 704 |
+
max-height: min(320px, 42vh) !important;
|
| 705 |
+
overflow-y: auto !important;
|
| 706 |
+
box-sizing: border-box !important;
|
| 707 |
+
padding: 6px !important;
|
| 708 |
+
border: 1px solid var(--ui-border-strong) !important;
|
| 709 |
+
border-radius: var(--radius-md) !important;
|
| 710 |
+
background: var(--ui-surface) !important;
|
| 711 |
+
box-shadow: 0 12px 28px oklch(24% 0.036 205 / 0.16) !important;
|
| 712 |
+
}
|
| 713 |
+
|
| 714 |
+
.gradio-container [role="listbox"] [role="option"] {
|
| 715 |
+
min-height: 46px !important;
|
| 716 |
+
padding: 9px 12px !important;
|
| 717 |
+
border-radius: var(--radius-sm) !important;
|
| 718 |
+
color: var(--ui-ink) !important;
|
| 719 |
+
font-size: 20px !important;
|
| 720 |
+
font-weight: 620 !important;
|
| 721 |
+
line-height: 1.4 !important;
|
| 722 |
+
}
|
| 723 |
+
|
| 724 |
+
.gradio-container [role="listbox"] [role="option"]:hover,
|
| 725 |
+
.gradio-container [role="listbox"] [role="option"].active {
|
| 726 |
+
background: var(--ui-surface-muted) !important;
|
| 727 |
+
}
|
| 728 |
+
|
| 729 |
+
.gradio-container [role="listbox"] [role="option"][aria-selected="true"] {
|
| 730 |
+
background: var(--ui-primary-soft) !important;
|
| 731 |
+
color: var(--ui-primary-hover) !important;
|
| 732 |
+
font-weight: 760 !important;
|
| 733 |
+
}
|
| 734 |
+
|
| 735 |
.demo-btn {
|
| 736 |
min-height: 56px !important;
|
| 737 |
border: 1px solid var(--ui-primary) !important;
|
|
|
|
| 1012 |
}
|
| 1013 |
|
| 1014 |
.results-heading {
|
| 1015 |
+
margin-top: 18px;
|
| 1016 |
margin-bottom: 10px;
|
| 1017 |
+
padding: 18px 28px 0;
|
| 1018 |
}
|
| 1019 |
|
| 1020 |
.action-plan {
|
|
|
|
| 1352 |
|
| 1353 |
.result-legend {
|
| 1354 |
display: block;
|
| 1355 |
+
padding: 10px 12px;
|
| 1356 |
}
|
| 1357 |
|
| 1358 |
.result-legend h2 {
|
|
|
|
| 1387 |
}
|
| 1388 |
|
| 1389 |
.legend-items strong {
|
| 1390 |
+
font-size: 21px;
|
| 1391 |
}
|
| 1392 |
|
| 1393 |
.legend-copy {
|
|
|
|
| 1398 |
|
| 1399 |
.legend-action {
|
| 1400 |
margin-top: 0;
|
| 1401 |
+
font-size: 19px;
|
| 1402 |
line-height: 1.25;
|
| 1403 |
}
|
| 1404 |
|
|
|
|
| 1432 |
padding: 14px 16px !important;
|
| 1433 |
}
|
| 1434 |
|
| 1435 |
+
.workflow-section + .workflow-section {
|
| 1436 |
+
border-top-width: 8px !important;
|
| 1437 |
+
}
|
| 1438 |
+
|
| 1439 |
.task-heading {
|
| 1440 |
gap: 10px;
|
| 1441 |
}
|
|
|
|
| 1495 |
}
|
| 1496 |
|
| 1497 |
.results-heading {
|
| 1498 |
+
margin-top: 16px;
|
| 1499 |
+
padding: 16px 16px 0;
|
| 1500 |
}
|
| 1501 |
|
| 1502 |
.action-plan-header {
|
tests/test_ui.py
CHANGED
|
@@ -328,6 +328,39 @@ def test_civic_theme_keeps_light_and_dark_tokens_consistent() -> None:
|
|
| 328 |
assert theme[token] == theme[f"{token}_dark"]
|
| 329 |
|
| 330 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 331 |
def test_load_demo_document_selects_file_and_matching_schema(tmp_path: Path) -> None:
|
| 332 |
file_path, schema, status = load_demo_document_callback(
|
| 333 |
"receipt_green",
|
|
|
|
| 328 |
assert theme[token] == theme[f"{token}_dark"]
|
| 329 |
|
| 330 |
|
| 331 |
+
def test_civic_css_keeps_steps_and_dropdowns_readable() -> None:
|
| 332 |
+
css = ui_module.CIVIC_CSS
|
| 333 |
+
|
| 334 |
+
assert ".workflow-section + .workflow-section" in css
|
| 335 |
+
assert "border-top: 10px solid var(--ui-bg)" in css
|
| 336 |
+
assert "box-shadow: inset 0 2px 0 var(--ui-border)" in css
|
| 337 |
+
assert "width: 18px" in css
|
| 338 |
+
assert "min-width: 18px" in css
|
| 339 |
+
assert "max-width: 18px" in css
|
| 340 |
+
assert "flex: 0 0 18px" in css
|
| 341 |
+
assert "font-size: 22px" in css
|
| 342 |
+
assert '.gradio-container [role="listbox"] [role="option"]' in css
|
| 343 |
+
assert "position: absolute !important" in css
|
| 344 |
+
assert "top: calc(100% + 6px) !important" in css
|
| 345 |
+
assert "bottom: auto !important" in css
|
| 346 |
+
assert '.workflow-section:has([role="listbox"])' in css
|
| 347 |
+
assert "min-height: 46px" in css
|
| 348 |
+
assert "font-size: 20px" in css
|
| 349 |
+
assert "width: 46px" in css
|
| 350 |
+
assert "flex: 0 0 46px" in css
|
| 351 |
+
assert "padding: 18px 28px 0" in css
|
| 352 |
+
assert ".results-heading::before" not in css
|
| 353 |
+
|
| 354 |
+
|
| 355 |
+
def test_civic_css_keeps_legend_cards_equal_sized() -> None:
|
| 356 |
+
css = ui_module.CIVIC_CSS
|
| 357 |
+
|
| 358 |
+
assert "grid-auto-rows: 1fr" in css
|
| 359 |
+
assert "align-items: stretch" in css
|
| 360 |
+
assert "height: 100%" in css
|
| 361 |
+
assert "align-self: stretch" in css
|
| 362 |
+
|
| 363 |
+
|
| 364 |
def test_load_demo_document_selects_file_and_matching_schema(tmp_path: Path) -> None:
|
| 365 |
file_path, schema, status = load_demo_document_callback(
|
| 366 |
"receipt_green",
|