bb.thread-ui

The bb.thread-ui plugin owns thread presentation and consumes the headless bb.threads service.

Purpose

bb.thread-ui presents thread lists, thread views, composers, timeline items, and thread controls.

The plugin keeps presentation contracts separate from thread records and execution. It requires the bb.threads service for all thread data.

Surfaces

ID kind replaceable props contract sketch notes
bb.thread-ui.list single yes ThreadListProps Owns the thread list. The winner receives Original.
bb.thread-ui.view single yes ThreadViewProps Owns one open thread. The winner receives Original.
bb.thread-ui.composer single yes ComposerProps Owns each thread composer. The winner receives Original.
bb.thread-ui.composer.actions list no ComposerActionContribution Adds ordered controls and cards.
bb.thread-ui.composer.send chain no ComposerSendMiddleware Wraps one send operation.
bb.thread-ui.composer.mentionPopover single yes ComposerMentionPopoverProps The default bb.thread-ui.composer implementation declares this child surface. The plugin root does not declare it.
bb.thread-ui.timeline.item keyed yes TimelineItemProps Selects one component for each timeline item type.
bb.thread-ui.sidePanels list no ThreadSidePanelContribution The default bb.thread-ui.view implementation declares this child surface. The plugin root does not declare it.

All surface declarations use version 1.0. The first-party plugin supplies each default implementation.

The first-party plugin claims bb.layout.main. Its active main implementation declares the root thread UI surfaces.

Hierarchical surface property

The plugin root declares the main presentation surfaces. Default implementations declare their presentation-only children.

bb.layout.main
└─ bb.thread-ui main implementation declares
   ├─ bb.thread-ui.list
   ├─ bb.thread-ui.view
   │  └─ default view declares bb.thread-ui.sidePanels
   ├─ bb.thread-ui.composer
   │  └─ default composer declares bb.thread-ui.composer.mentionPopover
   ├─ bb.thread-ui.composer.actions
   ├─ bb.thread-ui.composer.send
   └─ bb.thread-ui.timeline.item

The kernel activates each child surface only while its active parent implementation declares that child.

A replacement view can declare the same child contract. It can also omit the child contract.

If a replacement omits a child, claims on that child become inactive. The domain services continue without presentation assumptions.

This example shows a required property of the hierarchical surface graph. A child surface follows the active parent implementation.

Shared surface types

The UI contracts import all durable data types from the headless plugin.

import type { ComponentType, ReactNode } from "react";
import type {
  ComposerDraft,
  ComposerScope,
  ExecutionSelection,
  JsonValue,
  ProjectId,
  PromptInput,
  ThreadId,
  ThreadRecord,
  ThreadSummary,
  TimelineItem,
  TimelineItemId,
} from "@bb/threads/contracts";
import type {
  MentionSearchGroup,
  MentionSearchItem,
  MentionTrigger,
} from "@bb/mentions/contracts";

export interface ThreadAction {
  id: string;
  title: string;
  icon?: string;
  when?(thread: ThreadSummary): boolean;
  component?: ComponentType<{ thread: ThreadSummary; compact: boolean }>;
  run?(thread: ThreadSummary): void | Promise<void>;
}

A thread action supplies either component or run. The contract build rejects an incomplete action.

bb.thread-ui.list

The list receives thread state and typed actions. It does not store thread records.

export interface ThreadListProps {
  activeThreadId: ThreadId | null;
  activeProjectId: ProjectId | null;
  compact: boolean;
  searchQuery: string;
  state:
    | { status: "loading"; threads: readonly [] }
    | { status: "ready"; threads: readonly ThreadSummary[] }
    | { status: "error"; threads: readonly ThreadSummary[]; error: Error };
  actions: {
    open(threadId: ThreadId, options?: { split?: boolean }): void;
    create(options?: { projectId?: ProjectId; focusPrompt?: boolean }): void;
    setPinned(threadId: ThreadId, pinned: boolean): Promise<void>;
    setRead(threadId: ThreadId, read: boolean): Promise<void>;
    rename(threadId: ThreadId, title: string): Promise<void>;
    archive(threadId: ThreadId): Promise<void>;
    requestDelete(threadId: ThreadId): void;
    onNavigate(): void;
  };
}

export type ThreadListComponent = ComponentType<ThreadListProps>;

The kernel gives Original to the active claimant. A claimant can wrap the default list.

bb.thread-ui.view

The view owns the thread header and the ThreadChat composition.

export interface ThreadViewProps {
  threadId: ThreadId;
  projectId: ProjectId;
  thread: ThreadRecord;
  compact: boolean;
  focusRequest?: number;
  leadingContent?: ReactNode;
  headerActions?: readonly ThreadAction[];
  messageActions?: readonly MessageAction[];
  onOpenPanel?(panelId: string, params?: JsonValue): boolean;
}

export interface MessageReference {
  id: string;
  threadId: ThreadId;
  turnId: string | null;
  role: "user" | "assistant";
  text: string;
  sourceSeqEnd: number;
}

