Skip to content

Add native thread pinning with a dedicated "Pinned" sidebar section #2906

@tonisama

Description

@tonisama

Is your feature request related to a problem? Please describe.
Chainlit has no user-facing way to mark a thread as "save this one" or to surface specific threads above the time-based sidebar groups (Today / Yesterday / Previous 7 days / Previous 30 days). This matters in two cases:

(1) deployments with retention policies that auto-delete threads after N days need a way for users to flag threads that should be exempt, and;
(2) users who want to keep important chats (decisions, reference conversations, notes) visible without relying on recency-based ordering have no affordance to do so.

Describe the solution you'd like
Native thread pinning with:

  • A "Pin" / "Unpin" item in the existing per-thread ... dropdown (alongside Rename / Share / Delete).
  • A "Pinned" section at the top of the sidebar showing pinned threads above the time groups. Pinned threads should also still appear in their natural time bucket so recency-based navigation is unaffected.
  • Pinned state stored on ThreadDict.metadata.pinned (boolean), giving data layers a stable, queryable place to persist and filter on it, including for retention jobs.
  • A DataLayer.update_pinned_state(thread_id, pinned) method and matching REST endpoints (e.g. POST
    /project/thread/pin, DELETE /project/thread/pin).
  • No pin limit and no time-based restrictions (any thread can be pinned).

Describe alternatives you've considered

  • Client-side DOM injection via custom_js: I built a working prototype that registers custom FastAPI endpoints on Chainlit's app, uses a MutationObserver to inject a menu item into the Radix dropdown, and prepends a Pinned" section to #thread-history. It works but depends on private DOM selectors (id="thread-options", id="rename-thread", [data-radix-menu-content]) and on the aria-labelledby / data-state="open" relationships between Radix trigger and menu, none of which are stable across versions.
  • Slash command (e.g. /pin): simple to build, but has poor discoverability and doesn't address the sidebar-ordering requirement.
  • Separate "saved chats" view: higher friction than a pin toggle and pulls users out of their current flow.
  • Generic per-thread tags: more flexible, but there's no user-facing UX for tags today and retention filtering
    benefits from a dedicated flag rather than a free-form field.

Additional context

  • The cascade order for any retention cleanup script: stage files → ELEMENTS → FEEDBACK → STEPS → THREADS, is non-obvious. A helper such as DataLayer.delete_thread_cascade(thread_id) would pair well with this feature.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No fields configured for Feature.

    Projects

    Status

    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions