Skip to content

feat: smart composer experiment#6048

Open
rebelchris wants to merge 8 commits into
mainfrom
experiment-onestop-composer
Open

feat: smart composer experiment#6048
rebelchris wants to merge 8 commits into
mainfrom
experiment-onestop-composer

Conversation

@rebelchris
Copy link
Copy Markdown
Contributor

@rebelchris rebelchris commented May 13, 2026

Changes

Events

Did you introduce any new tracking events?

Experiment

Did you introduce any new experiments?

Manual Testing

Caution

Please make sure existing components are not breaking/affected by this PR

Preview domain

https://experiment-onestop-composer.preview.app.daily.dev

@vercel
Copy link
Copy Markdown

vercel Bot commented May 13, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
daily-webapp Ready Ready Preview May 15, 2026 1:07pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
storybook Ignored Ignored May 15, 2026 1:07pm

Request Review

@tsahimatsliah
Copy link
Copy Markdown
Member

Hey Chris 👋 — I went through the gap between this PR (experiment-onestop-composer) and the polished mock-up I prototyped on feat/smart-composer-experiment and turned it into a single action plan you can work through at your own pace. Nothing is pushed to the branch — this is a checklist, not code.

Each item has the same shape so it's easy to scan:

  • IDsurface
    • Why: what problem this solves / what effect it has.
    • Change: what to do, in plain English.

The reasoning sits above the action, so you can read each item top-to-bottom: "this is the problem → here's the fix". Tick what you agree with, push back on the rest — happy to pair on any of them.


1. Mode chip — rename + visual

The chip is the most important control in the composer. Today the labels are ambiguous and the chip blends into the chrome.

  • S1 — KindModePicker labels

    • Why: "Post" / "Link" + "mode" reads ambiguous; verb-first labels make the choice obvious at a glance.
    • Change: Rename the three options to Free form, Share a link, Poll, drop the trailing " mode" word, and widen the dropdown so the longer labels fit.
  • S2 — KindModePicker border + chevron

    • Why: Today the chip looks like grey chrome — the cabbage tint signals "this is the main switcher".
    • Change: Switch the chip border (and hover state) and the chevron icon to the brand cabbage colour.

2. Fullscreen modal — readable line length

When the composer is expanded it currently stretches edge-to-edge.

  • V1 — SmartComposerModal
    • Why: On wide displays the editor stretches past a comfortable reading width — paragraphs become hard to scan.
    • Change: When the modal is expanded, cap the inner form at ~928px wide and centre it horizontally; keep the modal chrome itself edge-to-edge.

3. Long-content scrolling + keyboard flow

Long titles and bodies were getting clipped or fighting the editor for scroll. The whole modal should scroll in exactly one place.

  • V16 — RichTextInput container

    • Why: Without this, the modal can't compute where the scroll should live.
    • Change: Allow the editor container to actually shrink inside the modal.
  • V17 — RichTextInput body

    • Why: Body scrolls, toolbar stays pinned to the bottom of the modal — one scroll axis only.
    • Change: Wrap the editor body in a single scroll container (only when the toolbar sits at the bottom — the composer case).
  • V18 — Markdown textarea

    • Why: Same one-scroll-axis reasoning, but for the markdown-mode editor.
    • Change: Make the markdown textarea grow with content (no inner scrollbar) and let the parent scroll wrapper handle overflow.
  • V23 — Editor body padding

    • Why: Text columns line up visually instead of jumping a few pixels left/right between sections.
    • Change: Match the editor's left/right padding to the title and cover above it.
  • V24 — Modal min-height

    • Why: The modal still has a sensible minimum size when content is short.
    • Change: Keep the existing min-height floor on the modal and let the new scroll containers handle the rest.
  • B1 — Title textarea auto-grow

    • Why: Long titles were being clipped at one line.
    • Change: Make the title textarea grow to fit its content (multi-line wrap).
  • B2 — Title Enter behaviour

    • Why: Matches every other modern composer; IME guard keeps CJK input working.
    • Change: Pressing Enter in the title moves the cursor into the editor body. Ignore Enter while an IME composition is in progress.
  • B3 — Title Cmd/Ctrl+Enter

    • Why: You can ship a quick post without leaving the title field.
    • Change: Submit the form when pressing Cmd/Ctrl+Enter from the title.
  • B4 — Editor Cmd/Ctrl+Enter

    • Why: Same shortcut from anywhere in the composer.
    • Change: Same submit shortcut works from inside the editor body.
  • B5 — Markdown toggle focus

    • Why: No "where did my cursor go?" moment after pressing the markdown button.
    • Change: After toggling markdown on/off, focus moves into the now-active editor on the next frame.
  • B6 — Markdown auto-height

    • Why: Keeps the single scroll axis whether you're in rich-text or markdown mode.
    • Change: Markdown textarea recomputes its height on every keystroke.

