Skip to content

v0.2.1

Latest

Choose a tag to compare

@github-actions github-actions released this 02 Aug 20:42

Two things: a picker bug that made whole custom categories invisible, and this
site joining the 2plot network properly. Only the first affects
pip install dash-emoji-mart.

This is the first 0.2.x anyone can install. 0.2.0 below was written, dated
and never shipped — no v0.2.0 tag was ever pushed and nothing was uploaded, so
the newest thing pip install dash-emoji-mart has ever returned is still
0.0.3 from 2024. Read the two sections together: 0.2.0 is what changed in the
repository, 0.2.1 is what changed since, and a single v0.2.1 tag publishes
both at once.

Fixed — the component

  • Custom emojis whose src is a dimensionless SVG rendered as nothing.
    emoji-mart sizes a custom emoji's image with max-width/max-height and
    nothing else. A raster source has intrinsic dimensions and scales down to
    fit; an SVG carrying only a viewBox has no intrinsic size, so width: auto
    under a bare max-width resolves to zero. The emoji was in the DOM,
    focusable and clickable — and 0×0.

    It looked arbitrary rather than systematic, because it depended only on what
    the author happened to link. Iconify's API SVGs ship width="1em" and were
    always fine; SVGs straight off a repo were not. On the docs site's own
    custom-emoji demo that meant one category rendered and the next two were
    blank strips under their headings.

    The component now adopts a stylesheet into the picker's shadow root giving
    custom-emoji images width: 1em; height: 1em; object-fit: contain. 1em is
    the size emoji-mart already intends — it sets font-size to emojiSize on
    each grid button and to the larger preview size in the footer — so one rule
    is right in both places, non-square artwork is not distorted, and raster
    sources render exactly as before.

  • theme="auto" follows the APP, then the OS — in that order. emoji-mart's
    "auto" reads prefers-color-scheme and nothing else, which is the wrong
    signal in a Dash app: almost every one ships a theme toggle, and a toggle
    does not touch the OS. On a machine set to dark, flipping a Dash Mantine app
    to light left every picker dark against a white page — and it looked like the
    component ignoring its own theme prop.

    The component now prefers data-mantine-color-scheme on <html> when the
    document advertises one, and falls back to the media query otherwise. Reading
    an attribute adds no dependency on DMC: an app that does not set it behaves
    exactly as before. An explicit theme="light"/"dark" is still passed
    straight through, and the resolution re-runs on a toggle without a remount.

    This is why the docs site had the bug on six of its pages: only
    /theming wired the clientside callback its own prose recommends. That
    callback still works and is still the right answer for a non-Mantine toggle —
    it is just no longer needed for the common case.

  • dynamicWidth made the picker collapse instead of fill. emoji-mart
    implements the option by setting width: 100% on a <section> inside its
    shadow root and never touches the <em-emoji-picker> host. A custom element
    has no author width, so it sizes to its content — and its content is that
    section asking for 100% of the host. The constraint is circular and resolves
    at min-content: measured, turning the switch on took the picker from 532px to
    216px inside a 482px parent, and the grid reflowed under the pointer,
    which reads as the component glitching on hover.

    The component now sizes the host itself when dynamicWidth is on, and clears
    the width when it is off. It has to happen outside the shadow root: the host
    is in the light DOM, so no shadow stylesheet reaches it, and className /
    style apply to our wrapper rather than to it. Measured after: 352px → 482px
    (the full container), 9 → 13 emoji per row, and it reverts cleanly.

    The container still needs a width of its own for "100%" to mean anything —
    a shrink-to-fit flex item gives 100% of nothing — so
    docs/configuration/example.py now gives the mount flex: 1; minWidth: 0
    and says why.

