Skip to content

UI redesign: theme toggle, inline filter bar, sort chips, real paginator#869

Open
VaishnavGhenge wants to merge 61 commits intojazzband:masterfrom
VaishnavGhenge:upstream-pr
Open

UI redesign: theme toggle, inline filter bar, sort chips, real paginator#869
VaishnavGhenge wants to merge 61 commits intojazzband:masterfrom
VaishnavGhenge:upstream-pr

Conversation

@VaishnavGhenge
Copy link
Copy Markdown

Closes #838

Summary

This PR contributes a full UI redesign developed in the django-silky fork, with the maintainer feedback from the initial review addressed.

What's new

  • Light/dark theme toggle — CSS custom properties (--silk-*), preference persisted in localStorage
  • Inline collapsible filter bar — replaces the 300 px slide-out drawer on requests + profiling pages
  • Multi-column sort chips — session-persisted, reflected in URL for shareability
  • Real Django Paginator — replaces query slicing; per-page setting in URL
  • Hero bar + metric pill layout — on request and profile detail pages
  • Self-hosted Lucide icons — zero CDN calls (silk/static/silk/lib/lucide.min.js)

Screenshots are in the updated README.

Maintainer feedback addressed

  • silk_seed.py moved from silk/management/commands/ (was shipping in the library) to project/example_app/management/commands/ (dev-only, excluded from the wheel by the existing find_packages exclude rule)
  • migrate_to_silky.py and MIGRATING.md removed (fork-specific artifacts)
  • setup.py restored to upstream django-silk identity (name, author, URLs)
  • CI expanded: Python 3.10–3.13 × Django 4.2–5.2 × SQLite / PostgreSQL / MySQL

Test baseline

277 passed, 1 skipped

vaishnav-noovosoft and others added 30 commits February 18, 2026 17:58
Complete UI overhaul published as django-silky on PyPI.
All 248 upstream tests pass unchanged.

UI changes
- Light/dark theme toggle using CSS custom properties; preference
  persisted in localStorage
- Replaced 300 px slide-out drawer with an inline collapsible filter bar;
  open/closed state persisted in localStorage
- Multi-column sort chips with add/remove/direction toggle; sort state
  stored in Django session and reflected in URL via history.replaceState
- Real Django Paginator on all list pages (requests, profiling, SQL)
  replacing query slicing
- Hero bar + metric pill layout on all detail pages (request, SQL,
  profile, cprofile)
- Modernized clear-DB page with option cards and danger-styled button
- Timestamps show full date + 12 h clock + timezone instead of relative
  strings
- Self-hosted Lucide icons; no CDN dependencies

State persistence
- Theme: localStorage
- Filter bar open/closed: localStorage (requests + profiling)
- Active filters and sort: Django session
- SQL per-page: Django session (silk_sql_per_page)
- Profiling per-page: Django session (silk_profiling_show)
- Sort criteria + per-page: URL query params (shareable links)

Backend changes
- TIME_RANGE_PRESETS + filters_from_data() in request_filters.py
- Multi-sort _apply_sort() with SORT_OPTIONS registry in requests view
- get_page() utility in silk/utils/pagination.py
- silk_json and silk_full_datetime template filters
- summary view gains time preset context and has_data flag

Package / repo changes
- setup.py: name=django-silky, url -> VaishnavGhenge/django-silky,
  maintainer field, project_urls with migration guide link
- README.md: rewritten for django-silky identity, comparison table,
  migration quickstart
- MIGRATING.md: zero-data-loss migration guide from django-silk 5.x
- migrate_to_silky.py: automated migration script (PostgreSQL, MySQL,
  SQLite); backup -> swap -> migrate -> verify -> rollback on failure
- CLAUDE.md: fully populated dev guide

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- setup.py: use find_packages() instead of ['silk'] so all submodules
  (urls, views, models, migrations, templatetags, etc.) are included
  in the wheel — fixes ModuleNotFoundError: No module named 'silk.urls'

- silk/__init__.py: try 'django-silky' metadata first, fall back to
  'django-silk' — fixes PackageNotFoundError on fresh installs

- release.py: new automated release script with --version, --dry-run,
  --test-pypi, --test-pypi-only, --skip-tests, --skip-push flags;
  uses python -m twine/build throughout to avoid PATH issues

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rast (v1.0.3)

N+1 Query Detection:
- New silk/utils/n_plus_one.py: fingerprint-based detection (threshold ≥ 3)
- SQL list page: amber banner listing detected patterns + highlighted rows
- Request detail: N+1 warning pill in hero bar linking to SQL list
- Requests list: per-row warning icon when N+1 detected (batch 1-query lookup)