4. General UI polish

A long tail of small visual + behaviour gaps so the modal feels finished, not MVP.

4a. Spacing / layout

  • V2 — Spam-warning banner

    • Why: Visual parity with the experiment branch tint.
    • Change: Verify the spam banner contrast and spacing match the mock-up.
  • V3 — Bottom action strip

    • Why: The post button was sitting too close to the modal edge.
    • Change: Bump the bottom strip's vertical padding when in link / poll / markdown mode.
  • V4 — Link / Poll scroll column

    • Why: The first field was glued to the audience row; this gives it room to breathe.
    • Change: Use the same vertical rhythm (gap + padding) as the text variant.
  • V5 — Post button spacing

    • Why: Stops the CTA from kissing the toolbar buttons next to it.
    • Change: Add left margin and inner horizontal padding to the post button.

4b. Cover image

  • V6 — Cover wrapper

    • Why: So the image and remove button can react to hover together.
    • Change: Wrap the cover image in a hover-aware container.
  • V7 — Cover hover dim

    • Why: Tells the user the cover is interactive (you can replace or remove it).
    • Change: Slightly dim the cover image on hover.
  • V8 — Cover uploading state

    • Why: Visual feedback that the cover is in flight (state ready for the proactive upload — see "Heads up" below).
    • Change: Half-fade the cover while it's uploading.
  • V9 — Cover remove button

    • Why: The remove control stays visible and clearly clickable on top of the dim layer.
    • Change: Polish the X button: layered above the image's hover dim and inverts on hover.

4c. Link preview

  • V11 — Link preview wrapper

    • Why: Hosts the dismiss button without breaking the card layout.
    • Change: Wrap the preview and skeleton in a shared positioning block.
  • V12 — Link preview dismiss

    • Why: Lets the writer dismiss a fetched preview without retyping the URL.
    • Change: Add an X button on the preview, styled the same as the cover-remove button.
  • B10 — Dismissed-preview memory

    • Why: Dismissing only makes sense if it sticks while you're editing; pasting the same URL again is treated as "yes, show it now".
    • Change: Remember which URL was dismissed; re-show the preview if the writer pastes a different URL.

4d. Poll

  • V13 — Per-option counter

    • Why: Surfaces the 35-char limit before users hit it (Twitter-style).
    • Change: Show the remaining characters next to each poll option.
  • V14 — Poll option transition

    • Why: Smoother focus state, no jump.
    • Change: Add a colour transition on the option row's focus state.
  • V15 — Poll duration trigger

    • Why: The previous layout looked off-centre.
    • Change: Justify the duration dropdown so the label sits left and the chevron sits right.

4e. Toolbar

  • V19 — Bottom toolbar single-row

    • Why: Previously it would wrap onto two rows on narrow viewports — see V22 for what to do when it actually overflows.
    • Change: Force the bottom toolbar to stay on one row even when it overflows.
  • V20 — Toolbar dividers

    • Why: Consistent rhythm across all toolbar clusters.
    • Change: Use a consistent divider geometry between visual groups (formatting / list / history).
  • V21 — Toolbar buttons no-shrink

    • Why: Icons stay legible when the toolbar is tight.
    • Change: Stop formatting buttons from being squeezed when other content competes for space.
  • V22 — Overflow menu

    • Why: The bottom toolbar is busy (mode chip, attach buttons, formatting, undo/redo, post button); overflow keeps narrow viewports usable.
    • Change: When the bottom toolbar runs out of room, collapse the lowest-priority formatting buttons into a "More formatting" dropdown. Top-position toolbars keep their existing wrap behaviour.
  • B18 — Toolbar focus preservation

    • Why: Selection-dependent commands (Bold on a selection, etc.) were breaking because the editor blurred on mousedown.
    • Change: Prevent the editor from blurring when a toolbar button is clicked, and add the missing tooltip on the inline link button.