export interface MessageAction {
  id: string;
  title: string;
  icon?: string;
  roles?: readonly ("user" | "assistant")[];
  run(message: MessageReference): void | Promise<void>;
}

The old header and message slots use this wrapper contract. A plugin wraps Original and supplies typed actions.

bb.thread-ui.composer

The composer reads and writes bb.threads.drafts. A winner change does not remove a stored draft.

export interface ComposerProps {
  composerId: string;
  scope: ComposerScope;
  layout: "regular" | "compact" | "zen";
  placeholder?: string;
  focusRequest?: number;
  draft: ComposerDraft;
  selection: ExecutionSelection;
  runState: "idle" | "submitting" | "active" | "stopping";
  submitMode: "ready" | "queue" | "blocked";
  disabledReason: { code: string; reason: string } | null;
  setDraft(next: ComposerDraft): Promise<void>;
  setSelection(next: ExecutionSelection): Promise<void>;
  send(intent?: ComposerSendIntent): Promise<ComposerSendResult>;
}

export interface ComposerSendIntent {
  verb?: "create" | "send" | "steer" | "queue" | "send-queue-head";
  clearDraftOnSuccess?: boolean;
}

export interface ComposerSendResult {
  status: "sent" | "queued" | "blocked" | "cancelled";
  threadId: ThreadId | null;
  reason?: string;
}

export interface ComposerHandle {
  getDraft(): ComposerDraft;
  setText(text: string): void;
  updateText(update: (text: string) => string): void;
  clear(options?: { attachments?: boolean }): void;
  addQuote(text: string): void;
  insertMention(mention: { provider: string; id: string; label: string }): void;
  setInputLock(owner: string, locked: boolean): void;
  setTextEffect(owner: string, effect: { className: string } | null): void;
  focus(): void;
}

The component handle controls local UI behavior. The headless service stores the draft and selection.

bb.thread-ui.composer.mentionPopover

The default composer declares this child surface. It calls the optional bb.mentions service and passes presentation state to the popover.

export interface ComposerMentionPopoverProps {
  composerId: string;
  scope: ComposerScope;
  trigger: MentionTrigger;
  query: string;
  anchor: {
    left: number;
    top: number;
    width: number;
    height: number;
  };
  state:
    | { status: "loading"; groups: readonly [] }
    | { status: "ready"; groups: readonly MentionSearchGroup[] }
    | { status: "error"; groups: readonly []; error: Error };
  activeItemId: string | null;
  setActiveItem(itemId: string | null): void;
  select(input: {
    providerId: string;
    item: MentionSearchItem;
  }): void;
  dismiss(): void;
}

The popover renders rows and owns selection and menu chrome. It does not search or resolve mentions.

A replacement composer can declare this child contract. It can also omit the child contract.

If the replacement omits it, popover claims become inactive. The bb.mentions service remains available to other consumers.

bb.thread-ui.composer.actions

This list replaces the old composer customization bundle. Each contribution has one stable ID and one placement.

export type ComposerActionContribution =
  | {
      id: string;
      order?: number;
      placement: "action-row";
      title: string;
      icon?: string;
      when?(context: ComposerActionContext): boolean;
      component?: ComponentType<ComposerActionContext>;
      run?(context: ComposerActionContext): void | Promise<void>;
    }
  | {
      id: string;
      order?: number;
      placement: "plus-menu";
      title: string;
      description?: string;
      icon?: string;
      disabled?(context: ComposerActionContext): boolean;
      run(context: ComposerActionContext): void | Promise<void>;
    }
  | {
      id: string;
      order?: number;
      placement: "stack";
      chrome?: "card" | "bare";
      showDuringInteraction?: boolean;
      component: ComponentType<ComposerActionContext>;
    };

export interface ComposerActionContext {
  composerId: string;
  scope: ComposerScope;
  draft: ComposerDraft;
  selection: ExecutionSelection;
  runState: ComposerProps["runState"];
  composer: ComposerHandle;
}

Rich-text effects use ComposerHandle.setTextEffect(). A plugin does not need a separate rich-text surface.

bb.thread-ui.composer.send

This chain wraps the final send. A wrapper can change the request, block it, or call the next wrapper.

export interface ComposerSendContext {
  composerId: string;
  scope: ComposerScope;
  draft: ComposerDraft;
  selection: ExecutionSelection;
  intent: Required<ComposerSendIntent>;
  signal: AbortSignal;
}

export interface ComposerSendBlock {
  status: "blocked";
  code: string;
  reason: string;
}

export type ComposerSendNext = (
  context?: ComposerSendContext,
) => Promise<ComposerSendResult | ComposerSendBlock>;

export interface ComposerSendMiddleware {
  id: string;
  order?: number;
  when?(context: ComposerSendContext): boolean;
  run(
    context: ComposerSendContext,
    next: ComposerSendNext,
  ): Promise<ComposerSendResult | ComposerSendBlock>;
}

