Skip to content

Add composer model selector#316

Open
aditya-damerla128 wants to merge 5 commits into
mieweb:mainfrom
aditya-damerla128:feat/composer-model-selector
Open

Add composer model selector#316
aditya-damerla128 wants to merge 5 commits into
mieweb:mainfrom
aditya-damerla128:feat/composer-model-selector

Conversation

@aditya-damerla128

@aditya-damerla128 aditya-damerla128 commented Jul 21, 2026

Copy link
Copy Markdown

Closes #317
Adds a reusable ComposerModelSelector primitive for compact provider/model selection in composer-style UIs.

What changed:

  • Added standalone ComposerModelSelector under AI components
image
  • Supports provider filtering, grouped model rows, providerLabel display labels, long-name truncation, and constrained boundary positioning
image
  • Adds Storybook examples for one provider, multiple providers, long names, and constrained containers

Scope:

  • Generic UI primitive only
  • No Ozwell widget code, backend calls, agent keys, or model fetching logic

Verification:

  • tsc --noEmit
  • focused eslint
  • Prettier
  • git diff --check

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new reusable ComposerModelSelector UI primitive under src/components/AI for selecting an AI provider/model pair in compact “composer” UIs, along with Storybook coverage.

Changes:

  • Added ComposerModelSelector component with provider filtering, grouped model rendering, and constrained/portaled dropdown positioning.
  • Exported the component and related types from the AI barrel.
  • Added Storybook stories covering single/multi-provider, long labels, and constrained container behavior.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/components/AI/index.ts Exports the new selector and its public types from the AI entrypoint.
src/components/AI/ComposerModelSelector.tsx Implements the portaled, filterable, grouped provider/model selector UI.
src/components/AI/ComposerModelSelector.stories.tsx Adds Storybook examples for common and edge display cases.

Comment thread src/components/AI/ComposerModelSelector.tsx
Comment thread src/components/AI/ComposerModelSelector.tsx
Copilot AI review requested due to automatic review settings July 23, 2026 14:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

Comment thread src/components/AI/ComposerModelSelector.tsx
Comment thread src/components/AI/ComposerModelSelector.tsx
Comment thread src/components/AI/ComposerModelSelector.tsx
Comment thread src/components/AI/ComposerModelSelector.tsx Outdated
Copilot AI review requested due to automatic review settings July 23, 2026 18:26
@aditya-damerla128
aditya-damerla128 force-pushed the feat/composer-model-selector branch from 2bab230 to 7abf153 Compare July 23, 2026 18:26
@aditya-damerla128
aditya-damerla128 force-pushed the feat/composer-model-selector branch from 7abf153 to 3717801 Compare July 23, 2026 18:28

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

src/components/AI/ComposerModelSelector.tsx:385

  • The listbox uses an aria-activedescendant pattern (focus stays on the listbox), but each option is a tabbable <button>. This makes Tab move into the options (and arrow-key handling on the listbox stops working), which breaks the expected listbox keyboard model. Make the options not tabbable and let the listbox be the single tab stop.
                          key={model.id ?? optionKey(model)}
                          id={getOptionId(index)}
                          type="button"
                          role="option"
                          aria-selected={selected}

Comment thread src/components/AI/ComposerModelSelector.tsx
Comment thread src/components/AI/ComposerModelSelector.tsx Outdated
Copilot AI review requested due to automatic review settings July 23, 2026 18:32

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

src/components/AI/ComposerModelSelector.tsx:386

  • The listbox uses aria-activedescendant with focus managed on the list container, but the option elements are plain <button>s that will take focus on mouse interaction and are also tabbable by default. This can break keyboard navigation/highlight behavior and is inconsistent with other listbox implementations in the repo (e.g. CodeLookup keeps focus on the list owner and makes options non-tabbable). Add tabIndex={-1} and prevent default on mouse down so focus remains on the listbox container.
                          type="button"
                          role="option"
                          aria-selected={selected}
                          onClick={() => selectModel(model)}

Comment thread src/components/AI/ComposerModelSelector.tsx
Comment thread src/components/AI/ComposerModelSelector.tsx Outdated
Comment thread src/components/AI/ComposerModelSelector.tsx
Copilot AI review requested due to automatic review settings July 23, 2026 18:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

src/components/AI/ComposerModelSelector.tsx:235

  • ComposerModelSelector introduces a fair amount of interaction logic (portal positioning, provider filtering, outside-click close, and Escape handling) but there are no automated tests covering these behaviors. Given there are existing Vitest/RTL tests in src/components/AI, adding a focused test file would help prevent regressions in keyboard/mouse interaction.
  React.useEffect(() => {
    if (!open) return;

    updateMenuPosition();
    window.addEventListener('resize', updateMenuPosition);
    window.addEventListener('scroll', updateMenuPosition, true);
    return () => {
      window.removeEventListener('resize', updateMenuPosition);
      window.removeEventListener('scroll', updateMenuPosition, true);
    };
  }, [open, updateMenuPosition]);

  React.useEffect(() => {
    if (!open) return;

    const handlePointerDown = (
      event: globalThis.MouseEvent | globalThis.TouchEvent
    ) => {
      const target = event.target as Node;
      if (
        triggerRef.current?.contains(target) ||
        menuRef.current?.contains(target)
      ) {
        return;
      }
      close();
    };

    document.addEventListener('mousedown', handlePointerDown);
    document.addEventListener('touchstart', handlePointerDown);
    return () => {
      document.removeEventListener('mousedown', handlePointerDown);
      document.removeEventListener('touchstart', handlePointerDown);
    };
  }, [close, open]);

  useEscapeKey(close, open);

Comment thread src/components/AI/ComposerModelSelector.tsx
Comment thread src/components/AI/ComposerModelSelector.tsx
Comment thread src/components/AI/ComposerModelSelector.tsx Outdated
Copilot AI review requested due to automatic review settings July 23, 2026 19:08

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

src/components/AI/ComposerModelSelector.tsx:408

  • The listbox options are rendered as <button role="option">, which creates nested interactive semantics inside a role="listbox" and leaves each option in the tab order. This can confuse assistive tech and keyboard users. Prefer non-interactive elements (div/li) with role="option" and keep keyboard interaction on the listbox container (via aria-activedescendant) or adopt the roving-tabindex pattern used in Select.
                        <button
                          key={model.id ?? optionKey(model)}
                          id={getOptionId(index)}
                          type="button"
                          role="option"

className
)}
>
<span className="max-w-40 min-w-0 truncate">
Comment on lines +28 to +31
type ControlledProviderFilterProps = {
providerFilter: string | 'any';
onProviderFilterChange: (provider: string | 'any') => void;
};
Comment on lines +124 to +127
const renderedModelIndexes = React.useMemo(
() =>
new Map(renderedModels.map((model, index) => [optionKey(model), index])),
[renderedModels]
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.

Add reusable composer model selector

2 participants