Select does what selecting does in every editor people already know. Double-click bare page and a caret opens there; double-click the document's own words and they open for replacement; what is selected copies with Ctrl+C, pastes with Ctrl+V and goes with Del. Nothing was taken away — a drag on empty page still gathers an area, and a drag that starts on words still selects words to copy. Type text and Edit text were two buttons for one question the click already answers. They are one Text tool: land on the document's own words and it offers to replace them, land anywhere else and it starts new text. The words light up under the cursor so which is which is visible before clicking, not after. The toolbar says what it is for. Select, Text and Sign are labelled and set apart; the drawing kit and the markup kit are their own groups. Sign gets a pen icon over a signature rather than a squiggle that could have been anything. Signatures take ink — black, blue, red, green — chosen while drawing and kept with the signature, because people sign in a particular colour and it belongs to the signature, not to whichever swatch was armed. And they turn: a grip above the box, free rotation, Shift to snap to 15°, for the signing line that is not square to the page. Two faults the tests found, both invisible by eye: The rotate grip was drawn in the right place and could not be grabbed — the selection bar floats directly above a mark, which is exactly where the grip sits, and it swallowed every click. The bar now stands clear of it. Undo would not undo a first rotation. Restoring a mark with Object.assign copies the keys the original HAD, so a property the drag introduced survived the restore; the journal then recorded the rotated state as the state to go back to. Restoring now forgets keys the original never had, which fixes every future property with the same shape. Also: building a document from pictures or joins refuses to start a second one on top of the first, and says so rather than failing quietly.
363 lines
22 KiB
CSS
363 lines
22 KiB
CSS
/* PDF Editor — full-tab. Same palette and control shapes as the screenshot
|
|
editor so the two add-ons read as one family; loaded AFTER pdf.js's
|
|
pdf_viewer.css so the overrides at the bottom win. */
|
|
:root { color-scheme: light dark;
|
|
--bg:#0e131c; --panel:#141a24; --panel2:#191f2b; --line:rgba(255,255,255,.09);
|
|
--ink:#e7eaf1; --mut:#8b98a9; --dim:#5e6678; --acid:#d6ff3d;
|
|
--danger:#ff5b5b; --board:#0a0d13; --shadow:rgba(0,0,0,.45); }
|
|
@media (prefers-color-scheme: light) {
|
|
:root { --bg:#f8faff; --panel:#ffffff; --panel2:#eff3fb; --line:rgba(0,0,0,.10);
|
|
--ink:#1a1f2b; --mut:#5c6577; --dim:#8a93a5; --acid:#0AC18E;
|
|
--board:#dde3ee; --shadow:rgba(20,30,50,.20); }
|
|
}
|
|
* { box-sizing: border-box; }
|
|
html, body { margin: 0; height: 100%; }
|
|
body { background: var(--bg); color: var(--ink);
|
|
font: 13px/1.5 system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
|
|
display: flex; flex-direction: column; overflow: hidden; }
|
|
/* pdf.js's stylesheet sets display on some of the nodes we toggle, so plain
|
|
[hidden] loses the cascade fight. */
|
|
[hidden] { display: none !important; }
|
|
|
|
/* ---- bars ----------------------------------------------------------- */
|
|
.topbar, .toolbar { display: flex; align-items: center; gap: 4px; padding: 6px 8px;
|
|
border-bottom: 1px solid var(--line); background: var(--panel);
|
|
user-select: none; }
|
|
.topbar { flex-wrap: nowrap; }
|
|
.toolbar { align-items: flex-start; gap: 8px; row-gap: 6px; flex-wrap: wrap; }
|
|
.tool-cluster { display: flex; flex-wrap: wrap; align-items: center;
|
|
gap: 10px; row-gap: 6px; flex: 1 1 auto; justify-content: center; }
|
|
.tgroup { display: inline-flex; align-items: center; gap: 4px; flex: 0 0 auto; }
|
|
.tgroup.zoomgroup { align-self: center; }
|
|
.spacer { flex: 1; }
|
|
.sep { width: 1px; height: 20px; background: var(--line); margin: 0 4px; flex: 0 0 auto; }
|
|
|
|
.docname { color: var(--mut); font-size: 12.5px; overflow: hidden; min-width: 0;
|
|
text-overflow: ellipsis; white-space: nowrap; max-width: 46vw; }
|
|
.docname.dirty::after { content: " •"; color: var(--acid); font-weight: 700; }
|
|
|
|
.btn, .tool {
|
|
border: 1px solid var(--line); background: var(--panel2); color: var(--ink);
|
|
width: 30px; height: 30px; border-radius: 6px; cursor: pointer;
|
|
display: inline-flex; align-items: center; justify-content: center;
|
|
padding: 0; transition: background 100ms, border-color 100ms;
|
|
font: inherit; line-height: 0; flex: 0 0 auto;
|
|
}
|
|
.btn:hover, .tool:hover { border-color: rgb(from var(--acid) r g b / .5); }
|
|
.btn:disabled, .tool:disabled { opacity: .4; cursor: default; }
|
|
.btn:disabled:hover { border-color: var(--line); }
|
|
.btn svg, .tool svg { width: 16px; height: 16px; display: block; }
|
|
.btn.wide { width: auto; padding: 0 10px; gap: 6px; line-height: 1; }
|
|
.btn.wide span { font-size: 12px; }
|
|
.btn.primary { background: var(--acid); color: #101418; border-color: transparent; font-weight: 600; }
|
|
.btn.primary:hover { filter: brightness(1.06); border-color: transparent; }
|
|
.btn.primary:disabled:hover { filter: none; }
|
|
.btn.danger { color: var(--danger); }
|
|
.btn.danger:hover { border-color: rgba(255,91,91,.55); }
|
|
.tool.active { background: rgb(from var(--acid) r g b / .15);
|
|
border-color: rgb(from var(--acid) r g b / .7); color: var(--acid); }
|
|
/* Redact is destructive; it reads red when armed rather than acid. */
|
|
.tool[data-tool="redact"].active { background: rgba(255,91,91,.16);
|
|
border-color: rgba(255,91,91,.7); color: var(--danger); }
|
|
|
|
.swatch { width: 22px; height: 22px; border-radius: 50%; padding: 0;
|
|
border: 2px solid var(--line); cursor: pointer; flex: 0 0 auto;
|
|
/* The near-black ink swatch would otherwise disappear into a dark
|
|
toolbar; a faint inner ring keeps every colour countable. */
|
|
box-shadow: inset 0 0 0 1px rgb(from var(--ink) r g b / .28); }
|
|
.swatch.active { border-color: var(--acid);
|
|
outline: 1px solid rgb(from var(--acid) r g b / .35); outline-offset: 1px; }
|
|
.width { border: 1px solid var(--line); background: var(--panel2); color: var(--ink);
|
|
width: 30px; height: 30px; border-radius: 6px; cursor: pointer;
|
|
display: inline-flex; align-items: center; justify-content: center; padding: 0; }
|
|
.width.active { border-color: rgb(from var(--acid) r g b / .7);
|
|
background: rgb(from var(--acid) r g b / .15); }
|
|
.width .dot { border-radius: 50%; background: currentColor; color: var(--ink); }
|
|
.width.active .dot { color: var(--acid); }
|
|
|
|
select, input[type="text"], textarea {
|
|
border: 1px solid var(--line); background: var(--panel2); color: var(--ink);
|
|
border-radius: 6px; padding: 5px 7px; font: inherit; height: 30px;
|
|
}
|
|
textarea { height: auto; resize: vertical; width: 100%; line-height: 1.45; }
|
|
select:focus, input:focus, textarea:focus { outline: none; border-color: rgb(from var(--acid) r g b / .6); }
|
|
|
|
/* ---- body: viewer + rail -------------------------------------------- */
|
|
.body { flex: 1; display: flex; min-height: 0; }
|
|
.viewerContainer { position: absolute; inset: 0; overflow: auto; background: var(--board); }
|
|
.body { position: relative; }
|
|
.viewerContainer { position: relative; flex: 1 1 auto; min-width: 0; }
|
|
|
|
.rail { flex: 0 0 176px; border-left: 1px solid var(--line); background: var(--panel);
|
|
display: flex; flex-direction: column; min-height: 0; }
|
|
body[data-rail="off"] .rail { display: none; }
|
|
.rail-head { display: flex; align-items: center; gap: 6px; padding: 7px 10px;
|
|
border-bottom: 1px solid var(--line); color: var(--dim); font-size: 11.5px; }
|
|
.rail-list { flex: 1; overflow: auto; padding: 8px; display: flex;
|
|
flex-direction: column; gap: 8px; }
|
|
|
|
.thumb { position: relative; border: 1px solid var(--line); border-radius: 6px;
|
|
background: var(--panel2); padding: 6px; cursor: grab; }
|
|
.thumb.current { border-color: rgb(from var(--acid) r g b / .75); }
|
|
.thumb.dragging { opacity: .45; }
|
|
.thumb.dropbefore { box-shadow: 0 -3px 0 0 var(--acid); }
|
|
.thumb.dropafter { box-shadow: 0 3px 0 0 var(--acid); }
|
|
.thumb.deleted { opacity: .38; }
|
|
.thumb.deleted .tcanvas { filter: grayscale(1); }
|
|
.thumb .tcanvas { display: block; width: 100%; height: auto; background: #fff;
|
|
border-radius: 3px; box-shadow: 0 1px 4px var(--shadow); }
|
|
.thumb .tfoot { display: flex; align-items: center; gap: 3px; margin-top: 5px; }
|
|
.thumb .tnum { color: var(--dim); font-size: 11px; font-variant-numeric: tabular-nums;
|
|
flex: 1 1 auto; min-width: 0; }
|
|
.thumb .tbtn { width: 20px; height: 20px; border-radius: 4px; border: 1px solid var(--line);
|
|
background: var(--panel); color: var(--ink); cursor: pointer; padding: 0;
|
|
display: inline-flex; align-items: center; justify-content: center; }
|
|
.thumb .tbtn svg { width: 12px; height: 12px; display: block; }
|
|
.thumb .tbtn:hover { border-color: rgb(from var(--acid) r g b / .55); }
|
|
.thumb .tbtn.del:hover { border-color: rgba(255,91,91,.6); color: var(--danger); }
|
|
|
|
.fbtn { border: 1px solid var(--line); background: var(--panel2); color: var(--ink);
|
|
border-radius: 6px; cursor: pointer; padding: 3px 7px; font: inherit;
|
|
font-size: 11px; line-height: 1.4; }
|
|
.fbtn:hover { border-color: rgb(from var(--acid) r g b / .55); }
|
|
|
|
/* ---- footer --------------------------------------------------------- */
|
|
.footer { display: flex; align-items: center; gap: 8px; padding: 5px 10px;
|
|
border-top: 1px solid var(--line); background: var(--panel); font-size: 11.5px; }
|
|
.fstat { color: var(--dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
.fstat.err { color: var(--danger); }
|
|
|
|
/* ---- drop zone ------------------------------------------------------ */
|
|
.dropzone { position: absolute; inset: 0; display: none; align-items: center;
|
|
justify-content: center; pointer-events: none; z-index: 20; }
|
|
body[data-empty="1"] .dropzone { display: flex; pointer-events: auto; }
|
|
body[data-dragover="1"] .dropzone { display: flex; pointer-events: none;
|
|
background: rgb(from var(--acid) r g b / .08); }
|
|
.dz-inner { text-align: center; color: var(--dim); border: 2px dashed var(--line);
|
|
border-radius: 14px; padding: 40px 54px; background: var(--panel);
|
|
max-width: 82%; }
|
|
body[data-dragover="1"] .dz-inner { border-color: rgb(from var(--acid) r g b / .8); color: var(--ink); }
|
|
.dz-inner svg { width: 48px; height: 48px; margin-bottom: 10px; }
|
|
.dz-title { font-size: 15px; color: var(--ink); font-weight: 600; }
|
|
.dz-sub { margin-top: 5px; max-width: 42ch; margin-left: auto; margin-right: auto; }
|
|
|
|
/* Start-screen tiles. Hidden while a drag is in flight: during a drop the
|
|
drop zone is the whole target, and a row of buttons under the cursor
|
|
reads as somewhere to aim, which it is not. */
|
|
.dz-actions { display: flex; gap: 10px; margin-top: 22px; justify-content: center;
|
|
flex-wrap: wrap; }
|
|
body[data-dragover="1"] .dz-actions { visibility: hidden; }
|
|
.tile { display: flex; flex-direction: column; align-items: flex-start; gap: 3px;
|
|
width: 176px; padding: 13px 14px; text-align: left; cursor: pointer;
|
|
background: var(--bg); color: var(--ink); border: 1px solid var(--line);
|
|
border-radius: 10px; font: inherit; }
|
|
.tile:hover { border-color: rgb(from var(--acid) r g b / .55);
|
|
background: rgb(from var(--acid) r g b / .06); }
|
|
.tile:focus-visible { outline: 2px solid var(--acid); outline-offset: 2px; }
|
|
.tile svg { width: 22px; height: 22px; margin: 0 0 5px; color: var(--acid); }
|
|
.tile-t { font-weight: 600; font-size: 13px; }
|
|
.tile-s { font-size: 11.5px; color: var(--dim); line-height: 1.35; }
|
|
.linkish { background: none; border: none; padding: 0; font: inherit; cursor: pointer;
|
|
color: var(--acid); text-decoration: underline; }
|
|
|
|
/* ---- the three main tools ------------------------------------------- */
|
|
/* Point at it, write on it, sign it. Wider, labelled, and set apart from the
|
|
drawing kit, because they are the things most people open a PDF to do and
|
|
they used to be four identical squares in a row of thirteen. */
|
|
.tgroup.primary { gap: 4px; }
|
|
.tool.big { width: auto; padding: 0 10px; gap: 6px; }
|
|
.tool.big .tlabel { font-size: 12px; line-height: 1; }
|
|
.tool.big svg { width: 15px; height: 15px; }
|
|
@media (max-width: 900px) { .tool.big .tlabel { display: none; } .tool.big { width: 30px; padding: 0; } }
|
|
|
|
/* ---- typing on the page --------------------------------------------- */
|
|
/* Positioned by lib/inline.js against the page's own coordinates, so it sits
|
|
where the text will sit. No background: the page is the background, which
|
|
is the whole point — you see the words against what they are going on. */
|
|
.inline-text { position: absolute; z-index: 24; margin: 0; padding: 0;
|
|
min-width: 1px; white-space: pre; outline: none;
|
|
caret-color: currentColor;
|
|
box-shadow: 0 0 0 1px rgb(from var(--acid) r g b / .55),
|
|
0 0 0 4px rgb(from var(--acid) r g b / .14); }
|
|
.inline-text:empty::before { content: ""; display: inline-block; width: 1px; }
|
|
|
|
/* ---- signature library ---------------------------------------------- */
|
|
.siglist { list-style: none; margin: 0 0 12px; padding: 0; display: grid;
|
|
gap: 8px; max-height: 40vh; overflow: auto; }
|
|
.sigrow { display: flex; align-items: center; gap: 10px; padding: 8px 10px;
|
|
border: 1px solid var(--line); border-radius: 9px; background: var(--bg); }
|
|
.sigrow .sigpreview { flex: 0 0 132px; height: 44px; }
|
|
.sigrow .sigpreview svg { width: 100%; height: 100%; }
|
|
.sigrow .signame { flex: 1 1 auto; min-width: 0; font-size: 13px; color: var(--ink);
|
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
/* flex: none on the buttons — without it they shrink below their own labels
|
|
and the words run into each other. The name gives way instead. */
|
|
.sigrow .sigacts { display: flex; gap: 6px; flex: 0 0 auto; }
|
|
/* width: auto matters most — .btn is a 30px square by default, made for an
|
|
icon, and a word does not fit in one. */
|
|
.sigrow .sigacts .btn { flex: none; width: auto; height: 26px; white-space: nowrap;
|
|
padding: 0 10px; font-size: 11.5px; line-height: 1; }
|
|
/* Ink for the pad. Dark by default: a signature is not a highlight. */
|
|
.sigink { display: flex; gap: 6px; margin: 0 0 8px; }
|
|
.sigink .swatch { width: 22px; height: 22px; border-radius: 50%;
|
|
border: 2px solid var(--line); cursor: pointer; padding: 0; }
|
|
.sigink .swatch.active { border-color: var(--acid); }
|
|
|
|
/* ---- modals --------------------------------------------------------- */
|
|
.scrim { position: fixed; inset: 0; background: rgba(4,7,12,.62); z-index: 50;
|
|
display: flex; align-items: center; justify-content: center; padding: 24px; }
|
|
.modal { background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
|
|
box-shadow: 0 18px 60px rgba(0,0,0,.5); padding: 18px 20px; max-width: 560px;
|
|
width: 100%; max-height: 90vh; overflow: auto; }
|
|
.modal h2 { margin: 0 0 10px; font-size: 15px; }
|
|
.modal p { margin: 0 0 10px; color: var(--mut); }
|
|
.modal p strong { color: var(--ink); }
|
|
.mhint { font-size: 12px; }
|
|
.mhint.warn { color: var(--danger); }
|
|
.mrow { display: flex; align-items: center; gap: 8px; margin-top: 14px; }
|
|
.mfield { display: inline-flex; align-items: center; gap: 6px; color: var(--mut); font-size: 12px; }
|
|
.mfield.block { display: flex; flex-direction: column; align-items: stretch; gap: 5px; }
|
|
.check { display: flex; align-items: flex-start; gap: 8px; margin: 12px 0 0;
|
|
color: var(--ink); font-size: 12.5px; cursor: pointer; }
|
|
.check input { margin: 2px 0 0; accent-color: var(--acid); }
|
|
#sign-pad { width: 100%; height: auto; aspect-ratio: 640 / 220; background: #fff;
|
|
border: 1px solid var(--line); border-radius: 8px; cursor: crosshair;
|
|
touch-action: none; display: block; }
|
|
.summary { margin: 10px 0 0; padding-left: 18px; color: var(--mut); font-size: 12px; }
|
|
.summary li { margin: 2px 0; }
|
|
.summary li.warn { color: var(--danger); }
|
|
|
|
.toast { position: fixed; left: 50%; bottom: 44px; transform: translateX(-50%) translateY(20px);
|
|
padding: 8px 14px; border-radius: 6px; background: rgba(11,14,20,.95);
|
|
color: #e7eaf1; font-size: 12.5px; border: 1px solid var(--line);
|
|
opacity: 0; pointer-events: none; transition: opacity 180ms, transform 180ms;
|
|
z-index: 100; max-width: 80vw; text-align: center; }
|
|
.toast.on { opacity: 1; transform: translateX(-50%) translateY(0); }
|
|
.toast.err { border-color: rgba(255,91,91,.6); color: #ffb3b3; }
|
|
|
|
/* ---- pdf.js layer overrides ----------------------------------------- */
|
|
/* A real gap between pages plus a drop shadow, instead of pdf.js's
|
|
transparent-border trick. */
|
|
/* overflow-anchor off: pages change height as they take their real size and
|
|
again on every zoom, and scroll anchoring "helpfully" moves the scroll
|
|
position to compensate — which walks the reader away from the page they
|
|
were on and, worse, can park the whole document outside the viewport. */
|
|
.viewerContainer { overflow-anchor: none; }
|
|
.pdfViewer { --page-margin: 0 auto 14px; --page-border: none; padding: 14px 0 4px; }
|
|
.pdfViewer .page { box-shadow: 0 2px 14px var(--shadow); }
|
|
|
|
/* Our overlay sits above the canvas but below the annotation (form) layer, so
|
|
a form field stays clickable even with marks drawn around it. */
|
|
/* Marks are clipped to the page by a clipPath on the marks group, not by the
|
|
SVG's own overflow — the selection handles sit just outside a mark's bounds
|
|
and a mark flush with the page edge still has to be grabbable. */
|
|
.pdfViewer .page .smOverlay { position: absolute; inset: 0; z-index: 3;
|
|
pointer-events: none; overflow: visible; }
|
|
.pdfViewer .page .annotationLayer { z-index: 4; }
|
|
.pdfViewer .page .textLayer { z-index: 2; }
|
|
|
|
/* Tool modality. When a drawing tool is armed the text and form layers stop
|
|
taking pointer events, so a drag paints instead of selecting text or
|
|
landing in a field. Highlight is the exception: it works BY selecting
|
|
text, so the text layer stays live for it. */
|
|
body[data-mode="draw"] .textLayer,
|
|
body[data-mode="draw"] .annotationLayer { pointer-events: none !important; }
|
|
body[data-mode="draw"] .pdfViewer .page { cursor: crosshair; }
|
|
body[data-mode="stamp"] .textLayer,
|
|
body[data-mode="stamp"] .annotationLayer { pointer-events: none !important; }
|
|
body[data-mode="stamp"] .pdfViewer .page { cursor: copy; }
|
|
body[data-mode="highlight"] .annotationLayer { pointer-events: none !important; }
|
|
body[data-mode="highlight"] .pdfViewer .page { cursor: text; }
|
|
|
|
/* Marks are only hit-testable with the select tool, so a drawing drag is
|
|
never stolen by something already on the page. */
|
|
body[data-mode="select"] .smOverlay { pointer-events: none; }
|
|
body[data-mode="select"] .smOverlay .mark { pointer-events: stroke; cursor: move; }
|
|
body[data-mode="select"] .smOverlay .mark[data-fill="1"] { pointer-events: all; }
|
|
.smOverlay .mark.sel { outline: none; }
|
|
/* ...with one exception: the SELECTED mark stays grabbable whatever tool is
|
|
armed. Its handles already were, so a shape you had just drawn could be
|
|
stretched but not shifted. Only the selected one opts in, so a press
|
|
anywhere else still draws. */
|
|
.smOverlay .mark.sel { pointer-events: stroke; cursor: move; }
|
|
.smOverlay .mark.sel[data-fill="1"] { pointer-events: all; }
|
|
.smOverlay .selbox { fill: none; stroke: var(--acid); stroke-width: 1;
|
|
stroke-dasharray: 4 3; vector-effect: non-scaling-stroke; }
|
|
/* A group: one solid box round the lot, and a fainter one round each member
|
|
so it is obvious WHICH marks are in it — a marquee that caught one more
|
|
than you meant is worth seeing before you press Delete. */
|
|
.smOverlay .selbox.group { stroke-dasharray: 2 4; stroke-width: 1.5; }
|
|
.smOverlay .selbox.member { stroke-dasharray: none; stroke-opacity: .45; }
|
|
/* The rubber band itself. */
|
|
.smOverlay .marquee { fill: rgb(from var(--acid) r g b / .10); stroke: var(--acid);
|
|
stroke-width: 1; stroke-dasharray: 5 3;
|
|
vector-effect: non-scaling-stroke; }
|
|
.smOverlay .handle { fill: var(--acid); stroke: #101418; stroke-width: 1;
|
|
vector-effect: non-scaling-stroke; cursor: pointer; pointer-events: all; }
|
|
|
|
/* pdf.js renders form widgets with a coloured backdrop; keep it, it is the
|
|
only cue that a field is fillable. */
|
|
.annotationLayer .textWidgetAnnotation input,
|
|
.annotationLayer .textWidgetAnnotation textarea,
|
|
.annotationLayer .choiceWidgetAnnotation select { font: inherit; }
|
|
|
|
/* ---- selection bar --------------------------------------------------- */
|
|
/* Pinned to the current selection in viewport coordinates, so a page edge
|
|
never clips it and moving the selection to another page needs no
|
|
re-parenting. It steps aside mid-drag rather than jittering under the
|
|
cursor. */
|
|
.selbar {
|
|
position: fixed; z-index: 40; display: flex; align-items: center; gap: 2px;
|
|
padding: 3px; border-radius: 8px; background: var(--panel);
|
|
border: 1px solid var(--line); box-shadow: 0 6px 22px var(--shadow);
|
|
user-select: none;
|
|
}
|
|
body[data-dragging="1"] .selbar,
|
|
.selbar.offscreen { opacity: 0; pointer-events: none; }
|
|
.selbar .sb {
|
|
border: 1px solid transparent; background: transparent; color: var(--ink);
|
|
min-width: 26px; height: 26px; border-radius: 5px; cursor: pointer; padding: 0 5px;
|
|
display: inline-flex; align-items: center; justify-content: center;
|
|
font: inherit; font-size: 13px; line-height: 1;
|
|
}
|
|
.selbar .sb svg { width: 15px; height: 15px; display: block; }
|
|
.selbar .sb:hover { background: var(--panel2); border-color: rgb(from var(--acid) r g b / .5); }
|
|
.selbar .sb.active { background: rgb(from var(--acid) r g b / .16);
|
|
border-color: rgb(from var(--acid) r g b / .7); color: var(--acid); }
|
|
.selbar .sb.danger:hover { border-color: rgba(255,91,91,.6); color: var(--danger); }
|
|
.selbar .sb-group { display: inline-flex; align-items: center; gap: 1px; }
|
|
.selbar .sb-val { min-width: 22px; text-align: center; font-size: 11.5px;
|
|
color: var(--mut); font-variant-numeric: tabular-nums; }
|
|
.selbar .sb-sep { width: 1px; height: 17px; background: var(--line); margin: 0 3px; }
|
|
|
|
/* Style toggles inside the text dialog. */
|
|
.btn.styl { width: 30px; font-size: 14px; line-height: 1; }
|
|
.btn.styl.active { background: rgb(from var(--acid) r g b / .16);
|
|
border-color: rgb(from var(--acid) r g b / .7); color: var(--acid); }
|
|
|
|
/* Resize handles stay grabbable whatever tool is armed, so a shape can be
|
|
sized the moment it is drawn without a detour through the select tool.
|
|
Safe to leave live because a handle is a 9 px square that exists only while
|
|
something is selected — aiming at one is not something you do by accident,
|
|
and Escape clears the selection if you want the space back for drawing. */
|
|
.smOverlay .handle { pointer-events: all; }
|
|
|
|
/* The invisible grab area under a thin stroke. `stroke` rather than
|
|
`visibleStroke`: the copy is transparent on purpose. */
|
|
body[data-mode="select"] .smOverlay .mark .hit { pointer-events: stroke; }
|
|
.smOverlay .mark .hit { pointer-events: none; }
|
|
|
|
/* Picking a run of the document's own text to replace. The text layer stays
|
|
live so a click can land on a run; the form layer steps aside so a widget
|
|
does not swallow it. The hover outline is the only cue that the invisible
|
|
text layer is a target at all. */
|
|
body[data-mode="text"] .annotationLayer { pointer-events: none !important; }
|
|
body[data-mode="text"] .pdfViewer .page { cursor: text; }
|
|
/* The document's own words light up under the cursor, so it is obvious that
|
|
clicking them replaces them and clicking anywhere else starts new text. */
|
|
body[data-mode="text"] .textLayer span:hover {
|
|
outline: 2px solid rgb(from var(--acid) r g b / .9);
|
|
outline-offset: 1px;
|
|
background: rgb(from var(--acid) r g b / .16);
|
|
border-radius: 2px;
|
|
}
|