The host freezes one chain for each send. A wrapper calls next() at most once.

bb.thread-ui.timeline.item

Each data item has one canonical type. The keyed winner store selects one component for each item type.

export interface TimelinePresentation {
  label: { pending: string; completed: string };
  icon: { glyph: string };
  title?: string;
  detail?: string;
  suppress?: boolean;
  tint?: { light: string; dark: string };
}

export interface TimelineItemProps {
  item: TimelineItem;
  thread: ThreadRecord;
  providerId: string | null;
  presentation: TimelinePresentation;
  view: {
    depth: number;
    dimmed: boolean;
    inClosedTurn: boolean;
    isFrontier: boolean;
  };
  expansion: {
    expanded: boolean;
    expandable: boolean;
    forced: boolean;
    toggle(): void;
  };
  children: ReactNode;
  actions: readonly MessageAction[];
  openPanel(panelId: string, params?: JsonValue): boolean;
}

The TimelineItem data type contains no presentation member. The selected UI implementation supplies TimelinePresentation.

The key uses the exact item.type. A plugin owns an extension type with <ownerId>/<name>.

The first-party plugin owns all core keys. It also owns * as the default for an unknown extension type.

bb.thread-ui.sidePanels

The default view declares this child list. Each claim supplies a launcher and a body.

export interface ThreadSidePanelContribution {
  id: string;
  title: string;
  icon?: string;
  order?: number;
  placement: "thread" | "new-thread" | "both";
  layout?: "padded" | "flush";
  when?(context: ThreadSidePanelContext): boolean;
  run?(context: ThreadSidePanelContext): void | Promise<void>;
  component: ComponentType<ThreadSidePanelProps>;
}

export type ThreadSidePanelContext =
  | { kind: "thread"; threadId: ThreadId; projectId: ProjectId }
  | { kind: "new-thread"; projectId: ProjectId | null };

export interface ThreadSidePanelProps {
  context: ThreadSidePanelContext;
  params: JsonValue | null;
  close(): void;
  setTitle(title: string): void;
  setParams(params: JsonValue | null): void;
}

The layout plugin supplies the panel frame. The default thread view supplies the child declaration and its placement contract.

Services

bb.thread-ui declares no data service. It consumes the thread service and can consume the mention service.

service edge range use
bb.threads required ^1 Reads records, timeline data, drafts, selection, events, and execution state.
bb.mentions optional ^1 Searches mention providers and resolves selected mentions for the default composer.

The kernel starts bb.thread-ui after it resolves bb.threads. A thread service winner change restarts bb.thread-ui with a fresh handle.

The required edge keeps the UI independent from the first-party data implementation. A replacement service can keep the same contract.

The optional mention edge keeps the composer usable without bb.mentions. Without it, trigger characters remain plain text.

Exports

The plugin exports exact first-party components for authors who do not want winner selection.

export interface ThreadChatProps {
  threadId: ThreadId;
  variant?: "full" | "compact" | "timeline";
  layout?: "contained" | "document";
  focusRequest?: number;
  permissionPolicy?: "inherit" | "editable";
  className?: string;
  leadingContent?: ReactNode;
  messageActions?: readonly MessageAction[];
  showComposer?: boolean;
}

export interface NewThreadComposerProps {
  projectId: ProjectId | null;
  defaults?: Partial<ExecutionSelection>;
  initialPrompt?: string;
  placeholder?: string;
  layout?: ComposerProps["layout"];
  focusRequest?: number;
  className?: string;
  draftKey?: string;
  onSubmit(request: {
    projectId: ProjectId;
    selection: ExecutionSelection;
    input: readonly PromptInput[];
    environment?: JsonValue;
  }): void | Promise<void>;
}

export interface MarkdownProps {
  content: string;
  className?: string;
}

export interface TimelineProps {
  threadId: ThreadId;
  items: readonly TimelineItem[];
  frontier: number;
  variant?: "full" | "compact";
  searchItemId?: TimelineItemId | null;
}
// @bb/thread-ui/components
export { ThreadChat } from "./ThreadChat";
export { ThreadList } from "./ThreadList";
export { ThreadView } from "./ThreadView";
export { Composer, NewThreadComposer } from "./Composer";
export { MentionPopover } from "./MentionPopover";
export { Markdown } from "./Markdown";
export { Timeline, TimelineItemFrame } from "./Timeline";
export { MessageItem, ToolItem, InteractionItem, NoticeItem } from "./timeline-items";
// @bb/thread-ui/app
export function useComposer(): ComposerHandle;
export function useComposerView(): ComposerProps;
export function useThreadList(): ThreadListProps;
// @bb/thread-ui/interactions
export {
  ASK_USER_QUESTION_RENDERER_ID,
  SECRET_REQUEST_RENDERER_ID,
  interactionPayloadSchema,
  interactionResponseSchema,
  secretRequestPayloadSchema,
  secretRequestResponseSchema,
};
export type {
  InteractionOption,
  InteractionQuestion,
  InteractionPayload,
  InteractionAnswer,
  InteractionResponse,
  SecretRequestPayload,
  SecretRequestResponse,
};

