mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-10 12:37:55 +01:00
- Give the assistant chat body its own scroll container so loading or
scrolling a chat never moves the page or the sidebar.
- Render the conference room with issue-chat density: the board user's
messages are right-aligned blue bubbles, and the company user directory
now resolves real names/avatars instead of "You".
- Report heartbeat scheduling suppression from the selected-agent-chat
comment route and surface it as an inline notice, so a send on a
worktree instance no longer looks like it silently did nothing.
- Deep-link Agent Feed events on board_chat issues to /board-chat/{ref}
(they are hidden from the issues list, so the old link was a broken
/issues/<uuid>), and stop showing the board user's own comment events.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
111 lines
3.9 KiB
TypeScript
111 lines
3.9 KiB
TypeScript
export type IssueChatThreadPresetName = "task" | "assistant";
|
|
|
|
export type IssueChatThreadVariant = "full" | "embedded";
|
|
|
|
export type IssueChatThreadDensity = "comfortable" | "compact";
|
|
|
|
export type IssueChatThreadWorkingIndicator = "expanded" | "collapsed";
|
|
|
|
export type IssueChatThreadComposerSubmitKey = "mod-enter" | "enter";
|
|
|
|
export interface IssueChatThreadPresetConfig {
|
|
preset: IssueChatThreadPresetName;
|
|
density: IssueChatThreadDensity;
|
|
workingIndicator: IssueChatThreadWorkingIndicator;
|
|
variant: IssueChatThreadVariant;
|
|
showComposer: boolean;
|
|
showJumpToLatest: boolean;
|
|
autoScrollToLatestOnInitialLoad: boolean;
|
|
emptyMessage: string;
|
|
suppressIssueStatusNotices: boolean;
|
|
enableReassign: boolean;
|
|
showBackgroundWorkChildren: boolean;
|
|
composerSubmitKey: IssueChatThreadComposerSubmitKey;
|
|
composerSingleLine: boolean;
|
|
}
|
|
|
|
export interface IssueChatThreadPresetOverrides {
|
|
variant?: IssueChatThreadVariant;
|
|
showComposer?: boolean;
|
|
showJumpToLatest?: boolean;
|
|
autoScrollToLatestOnInitialLoad?: boolean;
|
|
emptyMessage?: string;
|
|
suppressIssueStatusNotices?: boolean;
|
|
enableReassign?: boolean;
|
|
showBackgroundWorkChildren?: boolean;
|
|
}
|
|
|
|
type IssueChatThreadPresetDefinition = Omit<
|
|
IssueChatThreadPresetConfig,
|
|
"preset" | "variant" | "showJumpToLatest" | "emptyMessage"
|
|
> & {
|
|
variant: IssueChatThreadVariant;
|
|
showJumpToLatest: (variant: IssueChatThreadVariant) => boolean;
|
|
emptyMessage: (variant: IssueChatThreadVariant) => string;
|
|
};
|
|
|
|
const taskEmptyMessage = (variant: IssueChatThreadVariant) =>
|
|
variant === "embedded"
|
|
? "No run output yet."
|
|
: "This task conversation is empty. Start with a message below.";
|
|
|
|
const ISSUE_CHAT_THREAD_PRESETS: Record<IssueChatThreadPresetName, IssueChatThreadPresetDefinition> = {
|
|
task: {
|
|
density: "comfortable",
|
|
workingIndicator: "expanded",
|
|
variant: "full",
|
|
showComposer: true,
|
|
showJumpToLatest: (variant) => variant === "full",
|
|
autoScrollToLatestOnInitialLoad: true,
|
|
emptyMessage: taskEmptyMessage,
|
|
suppressIssueStatusNotices: false,
|
|
enableReassign: false,
|
|
showBackgroundWorkChildren: true,
|
|
composerSubmitKey: "mod-enter",
|
|
composerSingleLine: false,
|
|
},
|
|
assistant: {
|
|
// Comfortable density matches the issue-chat message rendering: the
|
|
// board user's own messages are right-aligned blue bubbles (PAP-13345).
|
|
density: "comfortable",
|
|
workingIndicator: "collapsed",
|
|
variant: "full",
|
|
showComposer: true,
|
|
showJumpToLatest: () => false,
|
|
autoScrollToLatestOnInitialLoad: true,
|
|
emptyMessage: () => "Send a message to start the conversation.",
|
|
suppressIssueStatusNotices: true,
|
|
enableReassign: false,
|
|
showBackgroundWorkChildren: false,
|
|
composerSubmitKey: "enter",
|
|
composerSingleLine: true,
|
|
},
|
|
};
|
|
|
|
export function resolveIssueChatThreadPreset(
|
|
preset: IssueChatThreadPresetName = "task",
|
|
overrides: IssueChatThreadPresetOverrides = {},
|
|
): IssueChatThreadPresetConfig {
|
|
const definition = ISSUE_CHAT_THREAD_PRESETS[preset];
|
|
const variant = overrides.variant ?? definition.variant;
|
|
|
|
return {
|
|
preset,
|
|
density: definition.density,
|
|
workingIndicator: definition.workingIndicator,
|
|
variant,
|
|
showComposer: overrides.showComposer ?? definition.showComposer,
|
|
showJumpToLatest: overrides.showJumpToLatest ?? definition.showJumpToLatest(variant),
|
|
autoScrollToLatestOnInitialLoad:
|
|
overrides.autoScrollToLatestOnInitialLoad ?? definition.autoScrollToLatestOnInitialLoad,
|
|
emptyMessage: overrides.emptyMessage ?? definition.emptyMessage(variant),
|
|
suppressIssueStatusNotices:
|
|
overrides.suppressIssueStatusNotices ?? definition.suppressIssueStatusNotices,
|
|
enableReassign: overrides.enableReassign ?? definition.enableReassign,
|
|
showBackgroundWorkChildren:
|
|
overrides.showBackgroundWorkChildren ?? definition.showBackgroundWorkChildren,
|
|
composerSubmitKey: definition.composerSubmitKey,
|
|
composerSingleLine: definition.composerSingleLine,
|
|
};
|
|
}
|