/* Copyright 2026 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ :root { color-scheme: light dark; /* Backgrounds */ --bg-color: light-dark(#fff, #1e1e1e); --surface-bg: light-dark(#f3f3f3, #252526); --input-bg: light-dark(#fff, #3c3c3c); --button-bg: light-dark(#f3f3f3, #3c3c3c); --button-hover-bg: light-dark(#e0e0e0, #4a4a4a); --clr-canvas-bg: var(--surface-bg); /* Text */ --text-color: light-dark(#1e1e1e, #d4d4d4); --muted-color: light-dark(#6e6e6e, #888); --accent-color: light-dark(#0070c1, #9cdcfe); /* Borders */ --border-color: light-dark(#e0e0e0, #3c3c3c); --border-subtle-color: light-dark(#d0d0d0, #444); --input-border-color: light-dark(#c8c8c8, #555); /* Interactive states */ --hover-bg: light-dark(rgb(0 0 0 / 0.05), rgb(255 255 255 / 0.05)); --hover-color: currentColor; --paused-bg: light-dark(rgb(255 165 0 / 0.15), rgb(255 165 0 / 0.2)); --paused-outline-color: rgb(255 140 0 / 0.6); --paused-color: currentColor; /* Semantic */ --ref-color: light-dark(#007b6e, #4ec9b0); --ref-hover-color: light-dark(#065, #89d9c8); --changed-bg: transparent; --changed-color: light-dark(#c00, #f66); --match-bg: light-dark(rgb(255 200 0 / 0.35), rgb(255 200 0 / 0.25)); --match-outline-color: light-dark(rgb(200 140 0 / 0.8), rgb(255 200 0 / 0.6)); /* Syntax highlighting */ --string-color: light-dark(#a31515, #ce9178); --number-color: light-dark(#098658, #b5cea8); --bool-color: light-dark(#00f, #569cd6); --null-color: light-dark(#767676, #808080); --name-color: light-dark(#795e26, #dcdcaa); --stream-color: light-dark(#af00db, #c586c0); } @media (forced-colors: active) { :root { /* Backgrounds */ --bg-color: Canvas; --surface-bg: Canvas; --input-bg: Field; --button-bg: ButtonFace; --button-hover-bg: Highlight; --clr-canvas-bg: var(--surface-bg); /* Text */ --text-color: CanvasText; --muted-color: GrayText; --accent-color: CanvasText; /* Borders */ --border-color: ButtonBorder; --border-subtle-color: ButtonBorder; --input-border-color: ButtonBorder; /* Interactive states */ --hover-bg: Highlight; --hover-color: HighlightText; --paused-bg: Mark; --paused-outline-color: ButtonBorder; --paused-color: MarkText; /* Semantic */ --ref-color: LinkText; --ref-hover-color: ActiveText; --changed-bg: Mark; --changed-color: MarkText; --match-bg: Mark; --match-outline-color: ButtonBorder; /* Syntax highlighting — replaced by plain text in HCM */ --string-color: CanvasText; --number-color: CanvasText; --bool-color: CanvasText; --null-color: GrayText; --name-color: CanvasText; --stream-color: CanvasText; } /* Resizer hover: accent color → CanvasText is wrong for a background. */ #render-resizer, #op-resizer, #op-gfx-state-resizer { &:hover, &.dragging { background: Highlight; } } /* Opacity trick for breakpoint glyph visibility → use Canvas color to hide. */ .bp-gutter::before { opacity: 1; color: Canvas; } .bp-gutter:hover::before { color: ButtonBorder; } .bp-gutter.active::before { color: ButtonText; } /* Opacity-only disabled style → explicit GrayText. */ button:disabled, input:disabled { color: GrayText; border-color: GrayText; opacity: 1; } /* Color swatch preserves the actual PDF color value. */ .color-swatch { forced-color-adjust: none; } } * { box-sizing: border-box; } .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; } body.loading { cursor: wait; } html { height: 100%; } body { font-family: "Courier New", Courier, monospace; margin: 0; padding: 16px; background: var(--bg-color); color: var(--text-color); font-size: 13px; line-height: 1.5; display: flex; flex-direction: column; } /* In debug mode the body must be viewport-height so #debug-view can fill it. In tree mode body is auto-height so the tree can grow and the page scrolls. */ body:has(#debug-view:not([hidden])) { height: 100%; overflow: hidden; } #header { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 12px; h1 { color: var(--accent-color); font-size: 1.2em; margin: 0; } #pdf-info { font-family: system-ui, sans-serif; font-size: 1.15em; font-weight: 500; color: var(--text-color); } } #password-dialog { background: var(--input-bg); color: var(--text-color); border: 1px solid var(--input-border-color); border-radius: 6px; padding: 20px; min-width: 320px; &::backdrop { background: rgb(0 0 0 / 0.4); } p { margin: 0 0 12px; } input { display: block; width: 100%; margin-top: 4px; background: var(--input-bg); color: var(--text-color); border: 1px solid var(--input-border-color); border-radius: 3px; padding: 4px 8px; font-family: inherit; font-size: inherit; } .password-dialog-buttons { display: flex; justify-content: flex-end; gap: 8px; margin-top: 16px; button { padding: 4px 14px; border-radius: 3px; border: 1px solid var(--input-border-color); background: var(--button-bg); color: inherit; cursor: pointer; font-family: inherit; font-size: inherit; &:hover { background: var(--button-hover-bg); } } } } #controls { position: sticky; top: 0; z-index: 1; display: flex; flex-direction: row; align-items: center; gap: 12px; margin-bottom: 16px; padding: 10px 14px; background: var(--surface-bg); border-radius: 4px; border: 1px solid var(--border-color); label { display: flex; align-items: center; gap: 4px; color: var(--muted-color); } #github-link { margin-inline-start: auto; display: flex; align-items: center; color: var(--muted-color); text-decoration: none; &:hover { color: var(--text-color); } svg { width: 20px; height: 20px; fill: currentColor; } } } #goto-input { background: var(--input-bg); color: var(--text-color); border: 1px solid var(--input-border-color); border-radius: 3px; padding: 2px 6px; font-family: inherit; font-size: inherit; &:disabled { opacity: 0.4; } &[aria-invalid="true"] { border-color: var(--changed-color); } } #status { color: var(--muted-color); font-style: italic; } #tree.loading { pointer-events: none; } #tree { padding: 8px 12px; background: var(--surface-bg); border-radius: 4px; border: 1px solid var(--border-color); min-height: 60px; .node { display: block; padding: 1px 0; } .key { color: var(--accent-color); } .separator { color: var(--muted-color); } [role="button"] { display: inline-block; width: 14px; font-size: 0.7em; color: var(--muted-color); cursor: pointer; user-select: none; vertical-align: middle; } [role="group"] { padding-left: 20px; border-left: 1px dashed var(--border-subtle-color); margin-left: 2px; &.hidden { display: none; } } .ref { color: var(--ref-color); cursor: pointer; text-decoration: underline dotted; &:hover { color: var(--ref-hover-color); } } .str-value { color: var(--string-color); } .num-value { color: var(--number-color); } .bool-value { color: var(--bool-color); } .null-value { color: var(--null-color); } .name-value { color: var(--name-color); } .bracket { color: var(--muted-color); cursor: pointer; user-select: none; &:hover { color: light-dark(#444, #bbb); } } .stream-label { color: var(--stream-color); font-style: italic; } [role="status"] { color: var(--muted-color); font-style: italic; } [role="alert"] { color: var(--changed-color); } .bytes-content { padding-left: 20px; white-space: pre-wrap; font-size: 1em; opacity: 0.85; color: var(--string-color); } .bytes-hex { font-family: monospace; color: var(--bool-color); } .image-preview { display: block; margin-top: 4px; max-width: 40%; height: auto; image-rendering: pixelated; border: 1px solid var(--border-subtle-color); } .content-stm-scroll { display: flex; flex-direction: column; max-height: 60vh; border: 1px solid var(--border-subtle-color); border-radius: 3px; overflow: hidden; } .content-stm-load-sentinel { height: 1px; } .token-cmd { color: var(--accent-color); font-weight: bold; } .token-num { color: var(--number-color); } .token-str { color: var(--string-color); } .token-name { color: var(--name-color); } .token-bool { color: var(--bool-color); } .token-null { color: var(--null-color); } .token-ref { color: var(--ref-color); } .token-array, .token-dict { color: var(--text-color); } } #debug-btn, #debug-back-btn { padding: 4px 12px; border-radius: 3px; border: 1px solid var(--input-border-color); background: var(--button-bg); color: inherit; cursor: pointer; font-family: inherit; font-size: inherit; &:hover { background: var(--button-hover-bg); } } #debug-view { flex: 1; min-height: 0; display: flex; flex-direction: column; gap: 10px; &[hidden] { display: none; } } #render-panels { flex: 1; min-height: 0; display: flex; flex-direction: row; gap: 0; align-items: stretch; } #render-resizer { flex-shrink: 0; width: 6px; align-self: stretch; cursor: col-resize; background: var(--border-color); &:hover, &.dragging { background: var(--accent-color); } } /* ── Shared code-view layout (content stream and op-list) ──────────────── */ /* Row wrapper that sits between the toolbar and the scrollable content. Hosts the frozen line-number column and the actual scroll container. */ .content-stm-body, .op-list-body { flex: 1; display: flex; flex-direction: row; min-height: 0; } .content-stm-body { line-height: 1.8; } /* The line-number column lives *outside* the scroll container so it is never affected by horizontal or vertical scroll. Its scrollTop is kept in sync with the adjacent scroll container via a JS scroll listener. */ .cs-line-nums-col { overflow: hidden; flex-shrink: 0; background: var(--surface-bg); border-inline-end: 1px solid var(--border-subtle-color); } .content-stm-inner { flex: 1; overflow: auto; /* Disable scroll anchoring so manual scrollTop corrections aren't doubled. */ overflow-anchor: none; } .content-stm-instruction { display: block; white-space: nowrap; padding-inline-start: 0.5em; } .raw-bytes-stream { color: var(--string-color); } /* ── Shared search/goto toolbar (used in .content-stm-scroll and #op-list-panel) ── */ .cs-goto-bar { position: sticky; top: 0; display: flex; align-items: center; gap: 8px; padding: 3px 4px; background: var(--surface-bg); border-bottom: 1px solid var(--border-subtle-color); z-index: 1; .cs-search-group { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; } .cs-search-input, .cs-goto { font: inherit; font-size: 0.85em; padding: 2px 6px; border: 1px solid var(--input-border-color); border-radius: 3px; background: var(--input-bg); color: var(--text-color); &:focus { outline: 2px solid var(--accent-color); outline-offset: 0; } &[aria-invalid="true"] { border-color: red; } } .cs-search-input { width: 140px; } .cs-goto { width: 110px; margin-inline-start: auto; } .cs-nav-btn { font: inherit; font-size: 0.85em; padding: 1px 6px; border: 1px solid var(--input-border-color); border-radius: 3px; background: var(--button-bg); color: var(--text-color); cursor: pointer; line-height: 1.4; &:hover:not(:disabled) { background: var(--button-hover-bg); } &:disabled { opacity: 0.4; cursor: default; } &[aria-pressed="true"] { background: var(--accent-color); color: light-dark(white, black); border-color: var(--accent-color); } } .cs-match-info { font-size: 0.8em; color: var(--muted-color); white-space: nowrap; min-width: 4ch; } .cs-check-label { display: flex; align-items: center; gap: 3px; font-size: 0.85em; cursor: pointer; white-space: nowrap; } } .cs-num-item { display: block; white-space: nowrap; } /* Op-list num items need padding/spacing to match .op-line and #op-list-panel. */ .op-list-body .cs-num-item { padding: 1px 0; } .op-list-body .cs-line-nums-col { padding-block: 8px; } .cs-num-item.cs-match { background: var(--match-bg); } .cs-line-num { display: inline-block; min-width: var(--line-num-width, 3ch); padding-inline: 0.4em; text-align: right; font-family: monospace; font-size: 0.8em; color: var(--muted-color); user-select: none; } .cs-match { background: var(--match-bg); outline: 1px solid var(--match-outline-color); border-radius: 2px; } #op-left-col { flex: 1; display: flex; flex-direction: column; min-width: 0; } #op-resizer { flex-shrink: 0; height: 6px; cursor: row-resize; background: var(--border-color); &:hover, &.dragging { background: var(--accent-color); } } #op-top-row { flex: 4 1 0; display: flex; flex-direction: row; min-height: 0; } /* Wrapper injected by showRenderView() to host the toolbar above #op-list-panel without it being part of the horizontal-scroll area. */ .op-list-panel-wrapper { flex: 1 1 0; display: flex; flex-direction: column; min-width: 0; min-height: 0; & > .cs-goto-bar { position: static; border: 1px solid var(--border-color); border-bottom-color: var(--border-subtle-color); border-radius: 4px 4px 0 0; } & > .op-list-body > #op-list-panel { flex: 1 1 0; border-radius: 0 0 4px 4px; border-top: none; } } #op-list-panel { flex: 1 1 0; overflow: auto; min-width: 0; min-height: 0; padding: 8px 12px; background: var(--surface-bg); border-radius: 4px; border: 1px solid var(--border-color); } #op-list { min-width: max-content; } #op-gfx-state-resizer { flex-shrink: 0; width: 6px; align-self: stretch; cursor: col-resize; background: var(--border-color); &:hover, &.dragging { background: var(--accent-color); } } #gfx-state-panel { flex: 0 0 auto; width: 40ch; overflow: auto; min-height: 0; padding: 8px 12px; background: var(--surface-bg); border-radius: 4px; border: 1px solid var(--border-color); .gfx-state-section + .gfx-state-section { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--border-color); } .gfx-state-title { display: flex; align-items: center; justify-content: space-between; gap: 4px; color: var(--accent-color); font-weight: bold; margin-bottom: 4px; } .gfx-state-stack-nav { display: flex; align-items: center; gap: 2px; font-weight: normal; font-size: 0.8em; &[hidden] { display: none; } } .gfx-state-stack-btn { padding: 0 3px; border: 1px solid currentcolor; border-radius: 2px; background: transparent; color: inherit; cursor: pointer; line-height: 1.3; &:disabled { opacity: 0.35; cursor: default; } } .gfx-state-stack-pos { min-width: 4ch; text-align: center; font-variant-numeric: tabular-nums; } .gfx-state-row { display: flex; align-items: center; gap: 8px; padding: 1px 0; } .gfx-state-key { color: var(--muted-color); flex-shrink: 0; min-width: 20ch; } .gfx-state-val { color: var(--number-color); flex: 1 1 0; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } } #op-detail-panel { flex: 1 1 0; container-type: size; overflow: auto; min-height: 0; padding: 8px 12px; background: var(--surface-bg); border-radius: 4px; border: 1px solid var(--border-color); .detail-name { color: var(--accent-color); font-weight: bold; margin-bottom: 4px; } .detail-empty { color: var(--muted-color); font-style: italic; } .detail-row { display: flex; align-items: center; gap: 8px; padding: 1px 0; } .detail-idx { color: var(--muted-color); flex-shrink: 0; } .detail-val { color: var(--number-color); white-space: pre-wrap; word-break: break-all; } .detail-body { display: flex; flex-direction: row; gap: 12px; align-items: flex-start; } .detail-args-col { flex: 1; min-width: 0; } .detail-img-col { flex-shrink: 0; max-width: 45%; overflow: hidden; .image-preview { height: 90cqh; width: auto; max-width: 100%; margin-top: 0; } } .path-preview { flex-shrink: 0; border: 1px solid var(--border-subtle-color); border-radius: 3px; background: var(--bg-color); } } #canvas-panel { flex: 1; display: flex; flex-direction: column; min-width: 0; background: var(--surface-bg); border-radius: 4px; border: 1px solid var(--border-color); } #canvas-toolbar { flex-shrink: 0; display: flex; align-items: center; justify-content: center; gap: 6px; padding: 4px 8px; border-bottom: 1px solid var(--border-color); button { padding: 1px 8px; border-radius: 3px; border: 1px solid var(--input-border-color); background: var(--button-bg); color: inherit; cursor: pointer; font-family: inherit; font-size: 1.1em; line-height: 1.4; &:hover { background: var(--button-hover-bg); } &:disabled { opacity: 0.4; cursor: default; } } #zoom-level { min-width: 4ch; text-align: center; } } #canvas-scroll { flex: 1; overflow: auto; padding: 8px 12px; min-height: 0; background: var(--clr-canvas-bg); display: flex; flex-direction: column; align-items: safe center; gap: 12px; } #canvas-wrapper { position: relative; display: inline-block; line-height: 0; } .temp-canvas-wrapper { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; cursor: pointer; } .temp-canvas-label { font-size: 0.85em; color: var(--muted-color); font-style: italic; } .temp-canvas-wrapper canvas { border: 1px solid var(--border-subtle-color); zoom: calc(1 / var(--dpr, 1)); } #render-canvas { cursor: pointer; } #highlight-canvas { position: absolute; top: 0; left: 0; pointer-events: none; } .op-line { display: flex; align-items: center; gap: 0.5ch; white-space: nowrap; padding: 1px 0; cursor: pointer; &.selected { text-decoration: underline; } &:hover { background: var(--hover-bg); color: var(--hover-color); } } .color-swatch { width: 14px; height: 14px; border-radius: 50%; border: 1px solid var(--muted-color); flex-shrink: 0; } .op-name { color: var(--accent-color); font-weight: bold; } .op-arg { color: var(--number-color); } .changed-value { font-weight: bold; background: var(--changed-bg); color: var(--changed-color); } .bp-gutter { display: inline-flex; align-items: center; justify-content: center; width: 14px; flex-shrink: 0; cursor: pointer; user-select: none; &::before { content: "●"; color: var(--changed-color); font-size: 0.75em; opacity: 0; } &:hover::before { opacity: 0.4; } &.active::before { opacity: 1; } } .op-line.paused { background: var(--paused-bg); color: var(--paused-color); outline: 1px solid var(--paused-outline-color); outline-offset: -1px; }