diff --git a/codex-rs/tui/assets/inline_visualization/visualize.css b/codex-rs/tui/assets/inline_visualization/visualize.css
index 81626e4d7f..96a7488060 100644
--- a/codex-rs/tui/assets/inline_visualization/visualize.css
+++ b/codex-rs/tui/assets/inline_visualization/visualize.css
@@ -75,7 +75,7 @@
--color-ring-primary,
light-dark(rgb(51 156 255), rgb(131 195 255 / 76%))
);
- --font-size-base: var(--font-text-md-size, 14px);
+ --font-size-base: var(--font-text-md-size, 16px);
--blue: light-dark(rgb(51 156 255), rgb(51 156 255));
--orange: light-dark(rgb(226 85 7), rgb(251 106 34));
--green: light-dark(rgb(0 162 64), rgb(64 201 119));
@@ -90,7 +90,10 @@
--viz-series-6: light-dark(rgb(58 185 177), rgb(90 203 194));
/* Internal implementation variables; not part of the agent contract. */
- --font-sans: -apple-system, system-ui, "Segoe UI", sans-serif;
+ --font-sans:
+ -apple-system-body, ui-sans-serif, -apple-system, system-ui, "Segoe UI",
+ Helvetica, "Apple Color Emoji", Arial, sans-serif, "Segoe UI Emoji",
+ "Segoe UI Symbol";
--font-mono:
ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono",
monospace;
@@ -100,16 +103,18 @@
--font-size-h1: calc(var(--font-size-normal) * 1.7142857143);
--font-size-h2: calc(var(--font-size-normal) * 1.4285714286);
--font-size-h3: calc(var(--font-size-normal) * 1.2857142857);
- --font-weight-normal: 430;
+ --font-weight-normal: 400;
--font-weight-medium: 500;
+ --icon-size: 16px;
--line-height-normal: calc(var(--font-size-normal) * 1.5);
--line-height-tooltip: calc(var(--font-size-tooltip) * 1.4285714286);
--line-height-small: calc(var(--font-size-small) + 4px);
- --radius: var(--border-radius-lg, 12.5px);
- --radius-sm: calc(var(--radius) * 0.6);
- --radius-md: calc(var(--radius) * 0.8);
+ --corner-shape: round;
+ --radius: var(--border-radius-lg, 8px);
+ --radius-sm: calc(var(--radius) * 0.5);
+ --radius-md: calc(var(--radius) * 0.75);
--radius-lg: var(--radius);
- --radius-2xl: calc(var(--radius) * 1.6);
+ --radius-2xl: calc(var(--radius) * 2);
--radius-full: 9999px;
--shadow-sm: 0 1px 2px -1px rgb(0 0 0 / 8%);
--checkmark-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 17 17'%3E%3Cpath d='M12.8961 3.64101C13.1297 3.41418 13.4984 3.37523 13.7779 3.56581C14.0571 3.75635 14.1554 4.11331 14.0299 4.41347L13.9615 4.53847L7.71151 13.7045C7.59411 13.8767 7.4063 13.9877 7.19881 14.0072C6.99136 14.0267 6.78564 13.9533 6.63826 13.806L2.88826 10.056L2.79842 9.9457C2.6192 9.67407 2.64927 9.30496 2.88826 9.06581C3.12738 8.82669 3.49647 8.79676 3.76815 8.97597L3.8785 9.06581L7.03084 12.2182L12.8053 3.74941L12.8961 3.64101Z'/%3E%3C/svg%3E");
@@ -155,21 +160,26 @@ html > body {
);
margin: 0;
- padding: 5px;
+ padding: var(--visualize-paint-gutter, 0px);
color: var(--foreground);
background: transparent !important;
- font-family: var(--font-sans);
- font-size: var(--font-size-normal);
+ font-family: var(--font-sans) !important;
+ -webkit-font-smoothing: antialiased;
+ font-size: var(--font-size-base);
+ line-height: calc(var(--font-size-base) * 1.625);
font-weight: var(--font-weight-normal);
- line-height: var(--line-height-normal);
+}
+
+.cursor-interaction {
+ cursor: var(--cursor-interaction, pointer);
}
a {
color: color-mix(in srgb, var(--viz-accent) 80%, var(--viz-text) 20%);
- cursor: pointer;
- font-weight: var(--font-weight-medium, 500);
+ cursor: var(--cursor-interaction, pointer);
text-decoration: none;
text-underline-offset: 2px;
+ font-weight: inherit;
}
a:is(:hover, :focus-visible) {
@@ -187,39 +197,58 @@ h6,
p {
margin-block: 0;
}
-
-h1 {
- font-size: var(--font-size-h1);
- font-weight: var(--font-weight-medium);
- line-height: 1.25;
-}
-
-h2 {
- font-size: var(--font-size-h2);
- font-weight: var(--font-weight-medium);
- line-height: 1.25;
-}
-
+p,
+h1,
+h2,
h3,
h4,
h5,
-h6 {
- font-size: var(--font-size-h3);
- font-weight: var(--font-weight-medium);
- line-height: 1.3;
+h6,
+li,
+.table {
+ font-family: var(--font-sans);
}
+h1 {
+ font-size: calc(var(--font-size-base) * 1.5);
+ line-height: 1.3333333333;
+}
+
+h2 {
+ font-size: calc(var(--font-size-base) * 1.25);
+ line-height: 1.4;
+}
+h3 {
+ font-size: calc(var(--font-size-base) * 1.125);
+ line-height: 1.5555555556;
+}
+h4 {
+ font-size: var(--font-size-base);
+ line-height: 1.5;
+}
+h5,
+h6 {
+ font-size: var(--font-size-base);
+ line-height: calc(var(--font-size-base) * 1.625);
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
b,
strong,
th {
- font-weight: var(--font-weight-medium);
+ font-weight: 600;
}
code:not(pre code) {
display: inline;
padding: 1px 6px;
border-radius: var(--radius-sm);
- corner-shape: superellipse(1.5);
+ corner-shape: var(--corner-shape);
background: var(--muted);
font-family: var(--font-mono);
font-size: 0.92em;
@@ -228,6 +257,12 @@ code:not(pre code) {
box-decoration-break: clone;
word-break: break-word;
}
+pre,
+pre code {
+ font-family: var(--font-mono);
+ font-size: 12px;
+ line-height: 20px;
+}
.table-responsive {
width: 100%;
@@ -237,10 +272,14 @@ code:not(pre code) {
.table {
width: 100%;
- border-collapse: collapse;
color: var(--foreground);
font: inherit;
text-align: start;
+ border-collapse: separate;
+ border-spacing: 0;
+ font-size: calc(var(--font-size-base) * 0.875);
+ line-height: calc(var(--font-size-base) * 1.625);
+ font-family: var(--font-sans);
}
.table :is(th, td) {
@@ -250,6 +289,10 @@ code:not(pre code) {
border-bottom: 1px solid var(--border);
text-align: start;
vertical-align: top;
+ border-bottom-color: color-mix(in srgb, var(--foreground) 5%, transparent);
+}
+.table :is(th, td):not(:last-child) {
+ padding-inline-end: calc(var(--font-size-base) * 1.5);
}
.table-responsive > .table :is(th, td) {
@@ -264,17 +307,21 @@ code:not(pre code) {
font-weight: 600;
}
-.table thead th {
- padding-block: 8px;
- border-bottom-color: color-mix(in srgb, var(--foreground) 16%, transparent);
+.table thead :is(th, td) {
+ border-bottom-color: var(--border-strong, var(--input));
+ padding-block: calc(var(--font-size-base) / 2);
+ line-height: var(--font-size-base);
}
.table tbody tr:last-child :is(th, td) {
border-bottom: 0;
}
+.table tbody :is(th, td) {
+ padding-block: calc(var(--font-size-base) * 0.625);
+}
.table.table-sm :is(th, td) {
- padding-block: 6px;
+ padding-block: 8px;
}
.table.table-sm :is(th, td):not(:last-child) {
@@ -309,12 +356,23 @@ code:not(pre code) {
overflow: hidden;
overflow-wrap: break-word;
border-radius: var(--radius-2xl);
- corner-shape: superellipse(1.5);
+ corner-shape: var(--corner-shape);
color: var(--card-foreground);
background: var(--card);
}
-#widget > :not(.card) {
+.widget {
+ min-width: 0;
+ overflow: hidden;
+ border: 0;
+ border-radius: var(--radius-lg);
+ corner-shape: var(--corner-shape);
+ box-shadow: 0 0 0 0.5px var(--border-strong, var(--border));
+ color: var(--foreground);
+ background: var(--background);
+}
+
+#widget > :not(.card, .widget, .progress) {
width: 100% !important;
max-width: none !important;
margin: 0 !important;
@@ -340,19 +398,29 @@ code:not(pre code) {
var(--tooltip-available-height, calc(100vh - 10px)),
calc(100vh - 10px)
);
- padding: 4px 8px;
border: 1px solid var(--border);
- border-radius: var(--radius-lg);
- corner-shape: superellipse(1.5);
- color: var(--popover-foreground);
- background: var(--popover);
- box-shadow: none;
- font-size: var(--font-size-tooltip);
- line-height: var(--line-height-tooltip);
+ corner-shape: var(--corner-shape);
overflow-wrap: break-word;
white-space: normal;
pointer-events: none;
user-select: none;
+ font-family: var(--font-sans);
+ padding: 5px 12px;
+ border-color: color-mix(
+ in oklab,
+ light-dark(var(--primary-foreground), var(--foreground)) 5%,
+ transparent
+ );
+ border-radius: var(--radius-2xl);
+ color: light-dark(var(--primary-foreground), var(--foreground));
+ background: light-dark(var(--foreground), var(--popover));
+ box-shadow: 0 8px 18px rgb(15 23 42 / 20%);
+ letter-spacing: -0.15px;
+ font-size: calc(var(--font-size-base) * 14 / 16);
+ font-size: round(nearest, calc(var(--font-size-base) * 14 / 16), 1px);
+ font-weight: 600;
+ line-height: 18px;
+ text-align: center;
}
.viz-grid {
@@ -380,17 +448,58 @@ code:not(pre code) {
gap: 10px;
}
-.viz-badge {
- padding: 3px 8px;
+hr {
+ width: 100%;
+ height: 1px;
+ margin-block: 6px;
+ border: 0;
+ background: var(--border);
+}
+
+.nav {
+ display: flex;
+ min-width: 0;
+ align-items: center;
+ gap: 4px;
+}
+
+.progress {
+ display: flex;
+ height: 8px;
+ margin-block: calc((var(--line-height-normal, 1.5em) - 8px) / 2);
+ overflow: hidden;
+ border: 0;
border-radius: var(--radius-full);
- color: var(--accent-foreground);
- background: var(--accent);
- font-weight: var(--font-weight-medium);
+ background: var(--muted);
+}
+
+.progress-bar:only-child {
+ border-radius: var(--radius-full);
+}
+
+.progress-bar {
+ height: 100%;
+ flex-shrink: 0;
+ border: 0;
+ background: var(--viz-series-1);
+}
+
+.viz-badge {
+ display: inline-flex;
+ align-items: center;
+ padding: 4px 8px;
+ border-radius: var(--radius-sm);
+ color: color-mix(in srgb, var(--foreground) 40%, var(--muted-foreground));
+ background: color-mix(in srgb, var(--foreground) 5%, transparent);
+ font-family: var(--font-sans);
+ font-size: calc(var(--font-size-base) * 14 / 16);
+ font-size: round(nearest, calc(var(--font-size-base) * 14 / 16), 1px);
+ font-weight: var(--font-weight-normal);
+ line-height: 1;
}
small,
-.text-small,
-.viz-badge {
+.text-small {
font-size: var(--font-size-small);
line-height: var(--line-height-small);
}
@@ -399,6 +508,10 @@ small,
color: var(--muted-foreground);
}
+.tabular-nums {
+ font-variant-numeric: tabular-nums;
+}
+
.sr-only {
position: absolute;
width: 1px;
@@ -432,6 +545,7 @@ small,
}
.btn,
+.nav-link,
.form-check-input,
.form-control,
.form-range,
@@ -439,12 +553,23 @@ small,
font: inherit;
}
-.btn {
+/* Match inline alignment without overriding a containing flex or grid layout. */
+.btn,
+.nav-link,
+.form-control,
+.form-select,
+.form-range,
+.form-check {
+ vertical-align: middle;
+}
+
+.btn,
+.nav-link {
+ --btn-size: calc(12px + 2 * var(--control-spacing, 12px));
appearance: button;
display: inline-flex;
inline-size: fit-content;
max-inline-size: 100%;
- min-height: 28px;
align-items: center;
justify-content: center;
gap: 4px;
@@ -452,16 +577,40 @@ small,
padding: 0 8px;
-webkit-app-region: no-drag;
border: 1px solid var(--input);
- border-radius: var(--radius-lg);
- corner-shape: superellipse(1.5);
- color: var(--secondary-foreground);
- background: var(--secondary);
+ corner-shape: var(--corner-shape);
cursor: var(--cursor-interaction, pointer);
text-align: center;
text-decoration: none;
white-space: nowrap;
user-select: none;
- -webkit-font-smoothing: antialiased;
+ -webkit-font-smoothing: inherit;
+ font-family: var(--font-sans);
+ height: fit-content;
+ min-height: var(--btn-size);
+ padding-inline: var(--control-spacing, 12px);
+ border-color: transparent;
+ border-radius: var(--radius-full);
+ color: var(--foreground);
+ background: color-mix(in srgb, var(--foreground) 5%, transparent);
+ font-size: calc(var(--font-size-base) * 14 / 16);
+ font-size: round(nearest, calc(var(--font-size-base) * 14 / 16), 1px);
+ font-weight: var(--font-weight-normal);
+ line-height: 18px;
+}
+
+.btn {
+ border-color: var(--border);
+ color: var(--secondary-foreground);
+ background: var(--secondary);
+}
+
+/* Codex Button size="compact"; appearance stays with the button variant. */
+.btn-sm {
+ --btn-size: 24px;
+ padding-inline: 8px;
+ font-size: calc(var(--font-size-base) * 12 / 16);
+ font-size: round(nearest, calc(var(--font-size-base) * 12 / 16), 1px);
+ line-height: 16px;
}
.btn:is(.btn-block, .viz-tile) {
@@ -475,19 +624,26 @@ small,
}
a.btn {
- cursor: pointer;
+ cursor: var(--cursor-interaction, pointer);
text-decoration: none;
}
.btn:not(:disabled):hover {
- background: color-mix(in srgb, var(--foreground) 6%, var(--secondary));
+ background: light-dark(
+ color-mix(in srgb, var(--foreground) 5.5%, transparent),
+ color-mix(in srgb, var(--foreground) 8%, transparent)
+ );
+}
+
+.nav-link:not(:disabled):not([aria-disabled="true"]):hover {
+ background: color-mix(in srgb, var(--foreground) 10%, transparent);
}
.btn-primary {
- border-color: transparent;
+ border-color: var(--border);
color: var(--primary-foreground);
background: var(--foreground);
- background-clip: padding-box;
+ background-clip: border-box;
}
.btn-primary .text-muted {
@@ -496,7 +652,7 @@ a.btn {
.btn-primary:not(:disabled):hover {
background: color-mix(in srgb, var(--foreground) 80%, transparent);
- background-clip: padding-box;
+ background-clip: border-box;
}
.btn-ghost {
@@ -506,11 +662,222 @@ a.btn {
}
.btn-ghost:not(:disabled):hover {
- color: var(--foreground);
- background: color-mix(in srgb, var(--foreground) 6%, var(--secondary));
+ color: var(--muted-foreground);
+ background: light-dark(
+ color-mix(in srgb, var(--foreground) 5.5%, transparent),
+ color-mix(in srgb, var(--foreground) 8%, transparent)
+ );
}
-.btn:disabled {
+.nav-pills .nav-link {
+ min-width: 0;
+ border-color: transparent;
+ color: var(--muted-foreground);
+ background: transparent;
+}
+
+.nav-justified .nav-link {
+ flex: 1 1 0;
+ overflow-wrap: anywhere;
+ white-space: normal;
+}
+
+.nav-pills .nav-link.active {
+ color: var(--foreground);
+ background: var(--muted);
+}
+
+/* Shared preview chrome; product styles stay inside each variant. */
+.viz-dotted-background {
+ background-image: radial-gradient(
+ circle,
+ color-mix(in srgb, var(--foreground) 14%, transparent) 0.8px,
+ transparent 0.8px
+ );
+ background-position: center;
+ background-size: 16px 16px;
+}
+
+.viz-carousel {
+ position: relative;
+ min-width: 0;
+}
+
+.viz-carousel:has(> .viz-carousel-controls) {
+ min-height: calc(60px + var(--viz-carousel-picker-height, 0px));
+ padding-bottom: 60px;
+}
+
+.viz-carousel > [data-variant][hidden] {
+ display: none !important;
+}
+
+.viz-carousel > [data-variant] {
+ isolation: isolate;
+}
+
+.viz-carousel-controls {
+ --viz-carousel-hover: light-dark(
+ color-mix(in srgb, var(--foreground) 5.4%, transparent),
+ color-mix(in srgb, var(--foreground) 7.8%, transparent)
+ );
+ position: absolute;
+ z-index: 2;
+ bottom: 8px;
+ left: 50%;
+ display: flex;
+ width: max-content;
+ /* Leave room for the host's design-controls launcher at the trailing edge. */
+ max-width: calc(100% - 96px);
+ align-items: center;
+ gap: 0;
+ padding: 0;
+ border: 1px solid var(--border);
+ border-radius: 16px;
+ color: var(--foreground);
+ background: var(--background);
+ font-family: var(--font-sans);
+ /* Match the settings dropdown chrome independently of the content font size. */
+ font-size: 13px;
+ font-weight: var(--font-weight-normal);
+ line-height: 18px;
+ transform: translateX(-50%);
+ user-select: none;
+}
+
+/* Keep authored mockup button styles out of the shared carousel chrome. */
+.viz-carousel-controls button,
+.viz-carousel-picker > summary {
+ display: flex !important;
+ height: auto !important;
+ min-height: 30px;
+ align-items: center !important;
+ justify-content: center !important;
+ gap: 4px;
+ padding: 0 6px !important;
+ -webkit-app-region: no-drag;
+ border: 0 !important;
+ border-radius: 14px !important;
+ color: inherit !important;
+ background: transparent !important;
+ font: inherit !important;
+ -webkit-tap-highlight-color: transparent;
+}
+
+.viz-carousel-controls > button,
+.viz-carousel-picker > summary {
+ position: relative;
+ isolation: isolate;
+}
+
+.viz-carousel-controls > button {
+ --icon-size: 16px;
+ width: 30px !important;
+ padding: 0 !important;
+}
+
+.viz-carousel-picker > summary {
+ padding-inline: 12px !important;
+}
+
+.viz-carousel-controls .viz-carousel-caret {
+ width: 14px;
+ height: 14px;
+ flex: none;
+ margin: 0;
+ color: inherit;
+ transform: rotate(180deg);
+}
+
+.viz-carousel-controls > button > svg,
+.viz-carousel-controls .viz-carousel-caret,
+.viz-carousel-count {
+ opacity: 0.5;
+}
+
+.viz-carousel-controls > button::before,
+.viz-carousel-picker > summary::before {
+ position: absolute;
+ z-index: -1;
+ inset: 3px;
+ border-radius: 12px;
+ content: "";
+ pointer-events: none;
+}
+
+.viz-carousel-controls > button:hover::before,
+.viz-carousel-picker > summary:hover::before {
+ background: var(--viz-carousel-hover);
+}
+
+.viz-carousel-controls > button,
+.viz-carousel-controls [data-lucide],
+.viz-carousel-controls .lucide {
+ flex-shrink: 0;
+}
+
+.viz-carousel-picker {
+ min-width: 0;
+}
+
+.viz-carousel-picker > summary {
+ min-width: 0;
+ list-style: none;
+}
+
+.viz-carousel-picker > summary::-webkit-details-marker {
+ display: none;
+}
+
+.viz-carousel-count {
+ flex-shrink: 0;
+ white-space: nowrap;
+}
+
+.viz-carousel-name {
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.viz-carousel-options {
+ position: absolute;
+ right: 0;
+ bottom: calc(100% + 4px);
+ left: 0;
+ display: flex;
+ max-height: 240px;
+ flex-direction: column;
+ gap: 2px;
+ overflow: auto;
+ padding: 4px;
+ border: 1px solid var(--border);
+ border-radius: var(--radius-lg);
+ background: var(--background);
+ box-shadow: 0 4px 12px light-dark(rgb(0 0 0 / 6%), rgb(0 0 0 / 25%));
+}
+
+.viz-carousel-options > button {
+ border-radius: max(0px, calc(var(--radius-lg) - 5px)) !important;
+ width: 100% !important;
+ min-height: 28px;
+ justify-content: flex-start !important;
+ padding: 4px 8px !important;
+ font-size: var(--font-size-small) !important;
+ text-align: start;
+ white-space: normal;
+ overflow-wrap: anywhere;
+}
+
+.viz-carousel-options > [aria-current="true"],
+.viz-carousel-options > button:hover {
+ background: var(--viz-carousel-hover) !important;
+}
+
+.btn:disabled,
+.nav-link:disabled,
+.nav-link[aria-disabled="true"] {
cursor: not-allowed;
opacity: 0.4;
}
@@ -528,14 +895,42 @@ a.btn {
.form-control {
display: block;
width: 100%;
- min-height: 28px;
padding: 0 8px;
outline: none;
border: 1px solid var(--input);
- border-radius: var(--radius-lg);
- corner-shape: superellipse(1.5);
+ corner-shape: var(--corner-shape);
color: var(--foreground);
- background: var(--secondary);
+ font-family: var(--font-sans);
+ height: calc(12px + 2 * var(--control-spacing, 12px));
+ min-height: calc(12px + 2 * var(--control-spacing, 12px));
+ padding-inline: calc(4px + var(--control-spacing, 12px) / 2);
+ border-color: var(--input);
+ border-radius: var(--radius-md);
+ background: color-mix(in srgb, var(--popover) 96%, transparent);
+ font-size: calc(var(--font-size-base) * 14 / 16);
+ font-size: round(nearest, calc(var(--font-size-base) * 14 / 16), 1px);
+ font-weight: var(--font-weight-normal);
+ line-height: 1.4285714286;
+}
+.form-control:is(
+ [type="file"],
+ [type="date"],
+ [type="datetime-local"],
+ [type="month"],
+ [type="week"],
+ [type="color"]
+ ) {
+ height: calc(12px + 2 * var(--control-spacing, 12px));
+ min-height: calc(12px + 2 * var(--control-spacing, 12px));
+ line-height: 18px;
+}
+.form-control:is(
+ [type="date"],
+ [type="datetime-local"],
+ [type="month"],
+ [type="week"]
+ ) {
+ padding-inline: var(--control-spacing, 12px);
}
.form-control::placeholder {
@@ -543,14 +938,12 @@ a.btn {
}
.form-control[type="file"] {
- padding: 0;
overflow: hidden;
cursor: var(--cursor-interaction, pointer);
+ padding: 0;
}
.form-control[type="file"]::file-selector-button {
- min-height: 26px;
- margin-right: 8px;
padding: 0 8px;
border: 0;
border-right: 1px solid var(--input);
@@ -558,6 +951,9 @@ a.btn {
background: var(--secondary);
cursor: inherit;
font: inherit;
+ min-height: calc(10px + 2 * var(--control-spacing, 12px));
+ padding-inline: var(--control-spacing, 12px);
+ margin-inline-end: var(--control-spacing, 12px);
}
.form-control[type="file"]:not(:disabled):hover::file-selector-button {
@@ -566,7 +962,7 @@ a.btn {
.form-control-color[type="color"] {
width: 40px;
- height: 28px;
+ height: calc(12px + 2 * var(--control-spacing, 12px));
padding: 3px;
cursor: var(--cursor-interaction, pointer);
}
@@ -578,63 +974,103 @@ a.btn {
.form-control-color[type="color"]::-webkit-color-swatch {
border: 0;
border-radius: calc(var(--radius-lg) - 4px);
- corner-shape: superellipse(1.5);
+ corner-shape: var(--corner-shape);
}
textarea.form-control {
height: auto;
- min-height: 72px;
- padding: 8px 10px;
resize: vertical;
+ min-height: 96px;
+ padding: 8px 10px;
}
.form-control:focus-visible {
border-color: var(--ring);
- box-shadow: inset 0 0 0 1px var(--ring);
+ box-shadow: none;
+}
+.form-control:disabled {
+ color: var(--muted-foreground);
+ background: color-mix(in srgb, var(--foreground) 5%, transparent);
+ opacity: 1;
+}
+.form-control[aria-invalid="true"] {
+ border-color: var(--red);
+ box-shadow: 0 0 0 2px color-mix(in srgb, var(--red) 20%, transparent);
}
.form-control:disabled,
.form-select:disabled {
cursor: not-allowed;
- opacity: 0.4;
}
.form-select {
appearance: none;
display: block;
width: 100%;
- min-height: 28px;
+ height: fit-content;
+ min-height: calc(12px + 2 * var(--control-spacing, 12px));
margin: 0;
- padding: 0 32px 0 8px;
+ /* The secondary icon role is 14px at compact density and 16px at web density. */
+ --icon-size: calc(10px + var(--control-spacing, 12px) / 2);
+ /* Settings dropdowns retain a 12px inset and a 4px text-to-chevron gap. */
+ padding: 0 calc(16px + var(--icon-size)) 0 12px;
outline: none;
- border: 1px solid var(--input);
- border-radius: var(--radius-lg);
- corner-shape: superellipse(1.5);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-full);
+ corner-shape: var(--corner-shape);
color: var(--foreground);
background-color: var(--secondary);
background-image:
- linear-gradient(45deg, transparent 50%, var(--muted-foreground) 50%),
- linear-gradient(135deg, var(--muted-foreground) 50%, transparent 50%);
+ linear-gradient(
+ 45deg,
+ transparent calc(50% - 0.6px),
+ var(--muted-foreground) calc(50% - 0.6px) calc(50% + 0.6px),
+ transparent calc(50% + 0.6px)
+ ),
+ linear-gradient(
+ 135deg,
+ transparent calc(50% - 0.6px),
+ var(--muted-foreground) calc(50% - 0.6px) calc(50% + 0.6px),
+ transparent calc(50% + 0.6px)
+ );
background-position:
- calc(100% - 14px) 50%,
- calc(100% - 10px) 50%;
+ calc(100% - 12px - var(--icon-size) / 2) 50%,
+ calc(100% - 12px - var(--icon-size) * 0.18) 50%;
background-repeat: no-repeat;
- background-size: 4px 4px;
+ background-size: calc(var(--icon-size) * 0.32) calc(var(--icon-size) * 0.32);
cursor: var(--cursor-interaction, default);
+ font-size: calc(var(--font-size-base) * 14 / 16);
+ font-size: round(nearest, calc(var(--font-size-base) * 14 / 16), 1px);
+ font-weight: var(--font-weight-normal);
+ line-height: 18px;
+}
+
+.form-select :is(option, optgroup) {
+ color: var(--popover-foreground);
+ background-color: var(--popover);
+}
+
+.form-select :is(option, optgroup):disabled {
+ color: var(--muted-foreground);
}
.form-select:not(:disabled):hover {
- background-color: color-mix(in srgb, var(--foreground) 6%, var(--secondary));
+ background-color: light-dark(
+ color-mix(in srgb, var(--foreground) 5.5%, transparent),
+ color-mix(in srgb, var(--foreground) 8%, transparent)
+ );
}
.form-select:focus-visible {
- border-color: var(--ring);
- box-shadow: inset 0 0 0 1px var(--ring);
+ box-shadow: 0 0 0 2px var(--ring);
+}
+.form-select:disabled {
+ opacity: 0.4;
}
.form-check {
display: flex;
- min-height: 20px;
+ min-height: calc(12px + 2 * var(--control-spacing, 12px));
align-items: center;
gap: 6px;
}
@@ -656,11 +1092,15 @@ textarea.form-control {
}
.form-check:not(.form-switch) .form-check-input[type="checkbox"] {
- border-color: var(--input);
- border-radius: var(--radius-sm);
- corner-shape: superellipse(1.5);
- background-color: var(--secondary);
+ corner-shape: round;
box-shadow: var(--shadow-sm);
+ position: relative;
+ width: calc(10px + var(--control-spacing, 12px) / 2);
+ height: calc(10px + var(--control-spacing, 12px) / 2);
+ border-color: var(--border-strong, var(--input));
+ border-radius: calc(var(--radius-sm) / 2);
+ background-color: transparent;
+ color: var(--foreground);
}
.form-check:not(.form-switch)
@@ -668,9 +1108,10 @@ textarea.form-control {
background-color: var(--card);
}
-.form-check:not(.form-switch) .form-check-input[type="checkbox"]:checked {
- border-color: var(--primary);
- background-color: var(--primary);
+.form-check:not(.form-switch)
+ .form-check-input[type="checkbox"]:is(:checked, :indeterminate) {
+ border-color: var(--border-strong, var(--input));
+ background-color: color-mix(in srgb, var(--popover) 96%, transparent);
}
.form-check:not(.form-switch)
@@ -681,25 +1122,49 @@ textarea.form-control {
background: var(--primary-foreground);
content: "";
mask: var(--checkmark-image) center / 12px 12px no-repeat;
+ background-color: var(--foreground);
+}
+.form-check:not(.form-switch)
+ .form-check-input[type="checkbox"]:indeterminate::before {
+ position: absolute;
+ inset: 0;
+ display: block;
+ width: 65%;
+ height: 6.65%;
+ margin: auto;
+ border-radius: var(--radius-full);
+ background: var(--foreground);
+ mask: none;
+ content: "";
}
.form-check-input[type="radio"] {
- width: 14px;
- height: 14px;
border-radius: var(--radius-full);
+ width: 16px;
+ height: 16px;
+ border: 0;
+ box-shadow: inset 0 0 0 1px var(--input);
}
.form-check-input[type="radio"]:checked {
- border: 2px solid var(--primary);
- background:
- radial-gradient(circle, var(--primary-foreground) 0 2.5px, transparent 3px),
- var(--primary);
+ border: 0;
+ background: radial-gradient(
+ circle,
+ var(--primary-foreground) 0 3px,
+ var(--foreground) 3px
+ );
+ box-shadow: none;
}
.form-check:not(.form-switch) .form-check-input:focus-visible {
outline: 2px solid var(--ring);
outline-offset: 2px;
}
+.form-check:not(.form-switch) .form-check-input[type="checkbox"]:focus-visible {
+ border-color: var(--ring);
+ outline: none;
+ box-shadow: 0 0 0 2px var(--ring);
+}
.form-check:not(.form-switch) .form-check-input:disabled {
cursor: not-allowed;
@@ -709,6 +1174,12 @@ textarea.form-control {
.form-check:not(.form-switch) .form-check-input:disabled + .form-check-label {
cursor: not-allowed;
}
+.form-check:not(.form-switch) .form-check-input[type="radio"]:disabled,
+.form-check:not(.form-switch)
+ .form-check-input[type="radio"]:disabled
+ + .form-check-label {
+ opacity: 0.5;
+}
.form-switch .form-check-input:disabled,
.form-switch .form-check-input:disabled + .form-check-label {
@@ -741,15 +1212,16 @@ textarea.form-control {
box-sizing: border-box;
border: 1px solid light-dark(var(--primary-foreground), var(--foreground));
border-radius: var(--radius-full);
- background: light-dark(var(--primary-foreground), var(--foreground));
box-shadow: var(--shadow-sm);
content: "";
transform: translate(2px, -50%);
transition: transform 200ms cubic-bezier(0, 0, 0.2, 1);
+ border-color: #fff;
+ background: #fff;
}
.form-switch .form-check-input:checked {
- background: var(--primary);
+ background: var(--blue);
}
.form-switch .form-check-input:checked::before {
@@ -758,49 +1230,54 @@ textarea.form-control {
.form-switch .form-check-input:focus-visible {
box-shadow: 0 0 0 2px var(--ring);
+ outline: none;
}
.form-range {
appearance: none;
display: block;
width: 100%;
- height: 28px;
flex: 1;
margin: 0;
padding: 0;
- outline: none;
border: 0;
accent-color: var(--primary);
- background: linear-gradient(
- color-mix(in srgb, var(--foreground) 7%, transparent),
- color-mix(in srgb, var(--foreground) 7%, transparent)
- )
- center / 100% 2px no-repeat;
+ height: calc(12px + 2 * var(--control-spacing, 12px));
+ min-height: calc(12px + 2 * var(--control-spacing, 12px));
+ outline: none;
+ border-radius: var(--radius-full);
+ color: var(--foreground);
+ background: transparent;
}
.form-range::-webkit-slider-runnable-track {
- height: 28px;
- background: transparent;
+ background: var(--form-range-track, var(--border));
+ height: 2px;
+ border-radius: var(--radius-full);
}
.form-range::-webkit-slider-thumb {
appearance: none;
width: 20px;
height: 20px;
- margin-top: 4px;
border: 1px solid var(--border);
border-radius: var(--radius-full);
- background: light-dark(var(--primary-foreground), var(--foreground));
+ margin-top: -9px;
+ border-color: transparent;
+ background: currentColor;
+ box-shadow: var(--shadow-sm);
}
-.form-range:focus-visible::-webkit-slider-thumb {
- border-color: var(--ring);
- box-shadow: inset 0 0 0 1px var(--ring);
+/* Outline the native track and thumb, preserving the larger input hit area. */
+.form-range:focus-visible::-webkit-slider-runnable-track {
+ outline: 2px auto Highlight;
+ outline-color: -webkit-focus-ring-color;
}
.form-range::-moz-range-track {
- height: 28px;
- background: transparent;
+ background: var(--form-range-track, var(--border));
+ height: 2px;
+ border-radius: var(--radius-full);
}
.form-range::-moz-range-thumb {
@@ -808,17 +1285,19 @@ textarea.form-control {
height: 20px;
border: 1px solid var(--border);
border-radius: var(--radius-full);
- background: light-dark(var(--primary-foreground), var(--foreground));
+ border-color: transparent;
+ background: currentColor;
+ box-shadow: var(--shadow-sm);
}
.form-range:focus-visible::-moz-range-thumb {
- border-color: var(--ring);
- box-shadow: inset 0 0 0 1px var(--ring);
+ outline: 2px solid Highlight;
+ outline-offset: 2px;
}
.form-range:disabled {
- cursor: not-allowed;
- opacity: 0.4;
+ opacity: 1;
+ cursor: default;
}
.btn:not(.btn-primary, .viz-tile):is(
@@ -826,9 +1305,9 @@ textarea.form-control {
[aria-selected="true"],
.is-selected
) {
- border-color: var(--primary);
+ border-color: transparent;
color: var(--primary-foreground);
- background: var(--primary);
+ background: var(--blue);
}
.btn.viz-tile:is([aria-pressed="true"], [aria-selected="true"], .is-selected) {
@@ -836,9 +1315,166 @@ textarea.form-control {
box-shadow: inset 0 0 0 1px var(--primary);
}
-.btn:focus-visible {
- outline: 2px solid var(--ring);
+.btn:focus-visible,
+.nav-link:focus-visible {
outline-offset: 2px;
+ outline: none;
+ box-shadow: 0 0 0 2px var(--ring);
+}
+
+.viz-carousel-controls :is(button, summary):focus-visible {
+ outline: 2px solid var(--ring) !important;
+ outline-offset: 2px !important;
+}
+
+/* Web recipes are the default, including on renderers without inline style queries. */
+@supports (border-radius: if(style(--corner-shape: round): 1px; else: 2px)) {
+ /* Match the host's proportional text-sm scale at every text preference. */
+ .tooltip,
+ .viz-badge,
+ .btn,
+ .nav-link,
+ .form-control,
+ .form-select {
+ font-size: if(
+ style(--corner-shape: round): round(
+ nearest,
+ calc(var(--font-size-base) * 14 / 16),
+ 1px
+ )
+ ; else: round(nearest, calc(var(--font-size-base) * 13 / 14), 1px)
+ );
+ }
+
+ .btn-sm {
+ font-size: if(
+ style(--corner-shape: round): round(
+ nearest,
+ calc(var(--font-size-base) * 12 / 16),
+ 1px
+ )
+ ; else: round(nearest, calc(var(--font-size-base) * 12 / 14), 1px)
+ );
+ }
+
+ .btn,
+ .nav-link,
+ .form-select {
+ border-radius: if(
+ style(--corner-shape: round): var(--radius-full) ; else: var(--radius-lg)
+ );
+ }
+
+ .form-check:not(.form-switch) .form-check-input[type="checkbox"] {
+ border-radius: if(
+ style(--corner-shape: round): calc(var(--radius-sm) / 2) ;
+ else: calc(var(--radius-lg) * 0.4)
+ );
+ }
+
+ .tooltip {
+ font-weight: if(
+ style(--corner-shape: round): 600; else: var(--font-weight-normal)
+ );
+ }
+}
+
+@media (pointer: coarse) {
+ .viz-carousel:has(> .viz-carousel-controls) {
+ min-height: calc(76px + var(--viz-carousel-picker-height, 0px));
+ padding-bottom: 76px;
+ }
+
+ .btn,
+ .nav-link,
+ .viz-carousel-controls button,
+ .viz-carousel-picker > summary,
+ .form-control,
+ .form-select {
+ --btn-size: 44px;
+ min-width: 44px;
+ min-height: 44px;
+ }
+
+ /* Keep small-screen WebKit from zooming when a text field receives focus. */
+ .form-control,
+ .form-select {
+ font-size: max(16px, calc(var(--font-size-base) * 14 / 16));
+ font-size: max(
+ 16px,
+ round(nearest, calc(var(--font-size-base) * 14 / 16), 1px)
+ );
+ }
+ .form-control:is(
+ [type="file"],
+ [type="date"],
+ [type="datetime-local"],
+ [type="month"],
+ [type="week"],
+ [type="time"],
+ [type="color"]
+ ) {
+ height: 44px;
+ min-height: 44px;
+ font-size: max(16px, calc(var(--font-size-base) * 14 / 16));
+ font-size: max(
+ 16px,
+ round(nearest, calc(var(--font-size-base) * 14 / 16), 1px)
+ );
+ }
+
+ .form-control[type="file"]::file-selector-button {
+ min-width: 44px;
+ min-height: 44px;
+ }
+
+ .form-control-color[type="color"] {
+ width: 44px;
+ height: 44px;
+ }
+
+ .form-control:is(
+ [type="date"],
+ [type="datetime-local"],
+ [type="month"],
+ [type="time"],
+ [type="week"]
+ ) {
+ min-width: 0;
+ max-width: 100%;
+ padding-block: 8px;
+ }
+
+ .form-check {
+ min-height: 44px;
+ padding-inline: 15px;
+ }
+
+ .form-check-input {
+ position: relative;
+ }
+
+ .form-check-input::after {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 44px;
+ height: 44px;
+ content: "";
+ transform: translate(-50%, -50%);
+ }
+
+ .form-check-label {
+ display: inline-flex;
+ min-width: 44px;
+ min-height: 44px;
+ align-items: center;
+ }
+
+ .form-range {
+ height: 44px;
+ touch-action: pan-y;
+ }
}
svg {
@@ -854,8 +1490,8 @@ svg {
/* Model-authored chart rules can otherwise stretch icons after these styles load. */
svg.lucide {
display: block;
- width: 16px !important;
- height: 16px !important;
+ width: var(--icon-size) !important;
+ height: var(--icon-size) !important;
flex: none;
margin: 0 !important;
stroke-width: 1.6;
diff --git a/codex-rs/tui/assets/inline_visualization/visualize.html b/codex-rs/tui/assets/inline_visualization/visualize.html
index a34c0d45df..50fa77473a 100644
--- a/codex-rs/tui/assets/inline_visualization/visualize.html
+++ b/codex-rs/tui/assets/inline_visualization/visualize.html
@@ -22,11 +22,18 @@
let autoUpdateCleanup = null;
let positionRequest = 0;
let skipDelayUntil = 0;
+ let lastPointerWasTouch = false;
+ let pinnedByTouch = false;
- const getTrigger = (target) =>
- target?.nodeType === Node.ELEMENT_NODE
- ? target.closest("[data-tooltip]")
- : null;
+ const getTrigger = (event) => {
+ for (const target of event.composedPath()) {
+ if (target?.nodeType === Node.ELEMENT_NODE) {
+ const trigger = target.closest("[data-tooltip]");
+ if (trigger) return trigger;
+ }
+ }
+ return null;
+ };
const containsTarget = (trigger, target) =>
target?.nodeType != null && trigger.contains(target);
const getContent = (trigger) =>
@@ -70,6 +77,7 @@
};
const closeTooltip = (activateSkipDelay = true) => {
clearOpenTimeout();
+ pinnedByTouch = false;
positionRequest += 1;
autoUpdateCleanup?.();
autoUpdateCleanup = null;
@@ -170,14 +178,47 @@
if (event.pointerType === "touch") {
return;
}
- const trigger = getTrigger(event.target);
+ const trigger = getTrigger(event);
if (trigger == null || containsTarget(trigger, event.relatedTarget)) {
return;
}
requestOpen(trigger);
};
+ const handlePointer = (event) => {
+ lastPointerWasTouch =
+ event.type === "pointerdown" && event.pointerType === "touch";
+ };
+ const handleTouchClick = (event) => {
+ const isTouch = event.pointerType === "touch" || lastPointerWasTouch;
+ lastPointerWasTouch = false;
+ if (!isTouch || event.detail === 0) {
+ return;
+ }
+ const trigger = getTrigger(event);
+ if (
+ trigger == null ||
+ !trigger.isConnected ||
+ getContent(trigger).length === 0 ||
+ (pinnedByTouch && activeTrigger === trigger)
+ ) {
+ if (pinnedByTouch) {
+ closeTooltip();
+ }
+ return;
+ }
+ openTooltip(trigger);
+ pinnedByTouch = activeTrigger === trigger;
+ window.setTimeout(() => {
+ if (activeTrigger === trigger && !trigger.isConnected) {
+ closeTooltip(false);
+ }
+ }, 0);
+ };
const handleLeave = (event) => {
- const trigger = getTrigger(event.target);
+ if (pinnedByTouch) {
+ return;
+ }
+ const trigger = getTrigger(event);
if (trigger == null || containsTarget(trigger, event.relatedTarget)) {
return;
}
@@ -186,7 +227,7 @@
}
};
const handleFocusIn = (event) => {
- const trigger = getTrigger(event.target);
+ const trigger = getTrigger(event);
if (trigger?.matches(":focus-visible")) {
requestOpen(trigger, true);
}
@@ -197,31 +238,344 @@
}
};
const handleContextMenu = (event) => {
- const trigger = getTrigger(event.target);
+ lastPointerWasTouch = false;
+ const trigger = getTrigger(event);
if (activeTrigger === trigger || pendingTrigger === trigger) {
closeTooltip();
}
};
+ const handlers = {
+ pointerover: handlePointerOpen,
+ pointerdown: handlePointer,
+ pointercancel: handlePointer,
+ click: handleTouchClick,
+ pointerout: handleLeave,
+ focusin: handleFocusIn,
+ focusout: handleLeave,
+ keydown: handleKeyDown,
+ contextmenu: handleContextMenu,
+ };
+ const roots = new Map();
+ const handledEvents = new WeakSet();
+ const handleEvent = (event) => {
+ if (handledEvents.has(event)) return;
+ handledEvents.add(event);
+ handlers[event.type](event);
+ };
+ const discoverRoots = (node) => {
+ if (node.shadowRoot) listen(node.shadowRoot);
+ for (const element of node.querySelectorAll("*")) {
+ if (element.shadowRoot) listen(element.shadowRoot);
+ }
+ };
+ const listen = (root) => {
+ if (roots.has(root)) return;
+ for (const type of Object.keys(handlers))
+ root.addEventListener(type, handleEvent);
+ const observer = new window.MutationObserver((records) => {
+ for (const { addedNodes } of records) {
+ for (const node of addedNodes) {
+ if (node.nodeType === Node.ELEMENT_NODE) discoverRoots(node);
+ }
+ }
+ for (const [root, cleanup] of roots) {
+ if (root.host && !root.host.isConnected) {
+ cleanup();
+ roots.delete(root);
+ }
+ }
+ if (activeTrigger && !activeTrigger.isConnected) closeTooltip(false);
+ });
+ observer.observe(root, { childList: true, subtree: true });
+ roots.set(root, () => {
+ observer.disconnect();
+ for (const type of Object.keys(handlers))
+ root.removeEventListener(type, handleEvent);
+ });
+ discoverRoots(root);
+ };
const destroy = () => {
closeTooltip(false);
- document.removeEventListener("pointerover", handlePointerOpen);
- document.removeEventListener("pointerout", handleLeave);
- document.removeEventListener("focusin", handleFocusIn);
- document.removeEventListener("focusout", handleLeave);
- document.removeEventListener("keydown", handleKeyDown);
- document.removeEventListener("contextmenu", handleContextMenu);
+ lastPointerWasTouch = false;
+ for (const cleanup of roots.values()) cleanup();
+ roots.clear();
tooltip = null;
};
- document.addEventListener("pointerover", handlePointerOpen);
- document.addEventListener("pointerout", handleLeave);
- document.addEventListener("focusin", handleFocusIn);
- document.addEventListener("focusout", handleLeave);
- document.addEventListener("keydown", handleKeyDown);
- document.addEventListener("contextmenu", handleContextMenu);
+ // Events between children of one shadow root never reach the document.
+ listen(document);
window.addEventListener("pagehide", destroy, { once: true });
})();
+
+