4f. Audience chip

  • V25 — Hide chevron when nothing to pick

    • Why: If the user has no squads, the chip looks like a label instead of a teaser dropdown.
    • Change: Only show the dropdown chevron when there's more than one possible audience.
  • B12 — First-squad-from-default

    • Why: Otherwise the very first squad click silently turns into a multi-audience post — not what people expect on a single click.
    • Change: When the user picks their first squad while only "Everyone" is selected, uncheck "Everyone" automatically; subsequent picks add alongside.

4g. Post button

  • B16 — Loading + disabled composition

    • Why: Keeps the button accurate when the proactive-upload flag flips on.
    • Change: Treat the cover-uploading state as part of "loading" and "disabled" on the post button.
  • B17 — Submit shortcut tooltip

    • Why: Surfaces the keyboard shortcut so users discover B3/B4 without docs.
    • Change: Wrap the post button in a tooltip showing "Press ⌘ + Enter to post" (Ctrl on non-Apple).

5. Modal behaviour, integration, telemetry

The composer needs to behave like a primary entry point, not a side modal.

  • B19 — Outside-click is non-destructive

    • Why: The composer holds a lot of work-in-progress; one stray click outside shouldn't nuke it.
    • Change: Disable close-on-overlay-click.
  • B20 — Drawer on mobile

    • Why: Below laptop the composer is full-screen anyway; drawer mode gives us safe-area handling, swipe-to-close, and the right shadow stack.
    • Change: Render the modal as a drawer below laptop.
  • B22 — Global "c" hotkey

    • Why: Parity with the rest of the app's keyboard shortcuts and with the `c` shortcut I prototyped.
    • Change: New `SmartComposerHotkey` mounted in `MainLayout` opens the composer when the user presses `c`. Gated on logged-in + laptop + flag enabled + not currently typing in an input.
  • B24 — SharePostBar entry

    • Why: Single entry point for new posts when the experiment is on.
    • Change: When the flag is on and we're on laptop, fetched link previews open the smart composer instead of `CreateSharedPost`.
  • B25 — CreatePostButton gate

    • Why: Mobile / tablet keep the dedicated `/posts/new` page; the smart composer isn't ready for small viewports yet.
    • Change: Only route through the smart composer when on laptop.
  • B28 — Telemetry funnel

    • Why: We need funnel data to decide whether the experiment is winning.
    • Change: Add `LogEvent`s for open / close / submit / switch-kind / toggle-markdown / toggle-expand / add-cover / remove-cover / dismiss-preview.

Heads up — needs a decision, not an action

These four didn't make the bullet list above because they're judgement calls rather than work to do.

  • B7 — Proactive cover upload

    • Status: State (`isUploadingCover`) is plumbed all the way through (V8 fade, B16 button gating) but the actual upload still happens at submit time.
    • Decision needed: Either wire the upload to the CDN now and flip the flag, or drop the state.
  • Cover position

    • Status: The polish version moves the cover from `Title → Cover → Body` to `Title → Body → Cover` so it scrolls with the body (one scroll axis).
    • Decision needed: Quick gut-check with design before committing to the new order.
  • V26 / B21 — "Open in full editor" inline banner

    • Status: Intentionally dropped from the plan.
    • Decision needed: Flagging here in case you'd like it back.
  • Drive-by — `SharePostBar` strict-typing nits

    • Status: Pre-existing strict-mode type errors in `SharePostBar` (`inputRef` typing, `user` possibly undefined) showed up while integrating B24.
    • Decision needed: Non-functional, but worth a quick fix in this PR or a separate one — your call.

For reference, the polished mock-up branch is feat/smart-composer-experiment (PR #5967) — feel free to cherry-pick or copy-paste from there if it helps. Ping me if anything is unclear 🙏

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.

2 participants