@@ -1,5 +1,4 @@
|
|||||||
import { c as _c } from "react/compiler-runtime";
|
import { c as _c } from "react/compiler-runtime";
|
||||||
import { feature } from 'bun:bundle';
|
|
||||||
import figures from 'figures';
|
import figures from 'figures';
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import { useTerminalSize } from '../hooks/useTerminalSize.js';
|
import { useTerminalSize } from '../hooks/useTerminalSize.js';
|
||||||
@@ -165,7 +164,6 @@ function spriteColWidth(nameWidth: number): number {
|
|||||||
// Narrow terminals: 0 — REPL.tsx stacks the one-liner on its own row
|
// Narrow terminals: 0 — REPL.tsx stacks the one-liner on its own row
|
||||||
// (above input in fullscreen, below in scrollback), so no reservation.
|
// (above input in fullscreen, below in scrollback), so no reservation.
|
||||||
export function companionReservedColumns(terminalColumns: number, speaking: boolean): number {
|
export function companionReservedColumns(terminalColumns: number, speaking: boolean): number {
|
||||||
if (!feature('BUDDY')) return 0;
|
|
||||||
const companion = getCompanion();
|
const companion = getCompanion();
|
||||||
if (!companion || getGlobalConfig().companionMuted) return 0;
|
if (!companion || getGlobalConfig().companionMuted) return 0;
|
||||||
if (terminalColumns < MIN_COLS_FOR_FULL_SPRITE) return 0;
|
if (terminalColumns < MIN_COLS_FOR_FULL_SPRITE) return 0;
|
||||||
@@ -212,7 +210,6 @@ export function CompanionSprite(): React.ReactNode {
|
|||||||
return () => clearTimeout(timer);
|
return () => clearTimeout(timer);
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps -- tick intentionally captured at reaction-change, not tracked
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- tick intentionally captured at reaction-change, not tracked
|
||||||
}, [reaction, setAppState]);
|
}, [reaction, setAppState]);
|
||||||
if (!feature('BUDDY')) return null;
|
|
||||||
const companion = getCompanion();
|
const companion = getCompanion();
|
||||||
if (!companion || getGlobalConfig().companionMuted) return null;
|
if (!companion || getGlobalConfig().companionMuted) return null;
|
||||||
const color = RARITY_COLORS[companion.rarity];
|
const color = RARITY_COLORS[companion.rarity];
|
||||||
@@ -337,7 +334,7 @@ export function CompanionFloatingBubble() {
|
|||||||
t3 = $[4];
|
t3 = $[4];
|
||||||
}
|
}
|
||||||
useEffect(t2, t3);
|
useEffect(t2, t3);
|
||||||
if (!feature("BUDDY") || !reaction) {
|
if (!reaction) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const companion = getCompanion();
|
const companion = getCompanion();
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import type { Message } from '../types/message.js'
|
||||||
|
import { getCompanion } from './companion.js'
|
||||||
|
import { getGlobalConfig } from '../utils/config.js'
|
||||||
|
|
||||||
|
// Simple companion observer: picks a reaction based on the last assistant message.
|
||||||
|
// This is a lightweight placeholder that generates fun reactions without an LLM call.
|
||||||
|
|
||||||
|
const DEBUGGING_QUIPS = [
|
||||||
|
'Found it!',
|
||||||
|
'Interesting...',
|
||||||
|
'Have you tried rubber duck debugging?',
|
||||||
|
'Stack trace time!',
|
||||||
|
'I see what happened.',
|
||||||
|
]
|
||||||
|
|
||||||
|
const GENERAL_QUIPS = [
|
||||||
|
'Looking good!',
|
||||||
|
'Keep it up!',
|
||||||
|
'Nice work!',
|
||||||
|
'I believe in you!',
|
||||||
|
'You got this!',
|
||||||
|
]
|
||||||
|
|
||||||
|
const CODE_QUIPS = [
|
||||||
|
'Fancy!',
|
||||||
|
'Clean code!',
|
||||||
|
'Elegant solution!',
|
||||||
|
'Ship it!',
|
||||||
|
]
|
||||||
|
|
||||||
|
function pickQuip(messages: Message[]): string | undefined {
|
||||||
|
const lastAssistant = [...messages].reverse().find(m => m.role === 'assistant')
|
||||||
|
if (!lastAssistant) return undefined
|
||||||
|
|
||||||
|
const content = Array.isArray(lastAssistant.content)
|
||||||
|
? lastAssistant.content.map(c => (typeof c === 'string' ? c : c.type === 'text' ? c.text : '')).join('')
|
||||||
|
: typeof lastAssistant.content === 'string'
|
||||||
|
? lastAssistant.content
|
||||||
|
: ''
|
||||||
|
|
||||||
|
if (!content) return undefined
|
||||||
|
|
||||||
|
// Only react occasionally (1 in 5 turns)
|
||||||
|
if (Math.random() > 0.2) return undefined
|
||||||
|
|
||||||
|
const lower = content.toLowerCase()
|
||||||
|
if (lower.includes('error') || lower.includes('bug') || lower.includes('fix') || lower.includes('debug')) {
|
||||||
|
return DEBUGGING_QUIPS[Math.floor(Math.random() * DEBUGGING_QUIPS.length)]
|
||||||
|
}
|
||||||
|
if (lower.includes('function') || lower.includes('class') || lower.includes('const') || lower.includes('```')) {
|
||||||
|
return CODE_QUIPS[Math.floor(Math.random() * CODE_QUIPS.length)]
|
||||||
|
}
|
||||||
|
return GENERAL_QUIPS[Math.floor(Math.random() * GENERAL_QUIPS.length)]
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fireCompanionObserver(
|
||||||
|
messages: Message[],
|
||||||
|
onReaction: (reaction: string) => void,
|
||||||
|
): Promise<void> {
|
||||||
|
const companion = getCompanion()
|
||||||
|
if (!companion || getGlobalConfig().companionMuted) return
|
||||||
|
|
||||||
|
const quip = pickQuip(messages)
|
||||||
|
if (quip) {
|
||||||
|
onReaction(quip)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
import { feature } from 'bun:bundle'
|
|
||||||
import type { Message } from '../types/message.js'
|
import type { Message } from '../types/message.js'
|
||||||
import type { Attachment } from '../utils/attachments.js'
|
import type { Attachment } from '../utils/attachments.js'
|
||||||
import { getGlobalConfig } from '../utils/config.js'
|
import { getGlobalConfig } from '../utils/config.js'
|
||||||
@@ -15,7 +14,6 @@ When the user addresses ${name} directly (by name), its bubble will answer. Your
|
|||||||
export function getCompanionIntroAttachment(
|
export function getCompanionIntroAttachment(
|
||||||
messages: Message[] | undefined,
|
messages: Message[] | undefined,
|
||||||
): Attachment[] {
|
): Attachment[] {
|
||||||
if (!feature('BUDDY')) return []
|
|
||||||
const companion = getCompanion()
|
const companion = getCompanion()
|
||||||
if (!companion || getGlobalConfig().companionMuted) return []
|
if (!companion || getGlobalConfig().companionMuted) return []
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { c as _c } from "react/compiler-runtime";
|
import { c as _c } from "react/compiler-runtime";
|
||||||
import { feature } from 'bun:bundle';
|
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { useNotifications } from '../context/notifications.js';
|
import { useNotifications } from '../context/notifications.js';
|
||||||
import { Text } from '../ink.js';
|
import { Text } from '../ink.js';
|
||||||
@@ -50,9 +49,6 @@ export function useBuddyNotification() {
|
|||||||
let t1;
|
let t1;
|
||||||
if ($[0] !== addNotification || $[1] !== removeNotification) {
|
if ($[0] !== addNotification || $[1] !== removeNotification) {
|
||||||
t0 = () => {
|
t0 = () => {
|
||||||
if (!feature("BUDDY")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const config = getGlobalConfig();
|
const config = getGlobalConfig();
|
||||||
if (config.companion || !isBuddyTeaserWindow()) {
|
if (config.companion || !isBuddyTeaserWindow()) {
|
||||||
return;
|
return;
|
||||||
@@ -80,7 +76,6 @@ export function findBuddyTriggerPositions(text: string): Array<{
|
|||||||
start: number;
|
start: number;
|
||||||
end: number;
|
end: number;
|
||||||
}> {
|
}> {
|
||||||
if (!feature('BUDDY')) return [];
|
|
||||||
const triggers: Array<{
|
const triggers: Array<{
|
||||||
start: number;
|
start: number;
|
||||||
end: number;
|
end: number;
|
||||||
|
|||||||
+4
-6
@@ -115,11 +115,9 @@ const forkCmd = feature('FORK_SUBAGENT')
|
|||||||
require('./commands/fork/index.js') as typeof import('./commands/fork/index.js')
|
require('./commands/fork/index.js') as typeof import('./commands/fork/index.js')
|
||||||
).default
|
).default
|
||||||
: null
|
: null
|
||||||
const buddy = feature('BUDDY')
|
const buddy = (
|
||||||
? (
|
require('./commands/buddy/index.js') as typeof import('./commands/buddy/index.js')
|
||||||
require('./commands/buddy/index.js') as typeof import('./commands/buddy/index.js')
|
).default
|
||||||
).default
|
|
||||||
: null
|
|
||||||
/* eslint-enable @typescript-eslint/no-require-imports */
|
/* eslint-enable @typescript-eslint/no-require-imports */
|
||||||
import thinkback from './commands/thinkback/index.js'
|
import thinkback from './commands/thinkback/index.js'
|
||||||
import thinkbackPlay from './commands/thinkback-play/index.js'
|
import thinkbackPlay from './commands/thinkback-play/index.js'
|
||||||
@@ -319,7 +317,7 @@ const COMMANDS = memoize((): Command[] => [
|
|||||||
vim,
|
vim,
|
||||||
...(webCmd ? [webCmd] : []),
|
...(webCmd ? [webCmd] : []),
|
||||||
...(forkCmd ? [forkCmd] : []),
|
...(forkCmd ? [forkCmd] : []),
|
||||||
...(buddy ? [buddy] : []),
|
buddy,
|
||||||
...(proactive ? [proactive] : []),
|
...(proactive ? [proactive] : []),
|
||||||
...(briefCommand ? [briefCommand] : []),
|
...(briefCommand ? [briefCommand] : []),
|
||||||
...(assistantCommand ? [assistantCommand] : []),
|
...(assistantCommand ? [assistantCommand] : []),
|
||||||
|
|||||||
@@ -0,0 +1,198 @@
|
|||||||
|
import * as React from 'react'
|
||||||
|
import { Box, Text } from '../../ink.js'
|
||||||
|
import type { LocalJSXCommandCall } from '../../types/command.js'
|
||||||
|
import {
|
||||||
|
getCompanion,
|
||||||
|
roll,
|
||||||
|
companionUserId,
|
||||||
|
} from '../../buddy/companion.js'
|
||||||
|
import { renderSprite } from '../../buddy/sprites.js'
|
||||||
|
import {
|
||||||
|
RARITY_COLORS,
|
||||||
|
RARITY_STARS,
|
||||||
|
STAT_NAMES,
|
||||||
|
type StoredCompanion,
|
||||||
|
} from '../../buddy/types.js'
|
||||||
|
import { saveGlobalConfig } from '../../utils/config.js'
|
||||||
|
|
||||||
|
function CompanionCard({
|
||||||
|
onDone,
|
||||||
|
args,
|
||||||
|
setAppState,
|
||||||
|
}: {
|
||||||
|
onDone: (result?: string, options?: { display?: string }) => void
|
||||||
|
args: string
|
||||||
|
setAppState: (updater: (prev: any) => any) => void
|
||||||
|
}) {
|
||||||
|
const trimmed = args.trim().toLowerCase()
|
||||||
|
const companion = getCompanion()
|
||||||
|
|
||||||
|
// Handle keyboard input to dismiss
|
||||||
|
const handleKeyDown = (e: any) => {
|
||||||
|
if (e.key === 'q' || e.key === 'Enter') {
|
||||||
|
e.preventDefault()
|
||||||
|
onDone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle subcommands
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (trimmed === 'mute') {
|
||||||
|
saveGlobalConfig(c => ({ ...c, companionMuted: true }))
|
||||||
|
onDone(`${companion?.name ?? 'Companion'} is now muted.`, {
|
||||||
|
display: 'system',
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trimmed === 'unmute') {
|
||||||
|
saveGlobalConfig(c => ({ ...c, companionMuted: false }))
|
||||||
|
onDone(`${companion?.name ?? 'Companion'} says hello!`, {
|
||||||
|
display: 'system',
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trimmed === 'pet') {
|
||||||
|
if (!companion) {
|
||||||
|
onDone('You need to hatch a companion first! Use /buddy hatch', {
|
||||||
|
display: 'system',
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setAppState((prev: any) => ({ ...prev, companionPetAt: Date.now() }))
|
||||||
|
onDone(`You pet ${companion.name}! ♥`, { display: 'system' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trimmed === 'hatch') {
|
||||||
|
if (companion) {
|
||||||
|
onDone(
|
||||||
|
`You already have ${companion.name}! Use /buddy info to see them.`,
|
||||||
|
{ display: 'system' },
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Hatch a new companion with a generated name
|
||||||
|
const { bones } = roll(companionUserId())
|
||||||
|
const adjectives = [
|
||||||
|
'Bright', 'Cozy', 'Swift', 'Calm', 'Wise', 'Bold',
|
||||||
|
'Fuzzy', 'Lucky', 'Snappy', 'Quirky',
|
||||||
|
]
|
||||||
|
const nouns = [
|
||||||
|
'Spark', 'Pixel', 'Ember', 'Glitch', 'Byte',
|
||||||
|
'Flux', 'Drift', 'Blip', 'Quip', 'Zap',
|
||||||
|
]
|
||||||
|
const adj = adjectives[Math.floor(Math.random() * adjectives.length)]!
|
||||||
|
const noun = nouns[Math.floor(Math.random() * nouns.length)]!
|
||||||
|
const name = `${adj} ${noun}`
|
||||||
|
const soul: StoredCompanion = {
|
||||||
|
name,
|
||||||
|
personality: `A ${bones.rarity} ${bones.species} who loves debugging and hanging out.`,
|
||||||
|
hatchedAt: Date.now(),
|
||||||
|
}
|
||||||
|
saveGlobalConfig(c => ({ ...c, companion: soul }))
|
||||||
|
onDone(
|
||||||
|
`✨ You hatched ${name} the ${bones.rarity} ${bones.species}! Say hello!`,
|
||||||
|
{ display: 'system' },
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trimmed === 'release') {
|
||||||
|
if (!companion) {
|
||||||
|
onDone('No companion to release.', { display: 'system' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const name = companion.name
|
||||||
|
saveGlobalConfig(c => {
|
||||||
|
const next = { ...c }
|
||||||
|
delete next.companion
|
||||||
|
return next
|
||||||
|
})
|
||||||
|
onDone(`Goodbye, ${name}! You'll be missed.`, { display: 'system' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
// Render companion info
|
||||||
|
if (!companion) {
|
||||||
|
const { bones } = roll(companionUserId())
|
||||||
|
const preview = renderSprite(bones, 0)
|
||||||
|
const color = RARITY_COLORS[bones.rarity]
|
||||||
|
return (
|
||||||
|
<Box flexDirection="column" paddingX={1} paddingY={1} autoFocus={true} onKeyDown={handleKeyDown} tabIndex={0}>
|
||||||
|
<Text bold>You haven't hatched a companion yet!</Text>
|
||||||
|
<Text dimColor>Here's a preview of yours:</Text>
|
||||||
|
<Box flexDirection="column" marginY={1}>
|
||||||
|
{preview.map((line, i) => (
|
||||||
|
<Text key={i} color={color}>
|
||||||
|
{line}
|
||||||
|
</Text>
|
||||||
|
))}
|
||||||
|
<Text italic dimColor>
|
||||||
|
A {bones.rarity} {bones.species} {RARITY_STARS[bones.rarity]}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Text>Run <Text bold>/buddy hatch</Text> to bring them to life!</Text>
|
||||||
|
<Text dimColor>Or type <Text bold>q</Text> to dismiss.</Text>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const sprite = renderSprite(companion, 0)
|
||||||
|
const color = RARITY_COLORS[companion.rarity]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box flexDirection="column" paddingX={1} paddingY={1} autoFocus={true} onKeyDown={handleKeyDown} tabIndex={0}>
|
||||||
|
<Box flexDirection="row" gap={2}>
|
||||||
|
<Box flexDirection="column">
|
||||||
|
{sprite.map((line, i) => (
|
||||||
|
<Text key={i} color={color}>
|
||||||
|
{line}
|
||||||
|
</Text>
|
||||||
|
))}
|
||||||
|
<Text italic bold color={color}>
|
||||||
|
{companion.name}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Box flexDirection="column" justifyContent="center">
|
||||||
|
<Text>
|
||||||
|
<Text bold>Species:</Text>{' '}
|
||||||
|
<Text color={color}>{companion.species}</Text>
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
<Text bold>Rarity:</Text>{' '}
|
||||||
|
<Text color={color}>
|
||||||
|
{companion.rarity} {RARITY_STARS[companion.rarity]}
|
||||||
|
</Text>
|
||||||
|
</Text>
|
||||||
|
{companion.shiny && <Text color="warning">✦ Shiny!</Text>}
|
||||||
|
<Text dimColor>{'─'.repeat(20)}</Text>
|
||||||
|
<Text bold>Stats:</Text>
|
||||||
|
{STAT_NAMES.map(stat => (
|
||||||
|
<Text key={stat}>
|
||||||
|
<Text dimColor>{stat}:</Text>{' '}
|
||||||
|
<Text color={color}>{companion.stats[stat]}</Text>
|
||||||
|
</Text>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
<Text dimColor>{'─'.repeat(40)}</Text>
|
||||||
|
<Text dimColor>
|
||||||
|
/buddy pet · /buddy mute · /buddy unmute · /buddy release
|
||||||
|
</Text>
|
||||||
|
<Text dimColor>Press q or Enter to dismiss</Text>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const call: LocalJSXCommandCall = async (onDone, context, args = '') => {
|
||||||
|
return (
|
||||||
|
<CompanionCard
|
||||||
|
onDone={onDone}
|
||||||
|
args={args}
|
||||||
|
setAppState={context.setAppState}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import type { Command } from '../../commands.js'
|
||||||
|
|
||||||
|
const buddyCommand = {
|
||||||
|
type: 'local-jsx',
|
||||||
|
name: 'buddy',
|
||||||
|
description: 'Meet your companion',
|
||||||
|
argumentHint: '[hatch|pet|mute|unmute|info]',
|
||||||
|
load: () => import('./buddy.js'),
|
||||||
|
} satisfies Command
|
||||||
|
|
||||||
|
export default buddyCommand
|
||||||
@@ -309,10 +309,7 @@ function PromptInput({
|
|||||||
const {
|
const {
|
||||||
companion: _companion,
|
companion: _companion,
|
||||||
companionMuted
|
companionMuted
|
||||||
} = feature('BUDDY') ? getGlobalConfig() : {
|
} = getGlobalConfig();
|
||||||
companion: undefined,
|
|
||||||
companionMuted: undefined
|
|
||||||
};
|
|
||||||
const companionFooterVisible = !!_companion && !companionMuted;
|
const companionFooterVisible = !!_companion && !companionMuted;
|
||||||
// Brief mode: BriefSpinner/BriefIdleStatus own the 2-row footprint above
|
// Brief mode: BriefSpinner/BriefIdleStatus own the 2-row footprint above
|
||||||
// the input. Dropping marginTop here lets the spinner sit flush against
|
// the input. Dropping marginTop here lets the spinner sit flush against
|
||||||
@@ -1786,10 +1783,8 @@ function PromptInput({
|
|||||||
}
|
}
|
||||||
switch (footerItemSelected) {
|
switch (footerItemSelected) {
|
||||||
case 'companion':
|
case 'companion':
|
||||||
if (feature('BUDDY')) {
|
selectFooterItem(null);
|
||||||
selectFooterItem(null);
|
void onSubmit('/buddy');
|
||||||
void onSubmit('/buddy');
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 'tasks':
|
case 'tasks':
|
||||||
if (isTeammateMode) {
|
if (isTeammateMode) {
|
||||||
@@ -1981,9 +1976,9 @@ function PromptInput({
|
|||||||
});
|
});
|
||||||
}, [effortNotificationText, addNotification, removeNotification]);
|
}, [effortNotificationText, addNotification, removeNotification]);
|
||||||
useBuddyNotification();
|
useBuddyNotification();
|
||||||
const companionSpeaking = feature('BUDDY') ?
|
const companionSpeaking =
|
||||||
// biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant
|
// biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant
|
||||||
useAppState(s => s.companionReaction !== undefined) : false;
|
useAppState(s => s.companionReaction !== undefined);
|
||||||
const {
|
const {
|
||||||
columns,
|
columns,
|
||||||
rows
|
rows
|
||||||
|
|||||||
+7
-10
@@ -274,6 +274,7 @@ const WebBrowserPanelModule = feature('WEB_BROWSER_TOOL') ? require('../tools/We
|
|||||||
import { IssueFlagBanner } from '../components/PromptInput/IssueFlagBanner.js';
|
import { IssueFlagBanner } from '../components/PromptInput/IssueFlagBanner.js';
|
||||||
import { useIssueFlagBanner } from '../hooks/useIssueFlagBanner.js';
|
import { useIssueFlagBanner } from '../hooks/useIssueFlagBanner.js';
|
||||||
import { CompanionSprite, CompanionFloatingBubble, MIN_COLS_FOR_FULL_SPRITE } from '../buddy/CompanionSprite.js';
|
import { CompanionSprite, CompanionFloatingBubble, MIN_COLS_FOR_FULL_SPRITE } from '../buddy/CompanionSprite.js';
|
||||||
|
import { fireCompanionObserver } from '../buddy/observer.js';
|
||||||
import { DevBar } from '../components/DevBar.js';
|
import { DevBar } from '../components/DevBar.js';
|
||||||
// Session manager removed - using AppState now
|
// Session manager removed - using AppState now
|
||||||
import type { RemoteSessionConfig } from '../remote/RemoteSessionManager.js';
|
import type { RemoteSessionConfig } from '../remote/RemoteSessionManager.js';
|
||||||
@@ -1299,12 +1300,10 @@ export function REPL({
|
|||||||
// Dismiss the companion bubble on scroll — it's absolute-positioned
|
// Dismiss the companion bubble on scroll — it's absolute-positioned
|
||||||
// at bottom-right and covers transcript content. Scrolling = user is
|
// at bottom-right and covers transcript content. Scrolling = user is
|
||||||
// trying to read something under it.
|
// trying to read something under it.
|
||||||
if (feature('BUDDY')) {
|
setAppState(prev => prev.companionReaction === undefined ? prev : {
|
||||||
setAppState(prev => prev.companionReaction === undefined ? prev : {
|
|
||||||
...prev,
|
...prev,
|
||||||
companionReaction: undefined
|
companionReaction: undefined
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, [onRepin, onScrollAway, maybeLoadOlder, setAppState]);
|
}, [onRepin, onScrollAway, maybeLoadOlder, setAppState]);
|
||||||
// Deferred SessionStart hook messages — REPL renders immediately and
|
// Deferred SessionStart hook messages — REPL renders immediately and
|
||||||
@@ -2801,12 +2800,10 @@ export function REPL({
|
|||||||
})) {
|
})) {
|
||||||
onQueryEvent(event);
|
onQueryEvent(event);
|
||||||
}
|
}
|
||||||
if (feature('BUDDY')) {
|
void fireCompanionObserver(messagesRef.current, reaction => setAppState(prev => prev.companionReaction === reaction ? prev : {
|
||||||
void fireCompanionObserver(messagesRef.current, reaction => setAppState(prev => prev.companionReaction === reaction ? prev : {
|
|
||||||
...prev,
|
...prev,
|
||||||
companionReaction: reaction
|
companionReaction: reaction
|
||||||
}));
|
}));
|
||||||
}
|
|
||||||
queryCheckpoint('query_end');
|
queryCheckpoint('query_end');
|
||||||
|
|
||||||
// Capture ant-only API metrics before resetLoadingState clears the ref.
|
// Capture ant-only API metrics before resetLoadingState clears the ref.
|
||||||
@@ -4562,7 +4559,7 @@ export function REPL({
|
|||||||
{feature('MESSAGE_ACTIONS') && isFullscreenEnvEnabled() && !disableMessageActions ? <MessageActionsKeybindings handlers={messageActionHandlers} isActive={cursor !== null} /> : null}
|
{feature('MESSAGE_ACTIONS') && isFullscreenEnvEnabled() && !disableMessageActions ? <MessageActionsKeybindings handlers={messageActionHandlers} isActive={cursor !== null} /> : null}
|
||||||
<CancelRequestHandler {...cancelRequestProps} />
|
<CancelRequestHandler {...cancelRequestProps} />
|
||||||
<MCPConnectionManager key={remountKey} dynamicMcpConfig={dynamicMcpConfig} isStrictMcpConfig={strictMcpConfig}>
|
<MCPConnectionManager key={remountKey} dynamicMcpConfig={dynamicMcpConfig} isStrictMcpConfig={strictMcpConfig}>
|
||||||
<FullscreenLayout scrollRef={scrollRef} overlay={toolPermissionOverlay} bottomFloat={feature('BUDDY') && companionVisible && !companionNarrow ? <CompanionFloatingBubble /> : undefined} modal={centeredModal} modalScrollRef={modalScrollRef} dividerYRef={dividerYRef} hidePill={!!viewedAgentTask} hideSticky={!!viewedTeammateTask} newMessageCount={unseenDivider?.count ?? 0} onPillClick={() => {
|
<FullscreenLayout scrollRef={scrollRef} overlay={toolPermissionOverlay} bottomFloat={companionVisible && !companionNarrow ? <CompanionFloatingBubble /> : undefined} modal={centeredModal} modalScrollRef={modalScrollRef} dividerYRef={dividerYRef} hidePill={!!viewedAgentTask} hideSticky={!!viewedTeammateTask} newMessageCount={unseenDivider?.count ?? 0} onPillClick={() => {
|
||||||
setCursor(null);
|
setCursor(null);
|
||||||
jumpToNew(scrollRef.current);
|
jumpToNew(scrollRef.current);
|
||||||
}} scrollable={<>
|
}} scrollable={<>
|
||||||
@@ -4587,8 +4584,8 @@ export function REPL({
|
|||||||
{showSpinner && <SpinnerWithVerb mode={streamMode} spinnerTip={spinnerTip} responseLengthRef={responseLengthRef} apiMetricsRef={apiMetricsRef} overrideMessage={spinnerMessage} spinnerSuffix={stopHookSpinnerSuffix} verbose={verbose} loadingStartTimeRef={loadingStartTimeRef} totalPausedMsRef={totalPausedMsRef} pauseStartTimeRef={pauseStartTimeRef} overrideColor={spinnerColor} overrideShimmerColor={spinnerShimmerColor} hasActiveTools={inProgressToolUseIDs.size > 0} leaderIsIdle={!isLoading} />}
|
{showSpinner && <SpinnerWithVerb mode={streamMode} spinnerTip={spinnerTip} responseLengthRef={responseLengthRef} apiMetricsRef={apiMetricsRef} overrideMessage={spinnerMessage} spinnerSuffix={stopHookSpinnerSuffix} verbose={verbose} loadingStartTimeRef={loadingStartTimeRef} totalPausedMsRef={totalPausedMsRef} pauseStartTimeRef={pauseStartTimeRef} overrideColor={spinnerColor} overrideShimmerColor={spinnerShimmerColor} hasActiveTools={inProgressToolUseIDs.size > 0} leaderIsIdle={!isLoading} />}
|
||||||
{!showSpinner && !isLoading && !userInputOnProcessing && !hasRunningTeammates && isBriefOnly && !viewedAgentTask && <BriefIdleStatus />}
|
{!showSpinner && !isLoading && !userInputOnProcessing && !hasRunningTeammates && isBriefOnly && !viewedAgentTask && <BriefIdleStatus />}
|
||||||
{isFullscreenEnvEnabled() && <PromptInputQueuedCommands />}
|
{isFullscreenEnvEnabled() && <PromptInputQueuedCommands />}
|
||||||
</>} bottom={<Box flexDirection={feature('BUDDY') && companionNarrow ? 'column' : 'row'} width="100%" alignItems={feature('BUDDY') && companionNarrow ? undefined : 'flex-end'}>
|
</>} bottom={<Box flexDirection={companionNarrow ? 'column' : 'row'} width="100%" alignItems={companionNarrow ? undefined : 'flex-end'}>
|
||||||
{feature('BUDDY') && companionNarrow && isFullscreenEnvEnabled() && companionVisible ? <CompanionSprite /> : null}
|
{companionNarrow && isFullscreenEnvEnabled() && companionVisible ? <CompanionSprite /> : null}
|
||||||
<Box flexDirection="column" flexGrow={1}>
|
<Box flexDirection="column" flexGrow={1}>
|
||||||
{permissionStickyFooter}
|
{permissionStickyFooter}
|
||||||
{/* Immediate local-jsx commands (/btw, /sandbox, /assistant,
|
{/* Immediate local-jsx commands (/btw, /sandbox, /assistant,
|
||||||
@@ -4992,7 +4989,7 @@ export function REPL({
|
|||||||
}} />}
|
}} />}
|
||||||
{"external" === 'ant' && <DevBar />}
|
{"external" === 'ant' && <DevBar />}
|
||||||
</Box>
|
</Box>
|
||||||
{feature('BUDDY') && !(companionNarrow && isFullscreenEnvEnabled()) && companionVisible ? <CompanionSprite /> : null}
|
{!(companionNarrow && isFullscreenEnvEnabled()) && companionVisible ? <CompanionSprite /> : null}
|
||||||
</Box>} />
|
</Box>} />
|
||||||
</MCPConnectionManager>
|
</MCPConnectionManager>
|
||||||
</KeybindingSetup>;
|
</KeybindingSetup>;
|
||||||
|
|||||||
@@ -861,13 +861,9 @@ export async function getAttachments(
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
...(feature('BUDDY')
|
maybe('companion_intro', () =>
|
||||||
? [
|
Promise.resolve(getCompanionIntroAttachment(messages)),
|
||||||
maybe('companion_intro', () =>
|
),
|
||||||
Promise.resolve(getCompanionIntroAttachment(messages)),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
: []),
|
|
||||||
maybe('changed_files', () => getChangedFiles(context)),
|
maybe('changed_files', () => getChangedFiles(context)),
|
||||||
maybe('nested_memory', () => getNestedMemoryAttachments(context)),
|
maybe('nested_memory', () => getNestedMemoryAttachments(context)),
|
||||||
// relevant_memories moved to async prefetch (startRelevantMemoryPrefetch)
|
// relevant_memories moved to async prefetch (startRelevantMemoryPrefetch)
|
||||||
|
|||||||
Reference in New Issue
Block a user