Analytics Dashboard (summary page):
- Self-hosted D3 v7 (silk/static/silk/lib/d3.min.js)
- Request Activity: full-width area chart (hourly/daily buckets)
- Status Codes: interactive donut with centre count + hover expand
- HTTP Methods: horizontal lollipop chart coloured by method
- Response Time Distribution: 6-bucket gradient histogram
- Latency Percentiles (request + SQL): gradient area + annotated dots, p25→p99

Light-mode contrast overhaul:
- Text: #111827 / #374151 / #6b7280 (WCAG AA compliant)
- Perf scale: vivid #16a34a → #9f1239
- Method/status badges: deep hues on pastel backgrounds
- Charts re-render on theme toggle and window resize
- Tooltip reads CSS vars from #silk-root for correct dark/light colors

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add --silk-chart-accent CSS var (indigo #4f46e5 light / lavender #818cf8 dark)
  so the Request Activity area+line is visible on dark backgrounds
  (was using --silk-nav-bg #1a1a2e which is indistinguishable from the dark bg)
- Fix n_plus_one fingerprint regex: only strip single-quoted string literals,
  not double-quoted SQL identifiers, so fingerprints remain human-readable
- N+1 banner now shows representative.query (real SQL) instead of the
  anonymised fingerprint that was full of confusing ? ? ? placeholders

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All 9 screenshots now show the modernized django-silky UI:
- Summary dashboard (dark mode, full + analytics section)
- Requests list (dark mode, with/without filter bar)
- Request detail (light mode, clean + N+1 badge)
- SQL list (dark mode + light mode with N+1 banner)
- SQL detail (dark mode, EXPLAIN plan + traceback)

README gets a Screenshots table section above Installation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Filter bar improvements:
- Replace single-value path <select> with custom searchable multi-select
  dropdown (MultiPathFilter → path__in)
- Replace single-value status <select> with custom multi-select dropdown
  showing coloured badges (MultiStatusCodeFilter → status_code__in)
- Method buttons now support multi-toggle instead of exclusive single
  selection (MultiMethodFilter → method__in)
- All three are backward-compatible with existing single-value sessions

UX fixes:
- "Changes pending" indicator near Apply button; appears on any filter
  change, disappears on submit — fixed always-visible bug caused by
  display:inline-flex overriding [hidden] attribute (switched to
  CSS class .is-visible)
- Active filter states now visible in dark mode: added --silk-action-bg
  / --silk-action-text theme tokens (#5252c8 in dark, distinguishable
  from --silk-filter-bg #1a1a30)
- Fixed hover contrast for HEAD/OPTIONS method buttons (generic fallback
  using --silk-row-hover-bg)
- Number inputs highlight border on focus and when non-empty
- Path multi-select trigger no longer stretches full row width

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace all bare Model.objects.get() in views with get_object_or_404
  so stale/deleted UUIDs return 404 instead of 500
- Scope ProfilingDetailView lookup to request__pk when request_id is
  present in the URL, preventing cross-request profile access
- Fix curl code-gen crash when query param values are non-string
  (str(v).encode instead of v.encode)
- Add silk_seed management command for populating realistic dev data:
  normal, N+1, error, and slow-with-cProfile request scenarios

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Syntax highlighting:
- Replace hljs 8.x with hljs 11.11.1 bundle (Python, SQL, JSON, Bash)
- New silk-theme.css using CSS custom properties for light/dark awareness
- Add language hints (language-python, language-sql, language-bash) on all
  code blocks: request detail, SQL detail, Django test client, curl, N+1 panel
- Fix code.html: remove <span>/<br> per-line structure that broke hljs 11.x
  textContent reading; output clean text + data-active-indices attribute
- detail_base.js: explicit hljs.highlightAll() + post-process line highlights
- Fix double-nesting of <pre><code> in profile_detail.html
- Fix sql_detail.html: drop |spacify|linebreaksbr (broke newlines with new hljs)
- Exclude blank active lines so only non-empty lines get highlighted
- Add --silk-code-active-line tokens for themed line highlight in both modes

Section card spacing:
- Wrap sections in .silk-detail-body on sql_detail.html and profile_detail.html

N+1 banner redesign:
- Header: title + pills (pattern count, flagged query count) + fix hint
- Each pattern row: count badge, truncated preview, ~total ms, chevron toggle
- Click to expand full formatted SQL with syntax highlighting (lazy hljs call)
- total_time_taken added to NPlusOneGroup for per-pattern cost visibility
- Load hljs + silk-theme.css on SQL list page to support in-banner highlighting

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Introduced **NPlusOneFilter** to identify requests with 3+ identical queries.
- Added multi-select filters for HTTP methods, status codes, and paths.
- Enhanced filter bar with "N+1 only" toggle and "Changes pending" indicator.
- Comprehensive test coverage for new filter functionalities and views.
- Changelog updated detailing features, bug fixes, and test additions.
The function was defined inside the IIFE but never added to the window
expose block, making the onclick handler a no-op.

Closes #2

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Triggers on v* tags, builds with python -m build, publishes via
PyPI Trusted Publishing (OIDC — no API token secret needed).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
fix: export silkToggleN1 to window so N+1 filter button works (#2)
Runs on PRs and pushes to master. Tests Python 3.12/3.13 against
Django 5.1/5.2 using SQLite — matches the local dev setup.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
#4)

- Define Light, Dark (Purple Silk), Midnight (neutral grey), High Contrast
  schemes in theme.scss — all text tokens verified ≥ 4.5:1 WCAG AA
- Bump base font size to 14px, sm to 12.5px, xs to 11.5px
- Fix perf-bad/perf-very-bad dark mode contrast (3.75→5.6:1)
- Fix N+1 pill text: #fff on orange → #3d1a00 (2.2→7.2:1)
- Replace nav theme toggle with scheme picker dropdown (4 options)
- Redesign Clear DB page into a full Settings page with visual
  scheme cards (mini colour preview + active state) and a
  Data Management danger zone section
- Rename nav item "Clear DB" → "Settings" with settings icon

Closes #4

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…from dark scheme

- Picker styles were in clear_db.scss (settings-only) causing broken
  nav dropdown on all other pages — moved to theme_toggle.scss
- Dark scheme: replace purple-tinted #0d0d1a/#181828 with deep blacks
  #0a0a0c/#141418, no hue bias

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ettes

- Replace hardcoded font sizes with `--silk-font-size-*` variables across stylesheets (base, sm, xs)
- Add new vivid palette for charts (`--silk-chart-*`, `--silk-chart-2xx/3xx/4xx/5xx`) for better SVG visibility
- Define `--silk-interactive-hover-bg` and `--silk-interactive-hover-text` variables for consistent hover states
- Update section headers, dropdowns, and buttons to use theme variables for a unified design
- Summary: add Hot Paths, N+1 Suspects tables, DB-Bound % metric card,
  Queries-per-request histogram chart
- N+1 suspects rows now link to requests filtered by view name (?view=)
- RequestsView: accept ?view= GET param for view_name filtering
- Charts: remove all gradients/shadows — flat solid fills, full-opacity
  lollipop tracks, per-segment perf coloring on percentile lines
- Status donut: switch from text-contrast status tokens to vivid
  silk-chart-2xx/3xx/4xx/5xx fill tokens
- Theme: add --silk-chart-1/2/3/4/5 categorical palette +
  --silk-chart-2xx/3xx/4xx/5xx across all 4 schemes
- Font sizes: replace 74 hardcoded px values with --silk-font-size-*
  tokens (base=14px, sm=13px, xs=12px) across all SCSS page files
- Fix: charts now re-render on scheme switch (silk-theme-changed →
  silk-scheme-changed event name)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
VaishnavGhenge and others added 29 commits April 7, 2026 00:08
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ar reflects it

Previously the view_name filter bypassed the session entirely — the
queryset was filtered but the filter bar showed nothing active.
Now ?view= is converted to a ViewNameFilter and saved to session on GET,
so it renders in the filter bar and can be cleared normally.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… quirk

Radio buttons don't fire onchange when re-clicked after programmatic uncheck.
Switch to checkboxes with JS-enforced single-selection so clearing and
re-selecting always works reliably.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ontrast

feat: analytics dashboard, 4 colour schemes, modern UI (v1.1.0)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Show all 6 new screenshots (analytics, requests, N+1, profile-detail,
cProfile, settings); remove stale references to unreplaced shots.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… v1.1.0 changelog

- setup.py: switch author to Vaishnav Ghenge <vaishnavghenge@gmail.com>;
  original django-silk attribution remains in README and LICENSE
- README: rewrite screenshot src paths to absolute raw.githubusercontent.com
  URLs so they render on the PyPI project page (relative paths don't resolve
  in PyPI's README renderer); add static.pepy.tech total-downloads badge
  alongside the existing monthly badge; relabel monthly badge for clarity
- CHANGELOG: backfill the missing 1.1.0 entry (4 colour schemes, scheme picker,
  analytics insights, view-filter fixes, lean CI workflow) and add 1.1.1 entry

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace manual twine flow with the automated release.yml pipeline
  description (Trusted Publishing on tag push); add a "do not run
  twine manually" warning explaining the CI race that causes the
  400 File-already-exists error
- Update test baseline from 248 to actual 277 passed, 1 skipped
- Update author line to match the fixed setup.py metadata

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a "Create GitHub Release" step to release.yml that runs after the
successful PyPI publish. The step extracts the matching ## [X.Y.Z] section
from CHANGELOG.md via awk and uses it as the release body via --notes-file
(safer than --notes for content with backticks/dollar signs). Falls back to
gh's --generate-notes if no CHANGELOG entry exists, so the workflow never
fails on a missing changelog.

Runs after the PyPI publish step (not before, not in parallel) so a GH
release is only ever created for a version that actually shipped to PyPI.

Adds the contents:write permission needed by gh release create. The
existing id-token:write for PyPI Trusted Publishing is preserved.

Closes the gap that left v1.1.1 without a GH release object even after
PyPI 1.1.1 was live (the v1.1.1 GH release was created manually as a
one-shot fix; this prevents recurrence).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…zation

Apply the `unlocalize` filter to `seconds` values in the filter bar template to ensure consistent serialization and avoid localization conflicts.
fix(filter bar): ensure unlocalized values for seconds filter seriali…
…elpers)

- Delete migrate_to_silky.py, MIGRATING.md, release.py, and
  .github/workflows/release.yml (all silky-fork-only artifacts)
- Rewrite README.md as upstream django-silk documentation (remove
  django-silky branding, fork comparison table, silky badges, and
  MIGRATING.md reference)
- Strip silky-specific entries from CHANGELOG.md header; retain upstream
  django-silk changelog body unchanged
- Fix silk/__init__.py to look up 'django-silk' package name (not 'django-silky')
- Fix project/wsgi.py comment to reference django-silk

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CLAUDE.md is a dev-tool config file specific to our fork and has no
place in an upstream PR. The UI overview section added to README.md
referenced our fork's screenshots URLs; revert to the clean upstream
description that was already in place.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The upstream 925376d commit added Django 6.0 to the test matrix and
setup.py classifiers. Our CI rewrite (4d7ca27) accidentally dropped it.

- Add Django 6.0 entries (Py 3.12 + 3.13) to SQLite, PostgreSQL, MySQL jobs
- Replace Django 5.0 classifier with 6.0 in setup.py (5.0 reached EOL
  and was never in upstream classifiers)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…new screenshots

Revert files that are out of scope for a UI-only PR:
- CHANGELOG.md — maintainer's responsibility, revert to upstream state
- .gitignore — remove local `postgres` dev artifact
- .pre-commit-config.yaml — revert version bumps (isort, django-upgrade)
- project/wsgi.py — revert docstring rename
- requirements.txt — revert dev-dependency version bumps

README.md: restore upstream text verbatim; update the two request-overview
screenshot references from screenshots/1.png to screenshots/requests-filter-bar.png
and screenshots/2.png to screenshots/n1-banner.png to show the new UI.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Brings in 10 upstream commits:
- Release 5.5.0 (Django 6.0, Python 3.14, hide pagination, paginator
  from URL params, fix mouse event for SQL navigation)
- Fix span elements not closed in request_summary.html
- Bump pillow 12.2.0, pygments 2.20.0 and other dep updates

Conflict: requirements.txt resolved by taking upstream versions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The merge took our reverted copies instead of upstream's. Apply the
correct upstream versions so these files are identical to upstream/master.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…eam state

These files had drifted from upstream/master:
- release.yml: was deleted; restore Jazzband release workflow
- test.yml: was replaced with our custom multi-job version; restore upstream
  tox-based matrix (Python 3.10-3.14 × Django 4.2-main × PG + MariaDB)
- requirements.txt: restore upstream versions (coverage 7.13.0, pygments 2.20.0,
  pytest-cov 7.0.0)
- setup.py: add missing Python 3.14 classifier (present in upstream)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
find_packages() was unnecessary; upstream intentionally uses the
explicit list to ensure only the silk app is distributed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@VaishnavGhenge VaishnavGhenge marked this pull request as ready for review April 16, 2026 17:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Is there an interest for improving the sql view ?

2 participants