Module imports select this exact implementation. Surface contracts follow the user's current winner.

Host roles

bb.thread-ui declares no host role. It uses domain services through declared edges.

Example

This plugin adds a review panel, a composer action, a send check, and a timeline item.

// bb.plugin.jsonc
{
  "id": "acme.review",
  "version": "2.0.0",
  "claims": [
    { "surface": "bb.thread-ui.sidePanels" },
    { "surface": "bb.thread-ui.composer.actions" },
    { "surface": "bb.thread-ui.composer.send" },
    { "surface": "bb.thread-ui.timeline.item", "key": "acme.review/review" }
  ],
  "requires": [{ "service": "bb.threads", "range": "^1" }],
  "artifacts": {
    "app": "./dist/app.js",
    "server": "./dist/server.js"
  }
}
// src/app.tsx
import { definePlugin } from "@get-bb/plugin/app";
import { ReviewItem, ReviewPanel } from "./components";

export default definePlugin((api) => {
  api.surfaces.provide("bb.thread-ui.sidePanels", {
    id: "acme.review/panel",
    title: "Review",
    placement: "thread",
    component: ReviewPanel,
  });

  api.surfaces.provide("bb.thread-ui.composer.actions", {
    id: "acme.review/request",
    placement: "plus-menu",
    title: "Request review",
    run: ({ composer }) => composer.setText("Please review this change. "),
  });

  api.surfaces.provide("bb.thread-ui.composer.send", {
    id: "acme.review/require-summary",
    order: 550,
    when: ({ draft }) => draft.text.startsWith("/review"),
    async run(context, next) {
      if (context.draft.text.trim() === "/review") {
        return { status: "blocked", code: "acme.review/summary", reason: "Add a review summary." };
      }
      return next();
    },
  });

  api.surfaces.provide("bb.thread-ui.timeline.item", {
    key: "acme.review/review",
    component: ReviewItem,
  });
});

The side-panel claim works while the active view declares bb.thread-ui.sidePanels.

A replacement view can omit that child. The other three claims continue because the plugin root declares their surfaces.

Covers

