Skip to content

feat: UI improvements — Save Figure panel, legend overhaul, polish - #19

Merged
symoon9 merged 20 commits into
mainfrom
claude/compassionate-mirzakhani-b058ff
Apr 27, 2026
Merged

feat: UI improvements — Save Figure panel, legend overhaul, polish#19
symoon9 merged 20 commits into
mainfrom
claude/compassionate-mirzakhani-b058ff

Conversation

@symoon9

@symoon9 symoon9 commented Apr 26, 2026

Copy link
Copy Markdown
Owner

Summary

  • Copy button in the header toolbar for quick clipboard export
  • Save Figure panel (save section): filename, path, DPI, format (png/jpg/pdf/svg/eps)
  • Legend overhaul: title field, collapsible series-label toggle (title → labels toggle → location → bbox)
  • Grid & Spines: styled ToggleButtons for grid (Off/On) and spine (Box/2-Side/None); compact layout fits a 260px column
  • Colors: palette dropdown shows color count per palette (e.g. "Okabe-Ito (8)"); width reduced 5%
  • UI polish: dropdown chevron 11px → 16px; logo 20px; per-element/series toggle buttons use accent text color; slider readout box compact (52px wide, 22px tall)
  • Style layer: set_legend_title() added; _mk_slider() helper for consistent slider creation across all sections
  • Bug fix: spine ToggleButtons value now uses S.SPINE_STYLES[0] — prevents TraitError when style names change
  • CSS fix: button.mpl-per-{pid} selector (was .mpl-per-{pid} button — never matched because add_class() attaches to the <button> element itself)

Test plan

  • pip install -e . + kernel restart
  • All sections render without error
  • Save panel: enter custom name/path/DPI/format → file written correctly
  • Legend: title field, labels toggle expand/collapse, location and bbox work
  • Grid toggle and all three spine options work
  • Per-element / per-series / series-labels toggle buttons show accent text color
  • Palette dropdown shows color count in each option
  • pytest tests/ -q → 56 passed

🤖 Generated with Claude Code

symoon9 and others added 10 commits April 26, 2026 01:34
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…utilities

- Add screenshot (docs/screenshot.png) below Quick Start code block
- Add link to examples/demo.ipynb for detailed usage examples
- Replace verbose palette utility code examples with a single-line
  function list + demo.ipynb link

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Buttons appear in the header bar (right side, next to Actual size toggle).

- Copy: reads the preview <img> src via JS, converts to Blob, writes to
  clipboard using navigator.clipboard.write + ClipboardItem.
  Shows "✓ Copied" / "Failed" feedback for 1.5 s then resets.
- Save: creates a temporary <a download="figure.png"> and clicks it,
  triggering a browser file-save dialog.

Each preview <img> gets a pid-scoped id (mpl-img-{pid}) so multiple
studio() panels in the same notebook don't interfere with each other.

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

Previous approach put buttons in the header (separate widget) and the image
in render_out — they ended up in different DOM contexts, so getElementById
failed silently and the buttons never appeared.

New approach:
- Buttons and image are rendered together in a single widgets.HTML inside
  render_out, guaranteeing the same DOM context in Jupyter, JupyterLab,
  and VSCode notebooks.
- data URL is embedded directly in each button's onclick JS, so no
  getElementById is needed — works even in iframe-isolated environments.
- Buttons appear above the figure preview, right-aligned.
- Copy shows "✓ Copied" / "⚠ Not supported" feedback (1.5–2 s).
- Save triggers a browser "figure.png" download dialog.
- Removed _img_id and img_toolbar from the header; reverted header to
  original layout (logo | actual-size toggle).

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

Button fix:
- Root cause: embedding a 100KB+ base64 data URL directly inside the
  onclick attribute caused HTML parsers to silently drop the <button>
  elements entirely — buttons were invisible.
- Fix: store data URL once in <img src>; buttons use
  this.closest(".mpl-prev-{pid}").querySelector("img").src to read it.
  Works in Jupyter, JupyterLab, and VSCode (same DOM context, no
  cross-widget getElementById needed).

README:
- Add centered logo (docs/mplstudio_logo_square.png, width 50%)

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

Python callbacks (comm-based) are not stripped by VSCode's DOMPurify sanitizer;
HTML onclick attributes are. Removes the Actual Size toggle (also non-functional),
uses hidden widgets.Output for clipboard JS execution.

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

- Add Save Figure section (filename, path, DPI, format dropdown + Save button);
  remove Save button from header toolbar
- Add legend title field; wrap series labels in collapsible toggle;
  reorder legend layout: title → location → bbox → labels toggle
- Replace grid Checkbox with ToggleButtons; compact spine buttons (no
  description, button_width 74px) so all three fit in a 260px column
- Dropdown chevron font-size 11px → 16px; logo font-size 1.1em → 20px
- readout=False on all sliders except axis-limits (figure_size, typography,
  opacity, legend bbox)
- Add set_legend_title() to style layer

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@symoon9 symoon9 changed the title docs: add LICENSE, README, and screenshot feat: UI improvements — Save Figure panel, legend overhaul, polish Apr 26, 2026
symoon9 and others added 10 commits April 26, 2026 11:53
- CSS: readout box width 52px (was 58px min), fixed height 22px — no
  more oversized input next to sliders; readout kept visible and editable
- helpers: add _mk_slider(cls, description, dw, **kw) for consistent
  slider construction across all sections
- figure_size, typography, opacity, legend: migrate to _mk_slider
- save_figure.py → save.py; section key "save_figure" → "save" to match
  _constants.py

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- CSS: collapsible toggle buttons (per-element/series/labels) now use
  accent-tinted background (#accent22) + accent border, text stays at
  normal color — similar style to the info button but not filled
- colors: palette dropdown width 100% → 95%; options now show color count
  e.g. "Okabe-Ito  (8)"
- grid_spines: remove font-size:0.82em from Spines label so it matches
  the Grid description label size

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- grid_spines: value=S.SPINE_STYLES[0] instead of hardcoded "box" — avoids
  TraitError when SPINE_STYLES values change (e.g. "Box" vs "box")
- _helpers: add _mk_slider(cls, desc, dw, **kw) reusable slider factory;
  readout=True by default so the compact readout box is always visible
- figure_size, typography, opacity: refactor to _mk_slider for consistency
- _constants: "save_figure" → "save" to match section name

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

Both mpl-per-{pid} and mpl-per-btn are on the same widget div —
nesting them in the selector matched nothing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
button.mpl-per-{pid}: ipywidgets add_class() attaches to the <button>
element itself, not a wrapper — the old .mpl-per-{pid} button selector
never matched. Use button.mpl-per-{pid} instead.
Also switch from 8-digit hex alpha ({accent}22) to rgba() for broad
browser compatibility.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- _theme.py: fix pading→padding, text-weight→font-weight typos;
  per-btn now accent-colored text on transparent bg (no border/fill)
- legend.py: reorder to title → labels toggle → location → bbox
- README: add save section, update legend/grid_spines/colors descriptions,
  add save to available_sections() example output
- demo.ipynb: updated notebook content

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@symoon9
symoon9 merged commit ae53c54 into main Apr 27, 2026
5 checks passed
@symoon9
symoon9 deleted the claude/compassionate-mirzakhani-b058ff branch April 27, 2026 03:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant