The divider between the stats dropdown head and body lived on
.stats-body's border-top, so it vanished when collapsed and only the
hover background hinted at the boundary (flicker). Move it to
.stats-head border-bottom so it is always present and spans the full
pill width.
#139 removed inline display:block from paste.html's created banner and
compensated with a global .created-banner { display:block } override.
That override also hit the hidden placeholder banner on /new, whose
base rule is display:none, making an empty padded bar render above the
action bar. Scope the override to .created-banner.show and mark only
the paste-view banner with the show class.
- images render fitted into the editor area (object-fit contain), editor hidden
- text files load content into the editor; title placeholder convention (Python.py, Text.txt fallback)
- title auto-fills only when blank; never overwrites a typed title
- external JS + CSS classes only, CSP-safe (no inline styles/scripts)
Missing or expired paste IDs (and unknown routes) now render the main
UI chrome with a 'Paste ID not found' message in a color-coded result
card instead of a bare text/JSON 404. HTTP status stays 404. No inline
scripts or styles; new CSS uses existing --err token and pill radii.
wrap button on the new paste page looked identical whether wrap was on
or off because the btn base is already accent filled. give the toggle a
neutral off state so the accent on state is visible, matching the icon
button toggles on the paste view.
also remove the accent border on the whole language settings pill when
any child has focus. individual controls already show their own focus
rings, so the pill highlight made no sense.
Give the custom expiry and readable N times submenus a shared boxed
container with proper padding, indent them under their parent option,
and style the number inputs and unit select with palette vars, the
existing radius scale, and the select chevron. No em dashes, no hard
coded colors in new rules.
Add word-break: normal; overflow-wrap: anywhere to .paste-title-bar h1 so
a >120-char unbroken title wraps inside the title card at mobile width
instead of producing an internal + page-level horizontal scrollbar.
- Move all inline <script> blocks (layout head/theme, topbar dark toggle,
foot, paste, new, history, mine, settings, admin, unlock) to external
files under internal/web/static/. Page data reaches scripts via data-*
attributes (data-paste-id, data-default-dark) instead of template vars.
- Replace inline onclick handlers (copy, delete, stats toggle) with
addEventListener wiring.
- Convert inline style="" attributes to CSS utility classes; swatch
colors are now set via CSSOM/DOM APIs instead of innerHTML strings.
- script-src/style-src are now plain 'self'; img-src data: stays for the
SVG data-URI backgrounds. Verified with headless chromium: zero CSP
violations on all pages in dark and light presets, theme swatches,
admin lock, tables and paste view render correctly.
- codebody/editor: overflow-wrap anywhere + word-break break-all + overflow-x hidden under html[data-wrap]
- code container and mobile .float also hidden overflow with wrap on
- topbar mobile: tighten gap/padding/nav link padding so nothing overflows at 375px
Fixes#152
Two compounding causes:
- CSP img-src fell back to default-src (self), blocking the data: URI
chevron image on all served pages, so no select ever painted it.
- .editor-head select used the background shorthand, wiping the
background-image set by the later select rule.
Chevron stroke is now a per-preset --chevron token (light stroke for
dark presets, dark for light presets) instead of hardcoded #888.
CSP gains img-src 'self' data: so the data-URI icons can load.
The PASTE column fallback for untitled pastes used the .slug class, which
renders as a monospace chip with a background pill. Give it a dedicated
paste-name modifier that strips the chip styling (background, padding,
radius, mono font) so the fallback looks identical to a titled paste
(var(--fg) plain text). The dedicated ID column and URL column chips keep
their existing styling. Applied to both /history and /mine tables.
Shared client-side preference in localStorage 'palette-wrap', default
off. Toggle button on /new editor head and paste title bar, plus a Line
wrap checkbox in the settings menu; all read/write the same preference.
Active state uses the accent treatment like other toggles. No em dashes
in UI text.
- Collapse the 10 theme cards into 5 pairs: light swatches top row, dark bottom row
- Dark mode toggle in settings and topbar (sun/moon inline SVG)
- Toggling dark mode switches to the other variant of the selected pair
- Persist pair id in localStorage palette-theme, dark flag in palette-dark
- Head script resolves base+dark to variant; URL ?theme= accepts both ids and wins
- Settings gear highlights like nav tabs on /settings
- PALETTE_DEFAULT_DARK env var sets server default dark state (default on)
- Server-side resolution helpers + tests
- Backend create/can validation paths emit machine-readable error codes
(slug_taken, slug_invalid, content_empty, content_too_large,
expiry_invalid, rate_limited, ...) alongside the human message
- new.html JS maps codes to plain-language guidance with generic fallback
- Result card colored via --ok/--err/--warn left border (result-ok/err/warn)
- docs/API.md error section documents the code field
- Tests assert the code on every validation path
Arrow is absolutely positioned at left:8px in the th, label padding-left
24px; translateY(-50%) keeps it centered regardless of row height, and
being out of flow the label never moves between sorted/unsorted states.
- Fix filter fetch to request limit=100 (API max) instead of 500, which
the API silently clamped, so filtered results actually cover the fetch window.
- Document client-side filtering behavior and limits in README Performance Notes.