old item ID new contract/verb note
app.components.ThreadChat @bb/thread-ui/components: ThreadChat The exact component remains available as a module export.
app.components.Markdown @bb/thread-ui/components: Markdown The chat Markdown component remains available as a module export.
app.components.experimental_NewThreadComposer @bb/thread-ui/components: NewThreadComposer The stable export removes the experimental prefix.
app.hooks.useComposer @bb/thread-ui/app: useComposer() The hook returns the shared composer handle.
app.hooks.useComposerView @bb/thread-ui/app: useComposerView() The hook reads the active composer props.
app.hooks.experimental_useSidebarThreads bb.thread-ui.list surface: ThreadListProps.state The surface receives the typed list state.
app.hooks.experimental_useSidebarThreadActions bb.thread-ui.list surface: ThreadListProps.actions The surface receives the typed list actions.
app.hooks.experimental_useSidebarThreadPullRequest bb.thread-ui.list surface: ThreadSummary and view wrappers A list winner can add thread status data through its own service.
app.hooks.experimental_useSidebarThreadSplit bb.thread-ui.list surface: ThreadListProps.actions.open() The open action selects current or split placement.
app.slots.threadPanelAction bb.thread-ui.sidePanels surface A list contribution provides the thread launcher and body.
app.slots.experimental_newThreadPanelAction bb.thread-ui.sidePanels surface The placement field selects the new-thread context.
app.slots.pendingInteraction bb.thread-ui.timeline.item surface and bb.threads interactions service The keyed item component shows pending and final states.
app.slots.experimental_threadList bb.thread-ui.list surface The single surface uses the global winner store.
app.slots.experimental_threadHeaderAction bb.thread-ui.view surface: ThreadViewProps.headerActions A view winner can wrap Original and add typed actions.
app.slots.messageDirective bb.thread-ui.timeline.item surface A plugin-owned directive becomes a plugin-owned item type.
app.slots.messageAction bb.thread-ui.view surface: ThreadViewProps.messageActions A view wrapper or ThreadChat instance adds typed actions.
app.slots.experimental_timelineRenderer bb.thread-ui.timeline.item surface The keyed surface selects one winner for each item type.
app.composer bb.thread-ui.composer.actions surface and ComposerHandle Separate contracts replace the customization bundle.
app.composer.customize bb.thread-ui.composer.actions surface Each old bundle entry becomes one ordered contribution.
app.contracts.PluginThreadPanelProps bb.thread-ui.sidePanels surface: ThreadSidePanelContribution and ThreadSidePanelProps One list contract replaces panel action and panel body records.
app.contracts.PluginNewThreadPanelProps bb.thread-ui.sidePanels surface: ThreadSidePanelContribution and ThreadSidePanelProps One list contract replaces panel action and panel body records.
app.contracts.PluginPendingInteractionView bb.thread-ui.timeline.item surface: InteractionItemProps The keyed timeline component displays the interaction.
app.contracts.PluginPendingInteractionProps bb.thread-ui.timeline.item surface: InteractionItemProps The keyed timeline component displays the interaction.
app.contracts.PluginThreadPanelActionRegistration bb.thread-ui.sidePanels surface: ThreadSidePanelContribution and ThreadSidePanelProps One list contract replaces panel action and panel body records.
app.contracts.PluginThreadPanelActionContext.openPanel bb.thread-ui.sidePanels surface: ThreadSidePanelContribution and ThreadSidePanelProps One list contract replaces panel action and panel body records.
app.contracts.PluginNewThreadPanelActionRegistration bb.thread-ui.sidePanels surface: ThreadSidePanelContribution and ThreadSidePanelProps One list contract replaces panel action and panel body records.
app.contracts.PluginNewThreadPanelActionContext.openPanel bb.thread-ui.sidePanels surface: ThreadSidePanelContribution and ThreadSidePanelProps One list contract replaces panel action and panel body records.
app.contracts.PluginPendingInteractionRegistration bb.thread-ui.timeline.item surface: InteractionItemProps The keyed timeline component displays the interaction.
app.contracts.PluginThreadListRegistration bb.thread-ui.list surface: ThreadListProps and ThreadSummary The list surface receives data and actions through typed props.
app.contracts.PluginMessageDirectiveRegistration bb.thread-ui.timeline.item surface: plugin-owned directive item type The timeline item contract carries message and thread identity.
app.contracts.PluginMessageActionRegistration bb.thread-ui.view surface: MessageAction A view wrapper or ThreadChat instance supplies the action.
app.contracts.PluginMessageActionContext.openPanel bb.thread-ui.view surface: MessageAction A view wrapper or ThreadChat instance supplies the action.
app.contracts.PluginTimelineRendererRegistration bb.thread-ui.timeline.item surface: TimelineItem and TimelineItemProps The keyed contract uses the canonical item type.
app.contracts.PluginTimelineRendererProps bb.thread-ui.timeline.item surface: TimelineItem and TimelineItemProps The keyed contract uses the canonical item type.
app.contracts.PluginThreadListProps bb.thread-ui.list surface: ThreadListProps and ThreadSummary The list surface receives data and actions through typed props.
app.contracts.PluginMessageDirectiveProps bb.thread-ui.timeline.item surface: plugin-owned directive item type The timeline item contract carries message and thread identity.
app.contracts.PluginMessageDirectiveMessage bb.thread-ui.timeline.item surface: plugin-owned directive item type The timeline item contract carries message and thread identity.
app.contracts.ThreadChatMessageReference @bb/thread-ui/components: ThreadChat and its props The exact component export keeps instance actions.
app.contracts.PluginTargetedPanelActionOpenOptions bb.thread-ui.sidePanels surface: ThreadSidePanelContribution and ThreadSidePanelProps One list contract replaces panel action and panel body records.
app.contracts.PluginSidebarThreadsState bb.thread-ui.list surface: ThreadListProps and ThreadSummary The list surface receives data and actions through typed props.
app.contracts.PluginSidebarThread bb.thread-ui.list surface: ThreadListProps and ThreadSummary The list surface receives data and actions through typed props.
app.contracts.PluginSidebarThreadActions bb.thread-ui.list surface: ThreadListProps and ThreadSummary The list surface receives data and actions through typed props.
app.contracts.PluginSidebarThreadActions.open bb.thread-ui.list surface: ThreadListProps and ThreadSummary The list surface receives data and actions through typed props.
app.contracts.PluginSidebarThreadActions.openNewThread bb.thread-ui.list surface: ThreadListProps and ThreadSummary The list surface receives data and actions through typed props.
app.contracts.PluginSidebarThreadActions.setPinned bb.thread-ui.list surface: ThreadListProps and ThreadSummary The list surface receives data and actions through typed props.
app.contracts.PluginSidebarThreadActions.setRead bb.thread-ui.list surface: ThreadListProps and ThreadSummary The list surface receives data and actions through typed props.
app.contracts.PluginSidebarThreadActions.rename bb.thread-ui.list surface: ThreadListProps and ThreadSummary The list surface receives data and actions through typed props.
app.contracts.PluginSidebarThreadActions.archive bb.thread-ui.list surface: ThreadListProps and ThreadSummary The list surface receives data and actions through typed props.
app.contracts.PluginSidebarThreadActions.requestDelete bb.thread-ui.list surface: ThreadListProps and ThreadSummary The list surface receives data and actions through typed props.
app.contracts.PluginSidebarThreadPullRequestState bb.thread-ui.list surface: ThreadListProps and ThreadSummary The list surface receives data and actions through typed props.
app.contracts.PluginSidebarPullRequest bb.thread-ui.list surface: ThreadListProps and ThreadSummary The list surface receives data and actions through typed props.
app.contracts.PluginSidebarSplit bb.thread-ui.list surface: ThreadListProps and ThreadSummary The list surface receives data and actions through typed props.
app.contracts.PluginTimelineRowPresentation bb.thread-ui.timeline.item surface: TimelineItem and TimelineItemProps The keyed contract uses the canonical item type.
app.contracts.PluginTimelineRendererRow bb.thread-ui.timeline.item surface: TimelineItem and TimelineItemProps The keyed contract uses the canonical item type.
app.contracts.PluginTimelineRowStatus bb.thread-ui.timeline.item surface: TimelineItem and TimelineItemProps The keyed contract uses the canonical item type.
app.composer.ComposerCustomization bb.thread-ui.composer.actions surface: ComposerActionContribution Each action, menu row, or card becomes one contribution.
app.composer.ComposerPlusMenuItem bb.thread-ui.composer.actions surface: plus-menu contribution The list contribution keeps the action behavior.
app.composer.ComposerPlusMenuItem.run bb.thread-ui.composer.actions surface: plus-menu contribution The list contribution keeps the action behavior.
app.composer.ComposerView bb.thread-ui.composer surface: ComposerProps The surface props expose reactive composer state.
app.composer.ComposerRichTextSpec @bb/thread-ui/components: ComposerHandle.setTextEffect() The composer handle owns text effects.
app.composer.PluginComposerApi @bb/thread-ui/components: ComposerHandle The shared handle keeps imperative composer operations.
app.composer.PluginComposerApi.setText @bb/thread-ui/components: ComposerHandle.setText The shared handle keeps imperative composer operations.
app.composer.PluginComposerApi.updateText @bb/thread-ui/components: ComposerHandle.updateText The shared handle keeps imperative composer operations.
app.composer.PluginComposerApi.clear @bb/thread-ui/components: ComposerHandle.clear The shared handle keeps imperative composer operations.
app.composer.PluginComposerApi.setTextEffect @bb/thread-ui/components: ComposerHandle.setTextEffect The shared handle keeps imperative composer operations.
app.composer.PluginComposerApi.setInputLock @bb/thread-ui/components: ComposerHandle.setInputLock The shared handle keeps imperative composer operations.
app.composer.PluginComposerApi.addQuote @bb/thread-ui/components: ComposerHandle.addQuote The shared handle keeps imperative composer operations.
app.composer.PluginComposerApi.insertMention @bb/thread-ui/components: ComposerHandle.insertMention The shared handle keeps imperative composer operations.
app.composer.PluginComposerApi.focus @bb/thread-ui/components: ComposerHandle.focus The shared handle keeps imperative composer operations.
app.contracts.ThreadChatProps @bb/thread-ui/components: ThreadChat and its props The exact component export keeps instance actions.
app.contracts.ThreadChatMessageAction @bb/thread-ui/components: ThreadChat and its props The exact component export keeps instance actions.
app.contracts.NewThreadRequest @bb/thread-ui/components: NewThreadComposer and ComposerProps The component uses the shared draft and selection contracts.
app.contracts.NewThreadComposerProps @bb/thread-ui/components: NewThreadComposer and ComposerProps The component uses the shared draft and selection contracts.
app.contracts.NewThreadComposerProps.onSubmit @bb/thread-ui/components: NewThreadComposer and ComposerProps The component uses the shared draft and selection contracts.
app.contracts.MarkdownProps @bb/thread-ui/components: Markdown and MarkdownProps The chat Markdown component remains an exact export.
app.composer.PluginComposerTextEffect @bb/thread-ui/components: ComposerHandle.setTextEffect()
app.interaction-contracts @bb/thread-ui/interactions exports Shared schemas remain available to native clients.
app.interactions.ASK_USER_QUESTION_RENDERER_ID @bb/thread-ui/interactions: ASK_USER_QUESTION_RENDERER_ID The interaction export keeps the shared wire shape.
app.interactions.SECRET_REQUEST_RENDERER_ID @bb/thread-ui/interactions: SECRET_REQUEST_RENDERER_ID The interaction export keeps the shared wire shape.
app.interactions.MAX_QUESTIONS @bb/thread-ui/interactions: MAX_QUESTIONS The interaction export keeps the shared wire shape.
app.interactions.MAX_OPTIONS @bb/thread-ui/interactions: MAX_OPTIONS The interaction export keeps the shared wire shape.
app.interactions.MAX_OPTION_PREVIEW_LENGTH @bb/thread-ui/interactions: MAX_OPTION_PREVIEW_LENGTH The interaction export keeps the shared wire shape.
app.interactions.interactionPayloadSchema @bb/thread-ui/interactions: interactionPayloadSchema The interaction export keeps the shared wire shape.
app.interactions.interactionResponseSchema @bb/thread-ui/interactions: interactionResponseSchema The interaction export keeps the shared wire shape.
app.interactions.secretRequestPayloadSchema @bb/thread-ui/interactions: secretRequestPayloadSchema The interaction export keeps the shared wire shape.
app.interactions.secretRequestResponseSchema @bb/thread-ui/interactions: secretRequestResponseSchema The interaction export keeps the shared wire shape.
app.interactions.InteractionOption @bb/thread-ui/interactions: InteractionOption The interaction export keeps the shared wire shape.
app.interactions.InteractionQuestion @bb/thread-ui/interactions: InteractionQuestion The interaction export keeps the shared wire shape.
app.interactions.InteractionPayload @bb/thread-ui/interactions: InteractionPayload The interaction export keeps the shared wire shape.
app.interactions.InteractionAnswer @bb/thread-ui/interactions: InteractionAnswer The interaction export keeps the shared wire shape.
app.interactions.InteractionResponse @bb/thread-ui/interactions: InteractionResponse The interaction export keeps the shared wire shape.
app.interactions.SecretRequestPayload @bb/thread-ui/interactions: SecretRequestPayload The interaction export keeps the shared wire shape.
app.interactions.SecretRequestResponse @bb/thread-ui/interactions: SecretRequestResponse The interaction export keeps the shared wire shape.
app.replacement.threadListPreference kernel winner store: bb.thread-ui.list The global winner replaces the client local-storage preference.
app.contracts.PluginThreadHeaderActionProps bb.thread-ui.view surface: ThreadViewProps.headerActions A view winner can wrap Original.
app.contracts.PluginThreadHeaderActionRegistration bb.thread-ui.view surface: ThreadViewProps.headerActions A view winner can wrap Original.
app.contracts.PluginSidebarThreadIndicator bb.thread-ui.list surface: ThreadListProps and ThreadSummary The list surface receives data and actions through typed props.
app.contracts.PluginSidebarWorkspaceKind bb.thread-ui.list surface: ThreadListProps and ThreadSummary The list surface receives data and actions through typed props.
app.contracts.PluginSidebarThreadActivity bb.thread-ui.list surface: ThreadListProps and ThreadSummary The list surface receives data and actions through typed props.
app.contracts.PluginSidebarProject bb.thread-ui.list surface: ThreadListProps and ThreadSummary The list surface receives data and actions through typed props.
app.contracts.PluginSidebarSplitPane bb.thread-ui.list surface: ThreadListProps and ThreadSummary The list surface receives data and actions through typed props.
app.contracts.PluginMessageActionContext bb.thread-ui.view surface: MessageAction A view wrapper or ThreadChat instance supplies the action.
app.contracts.PluginComposerThreadRowStatus bb.thread-ui.composer surface and @bb/thread-ui/components: ComposerHandle The surface and handle split state from local focus actions.
app.contracts.PluginThreadPanelActionContext bb.thread-ui.sidePanels surface: ThreadSidePanelContribution and ThreadSidePanelProps One list contract replaces panel action and panel body records.
app.contracts.PluginNewThreadPanelActionContext bb.thread-ui.sidePanels surface: ThreadSidePanelContribution and ThreadSidePanelProps One list contract replaces panel action and panel body records.
app.interactions.secretNameSchema @bb/thread-ui/interactions: secretNameSchema The interaction export keeps the shared wire shape.
app.contracts.PluginThreadPanelProps.threadId bb.thread-ui.sidePanels surface: ThreadSidePanelContribution and ThreadSidePanelProps One list contract replaces panel action and panel body records.
app.contracts.PluginThreadPanelProps.params bb.thread-ui.sidePanels surface: ThreadSidePanelContribution and ThreadSidePanelProps One list contract replaces panel action and panel body records.
app.contracts.PluginNewThreadPanelProps.projectId bb.thread-ui.sidePanels surface: ThreadSidePanelContribution and ThreadSidePanelProps One list contract replaces panel action and panel body records.
app.contracts.PluginNewThreadPanelProps.params bb.thread-ui.sidePanels surface: ThreadSidePanelContribution and ThreadSidePanelProps One list contract replaces panel action and panel body records.
app.contracts.PluginPendingInteractionProps.interaction bb.thread-ui.timeline.item surface: InteractionItemProps The keyed timeline component displays the interaction.
app.contracts.PluginPendingInteractionProps.submit bb.thread-ui.timeline.item surface: InteractionItemProps The keyed timeline component displays the interaction.
app.contracts.PluginPendingInteractionProps.cancel bb.thread-ui.timeline.item surface: InteractionItemProps The keyed timeline component displays the interaction.
app.contracts.PluginThreadListProps.activeThreadId bb.thread-ui.list surface: ThreadListProps and ThreadSummary The list surface receives data and actions through typed props.
app.contracts.PluginThreadListProps.activeProjectId bb.thread-ui.list surface: ThreadListProps and ThreadSummary The list surface receives data and actions through typed props.
app.contracts.PluginThreadListProps.isCompactViewport bb.thread-ui.list surface: ThreadListProps and ThreadSummary The list surface receives data and actions through typed props.
app.contracts.PluginThreadListProps.onNavigate bb.thread-ui.list surface: ThreadListProps and ThreadSummary The list surface receives data and actions through typed props.
app.contracts.PluginThreadListProps.searchQuery bb.thread-ui.list surface: ThreadListProps and ThreadSummary The list surface receives data and actions through typed props.
app.contracts.PluginThreadListProps.Original bb.thread-ui.list surface: ThreadListProps and ThreadSummary The list surface receives data and actions through typed props.
app.contracts.PluginThreadHeaderActionProps.threadId bb.thread-ui.view surface: ThreadViewProps.headerActions A view winner can wrap Original.
app.contracts.PluginThreadHeaderActionProps.projectId bb.thread-ui.view surface: ThreadViewProps.headerActions A view winner can wrap Original.
app.contracts.PluginThreadHeaderActionProps.isCompactViewport bb.thread-ui.view surface: ThreadViewProps.headerActions A view winner can wrap Original.
app.contracts.PluginMessageDirectiveProps.attributes bb.thread-ui.timeline.item surface: plugin-owned directive item type The timeline item contract carries message and thread identity.
app.contracts.PluginMessageDirectiveProps.source bb.thread-ui.timeline.item surface: plugin-owned directive item type The timeline item contract carries message and thread identity.
app.contracts.PluginMessageDirectiveProps.message bb.thread-ui.timeline.item surface: plugin-owned directive item type The timeline item contract carries message and thread identity.
app.contracts.PluginMessageDirectiveMessage.id bb.thread-ui.timeline.item surface: plugin-owned directive item type The timeline item contract carries message and thread identity.
app.contracts.PluginMessageDirectiveMessage.threadId bb.thread-ui.timeline.item surface: plugin-owned directive item type The timeline item contract carries message and thread identity.
app.contracts.PluginMessageDirectiveMessage.turnId bb.thread-ui.timeline.item surface: plugin-owned directive item type The timeline item contract carries message and thread identity.
app.contracts.PluginMessageDirectiveMessage.projectId bb.thread-ui.timeline.item surface: plugin-owned directive item type The timeline item contract carries message and thread identity.
app.contracts.PluginThreadPanelActionRegistration.id bb.thread-ui.sidePanels surface: ThreadSidePanelContribution and ThreadSidePanelProps One list contract replaces panel action and panel body records.
app.contracts.PluginThreadPanelActionRegistration.title bb.thread-ui.sidePanels surface: ThreadSidePanelContribution and ThreadSidePanelProps One list contract replaces panel action and panel body records.
app.contracts.PluginThreadPanelActionRegistration.icon bb.thread-ui.sidePanels surface: ThreadSidePanelContribution and ThreadSidePanelProps One list contract replaces panel action and panel body records.
app.contracts.PluginThreadPanelActionRegistration.component bb.thread-ui.sidePanels surface: ThreadSidePanelContribution and ThreadSidePanelProps One list contract replaces panel action and panel body records.
app.contracts.PluginThreadPanelActionRegistration.run bb.thread-ui.sidePanels surface: ThreadSidePanelContribution and ThreadSidePanelProps One list contract replaces panel action and panel body records.
app.contracts.PluginNewThreadPanelActionRegistration.id bb.thread-ui.sidePanels surface: ThreadSidePanelContribution and ThreadSidePanelProps One list contract replaces panel action and panel body records.
app.contracts.PluginNewThreadPanelActionRegistration.title bb.thread-ui.sidePanels surface: ThreadSidePanelContribution and ThreadSidePanelProps One list contract replaces panel action and panel body records.
app.contracts.PluginNewThreadPanelActionRegistration.icon bb.thread-ui.sidePanels surface: ThreadSidePanelContribution and ThreadSidePanelProps One list contract replaces panel action and panel body records.
app.contracts.PluginNewThreadPanelActionRegistration.component bb.thread-ui.sidePanels surface: ThreadSidePanelContribution and ThreadSidePanelProps One list contract replaces panel action and panel body records.
app.contracts.PluginNewThreadPanelActionRegistration.run bb.thread-ui.sidePanels surface: ThreadSidePanelContribution and ThreadSidePanelProps One list contract replaces panel action and panel body records.
app.contracts.PluginMessageActionRegistration.run bb.thread-ui.view surface: MessageAction A view wrapper or ThreadChat instance supplies the action.
app.contracts.PluginTimelineRendererRegistration.kind bb.thread-ui.timeline.item surface: TimelineItem and TimelineItemProps The keyed contract uses the canonical item type.
app.contracts.PluginTimelineRendererRegistration.component bb.thread-ui.timeline.item surface: TimelineItem and TimelineItemProps The keyed contract uses the canonical item type.