Four user reports from the 0.6.0 rollout: - Text tool never committed. openTextInput placed the box correctly but a couple of Chromium quirks stopped a normal type-Enter cycle: focus() called synchronously right after appendChild lost the race in some builds, and the input's own mousedown / click was bubbling through to #base and re-firing openTextInput on every subsequent keystroke click-through, so what looked like "nothing happens" was actually "a new empty box spawned on top of the last one every time". Now: focus after requestAnimationFrame, contain pointerdown / mousedown / click inside the input so they don't bubble to the canvas, track the font size on the state so commit uses the same one openTextInput measured against, and preventDefault on the base pointerdown so Chromium doesn't reset focus back to <body>. - Toolbar wrapped one dot at a time when the sidebar was narrow (a lonely thin/medium/thick width would jump to a second row while the swatches stayed above it). Toolbar items are now wrapped in `<div class="tgroup">` per category — tools / swatches / widths / undo-redo — with `flex: 0 0 auto`, so a whole row wraps as a unit and lands cleanly under the previous one. `gap: 10px / row-gap: 6px` keeps the visual grouping obvious. - No way to close the sidebar without hunting for the dock icon. Added an X button in the top-right of both the sidebar panel and the editor toolbar. Both wire through a new `silentmode.sidebar.close()` preload method that calls the existing `sidebar-close` IPC. - Tightened the pointerdown text branch so preventDefault + explicit focus-after-frame make the click-through races impossible. Bundled but not shipped separately — parent session signs and pushes.
136 lines
6.2 KiB
CSS
136 lines
6.2 KiB
CSS
/* Screenshot editor v2 — sidebar-embedded. Compact but not cramped; scales
|
|
the canvas to fit whatever width the sidebar has, up to full window when
|
|
the user hits the maximize button in the topbar. */
|
|
: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; }
|
|
@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; }
|
|
}
|
|
* { 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; }
|
|
/* Author styles set `display: flex` on .empty/.stage — plain [hidden]
|
|
loses the cascade fight and the "Loading capture" pill stayed visible
|
|
over the drawn image. Force-hide takes it out for real. */
|
|
[hidden] { display: none !important; }
|
|
|
|
.topbar, .toolbar { display: flex; align-items: center; gap: 4px; padding: 6px 8px;
|
|
border-bottom: 1px solid var(--line); background: var(--panel);
|
|
user-select: none; flex-wrap: wrap; }
|
|
/* Older versions grouped the drawing tools in the middle of the bar — keep
|
|
the drawing / swatch / width / undo cluster centred while the top-bar
|
|
(back / name → save / copy / sound / max / close) stays edge-anchored. */
|
|
.toolbar { justify-content: center; gap: 10px; row-gap: 6px; }
|
|
/* Each visible group of tools wraps as a unit — when the sidebar gets
|
|
narrow, the whole swatch row moves to the next line instead of a
|
|
single dot spilling by itself. */
|
|
.tgroup { display: inline-flex; align-items: center; gap: 4px; flex: 0 0 auto; }
|
|
|
|
/* Close-sidebar button — reads as danger without shouting. */
|
|
#close-sidebar:hover { border-color: rgba(255,91,91,.55); color: var(--danger); }
|
|
.topbar .name { color: var(--dim); font-size: 11.5px; overflow: hidden;
|
|
text-overflow: ellipsis; white-space: nowrap; max-width: 200px; margin: 0 4px; }
|
|
.topbar .spacer, .toolbar .spacer { flex: 1; }
|
|
|
|
.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;
|
|
}
|
|
.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, .tool:disabled:hover { border-color: var(--line); }
|
|
.tool.active {
|
|
background: rgb(from var(--acid) r g b / .15);
|
|
border-color: rgb(from var(--acid) r g b / .7);
|
|
color: var(--acid);
|
|
}
|
|
.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; }
|
|
|
|
.sep { width: 1px; height: 20px; background: var(--line); margin: 0 3px; }
|
|
|
|
.swatch {
|
|
width: 22px; height: 22px; border-radius: 50%; padding: 0;
|
|
border: 2px solid var(--line); cursor: pointer;
|
|
}
|
|
.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); }
|
|
|
|
/* Canvas board -------------------------------------------------------- */
|
|
.board {
|
|
flex: 1; overflow: auto; background: var(--board);
|
|
display: flex; align-items: flex-start; justify-content: center;
|
|
padding: 12px;
|
|
}
|
|
.empty { color: var(--dim); font-size: 13px; padding: 40px 20px; text-align: center;
|
|
display: flex; align-items: center; justify-content: center; height: 100%; }
|
|
.empty.err { color: var(--danger); }
|
|
|
|
.stage { position: relative; transform-origin: top left;
|
|
box-shadow: 0 2px 12px rgba(0,0,0,.35); background: #fff; }
|
|
.stage canvas { display: block; position: absolute; left: 0; top: 0; }
|
|
.stage #base { position: static; }
|
|
.stage #over { pointer-events: none; }
|
|
.stage[data-tool="arrow"] { cursor: crosshair; }
|
|
.stage[data-tool="rect"] { cursor: crosshair; }
|
|
.stage[data-tool="ellipse"]{ cursor: crosshair; }
|
|
.stage[data-tool="pen"] { cursor: crosshair; }
|
|
.stage[data-tool="text"] { cursor: text; }
|
|
.stage[data-tool="crop"] { cursor: crosshair; }
|
|
.stage[data-tool="blur"] { cursor: crosshair; }
|
|
|
|
/* Apply-crop is the primary action while a crop marquee is up. */
|
|
#apply-crop {
|
|
background: var(--acid); color: #101418; border-color: transparent; font-weight: 600;
|
|
}
|
|
#apply-crop:hover { filter: brightness(1.06); background: var(--acid); border-color: transparent; }
|
|
#cancel-crop:hover { border-color: rgba(255,91,91,.5); color: var(--danger); }
|
|
|
|
.text-input {
|
|
position: absolute; z-index: 10;
|
|
background: rgba(20,26,36,.92); color: var(--ink);
|
|
border: 1px solid var(--acid); padding: 2px 6px;
|
|
font: 15px/1.2 system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
|
|
outline: none; border-radius: 3px; min-width: 80px;
|
|
}
|
|
|
|
.toast {
|
|
position: fixed; left: 50%; bottom: 20px; transform: translateX(-50%) translateY(20px);
|
|
padding: 8px 14px; border-radius: 6px; background: rgba(11,14,20,.94);
|
|
color: var(--ink); 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,.5); color: var(--danger); }
|
|
</style>
|