Fixed — the documentation site

  • The popover example never opened. dmc.Popover toggles opened itself
    when its target is clicked, so the example's extra toggle callback read the
    already-flipped value as State and returned not True — closing the
    popover in the same round trip that opened it. The symptom was a trigger that
    appeared to do nothing. Down to one callback, and the page now documents the
    trap rather than demonstrating it.
  • Inline **bold** and *italic* put a <p> inside a <p>.
    markdown2dash renders paragraphs and inline emphasis with the same
    dmc.Text. lib/markdown_inline.py rebinds the three inline formatters to
    span=True. Five invalid nestings across the site, now none.

Added — a visual identity of its own

The site's mark was noto:grinning-face-with-smiling-eyes — the generic yellow
smiley, which is every emoji library's placeholder and named nothing about this
one. It is now 🤠 U+1F920, and the same drawing reaches every surface.

  • Header, favicon and share card are one glyph. assets/brand/cowboy-hat-face.png
    (Noto Emoji, Apache-2.0) is committed as the single source; the header renders
    noto:cowboy-hat-face through Iconify, so the tab icon, the navigation mark
    and an unfurl are the same drawing rather than three vendors' idea of it.
  • scripts/make_brand_assets.py derives the favicon set and the web app
    manifest from that source: favicon.ico (16/32/48), 192 and 512 PNGs, an
    apple-touch-icon flattened onto the dark surface because iOS backs a
    transparent icon with white, and site.webmanifest whose name, short name
    and theme colour are read from lib/constants.py rather than retyped.
    --check re-derives and compares, so a hand-edit or a stale run fails.
  • The manifest tests are no longer stubbed. 0.2.1 shipped this file with a
    block explaining that the donor's seven installable-app assertions had no
    surface to test here. They do now: the manifest is linked and served, names
    this site, is installable, every icon it declares resolves, the
    apple-touch-icon resolves and is opaque, and the theme colour agrees. Suite
    is 92 → 99.
  • The social card exists. 1200x630, the fleet layout — accent rule, brand,
    three-line tagline, mono domain — with the cowboy on the right, matching how
    email.2plot.dev frames its artwork. make_social_card.py now WARNS when a
    tagline overflows its three-line budget instead of silently slicing it; the
    first render of this card read "...custom image and SVG categories, and" and
    stopped mid-clause, which is precisely the kind of thing nobody catches on an
    asset they never see.
  • github_assets/ is gone. The 2.9 MB README demo GIF now comes from
    cdn.2plot.ai/github_assets/github-demo.gif, so cloning the repo no longer
    drags it along.

Changed — the 2plot network standard

Brings this host onto the standard proven on 2plot.ai, 2plot.dev, boilerplate,
leaflet, email, flexlayout and llms. Nothing here changes the package.

  • One brand, every surface. SITE_BRAND = "dash-emoji-mart — emoji picker for Dash" now reaches Dash(title=), the /llms.txt H1 and viewer chip
    (via dimll 2.3.4's resolve_site_title), the template's fallback <title>,
    the README and the home page's own heading. The package name leads, because
    for a component library the package is what a reader came to find; "Pip
    Install Python" is the byline and lives in the description.
  • One short app id. AD_APP_ID defaulted to dash-emoji-mart — the PyPI
    name — while the traffic reporter already said emojimart, so the same app
    would have reached the hub under two ids and its ad rows would never have
    lined up with its traffic rows. Everything now folds to lib.constants.APP_KEY.
  • The internal-traffic contract, both halves. Requests carrying
    2plot-internal are dropped at WRITE time, before bot classification, so the
    hub's health sweep and CI batteries cannot land in bot_hits; /healthz is
    never a visit. Outbound, the ad client and the traffic reporter now identify
    themselves — the ad fetch runs once per page view and was arriving at
    2plot.dev as python-requests, which its tracker counts as a bot, so this
    site's readers were inflating the hub's crawler numbers.
  • The social card. An OG block in lib/constants.py, image_url= and
    description= at every register_page, and the auxiliary tags Dash never
    emits in the template. One missing image_url= makes Dash emit
    content="", and an empty tag late in the document beats a good one earlier.
  • lib/bulletin.py, wired through NETWORK_BULLETIN_URL with a boot line
    that says which state the process is in — an unwired bulletin has no failure
    mode, only an announcement that never appears.
  • gunicorn>=23. The floor was >=21.2,<22, a ceiling inherited wholesale
    from markdown2dash's dependency block, holding this image on a gunicorn
    affected by CVE-2024-6827 and CVE-2024-1135. markdown2dash now installs
    --no-deps at every install site and CI asserts the resolved version inside
    the built image.
  • dash-improve-my-llms >=2.3.4, the floor that delivers resolve_site_title.
  • CI/CD. cd.yml owns main and calls ci.yml via uses: so merges stop
    running everything twice; actionlint runs first; a container job builds the
    real image, boots it, asserts dependency fingerprints inside it and runs the
    network battery against it; the deploy waits for five consecutive /healthz
    successes rather than one, because Render swaps instances and the old one
    answers throughout.

Measured, not fixed — three upstream findings

All verified on this host and recorded so the next pass does not rediscover
them.

  • Removing a category is one-way for the lifetime of the page. Setting
    maxFrequentRows to 0 drops the Frequently-used section and setting it
    back does not restore it; picking a few categories and then clearing the
    field does not restore "all of them" either. Same cause: init() builds
    Data.categories once and mutates that array in place, splice-ing out any
    category that ends up empty, and the only path that rebuilds it from
    Data.originalCategories is the one that runs when categories is passed.
    The state is a module global that outlives the component, every remount and
    every SPA navigation; a page reload is the only reset. Documented on the
    configuration page.

  • noCountryFlags and exceptEmojis filter the grid, not the search
    (emoji-mart 5.6.0). Both remove the emoji from category.emojis while
    SearchIndex.search matches over Object.values(Data.emojis) — the
    unfiltered map — with no category filter of its own. Measured: with
    noCountryFlags on, the flags category shrinks to the safe list and typing
    "united" still returns 🇬🇧 🇺🇸 🇦🇪 🇺🇳, identically to having it off.

    Not worked around, deliberately. emoji-mart loads its data into a
    module-global exactly once per page (if (!Data) Data = props.data), so
    pre-filtering the data for one picker would silently change every other
    picker on the page and every page after it in a Dash SPA — the same aliasing
    trap categories + custom already carries a red warning about. A visible
    search result beats an invisible, mount-order-dependent one. Documented on
    both props, in the README table, and on the configuration page; pinned by
    tests/test_component.py so a regeneration cannot quietly drop the caveat.

  • The prerender overwrites Dash's per-page <title> and appends its own
    og:title,
    both from the page's registered name. PAGE_TITLE_PREFIX
    therefore never reaches <title> or the scraper-winning og:title on any
    page of any satellite — they read "Picker in a Popover", not
    "dash-emoji-mart | Picker in a Popover". Registering the prefixed string as
    name would fix the tags and wreck the ## Pages index in /llms.txt,
    which lists that same value inside a document already headed by the brand.
    Left as-is and pinned by a test that fails when the behaviour changes.

  • og:image for crawler-classified UAs is still open (LESSONS §6), and the
    obvious fix is a trap.
    2.3.4's prerender does emit og:image when the
    page metadata carries an og_image key, and passing it measurably works —
    but Dash already emitted one, so the page ends up with two og:image tags
    and scripts/smoke_live.py requires exactly one. Duplicate og:image is how
    the fleet previously shipped an SVG card that beat a good one on tag order,
    so the rule is right and the per-site fix is wrong. Closing this needs the
    prerender to emit og:image only when the document does not already carry
    one. Separately, Slackbot and Twitterbot are served
    html_generator.py's hard-coded head, which has no og:image and no hook
    for one at all.

What's Changed

New Contributors

Full Changelog: https://github.com/pip-install-python/dash-emoji-mart/commits/v0.2.1