fix: remove queued content as-any casts
This commit is contained in:
parent
c130eaad4b
commit
ba36ca454d
|
|
@ -49,7 +49,7 @@ export interface ResolvePrepareCaptureResult extends ScreenshotResult {
|
||||||
|
|
||||||
export interface ComputerExecutorCapabilities {
|
export interface ComputerExecutorCapabilities {
|
||||||
screenshotFiltering: 'native' | 'none'
|
screenshotFiltering: 'native' | 'none'
|
||||||
platform: 'darwin' | 'win32'
|
platform: 'darwin' | 'win32' | 'linux'
|
||||||
hostBundleId: string
|
hostBundleId: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,7 @@ export function normalizeKeySequence(seq: string): string {
|
||||||
*/
|
*/
|
||||||
export function isSystemKeyCombo(
|
export function isSystemKeyCombo(
|
||||||
seq: string,
|
seq: string,
|
||||||
platform: 'darwin' | 'win32',
|
platform: 'darwin' | 'win32' | 'linux',
|
||||||
): boolean {
|
): boolean {
|
||||||
const blocklist = platform === 'darwin' ? BLOCKED_DARWIN : BLOCKED_WIN32
|
const blocklist = platform === 'darwin' ? BLOCKED_DARWIN : BLOCKED_WIN32
|
||||||
const { mods, keys } = partitionKeys(seq)
|
const { mods, keys } = partitionKeys(seq)
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,14 @@ import type {
|
||||||
* Individual message subtypes (UserMessage, AssistantMessage, etc.) extend
|
* Individual message subtypes (UserMessage, AssistantMessage, etc.) extend
|
||||||
* this with narrower `type` literals and additional fields.
|
* this with narrower `type` literals and additional fields.
|
||||||
*/
|
*/
|
||||||
export type MessageType = 'user' | 'assistant' | 'system' | 'attachment' | 'progress' | 'grouped_tool_use' | 'collapsed_read_search'
|
export type MessageType =
|
||||||
|
| 'user'
|
||||||
|
| 'assistant'
|
||||||
|
| 'system'
|
||||||
|
| 'attachment'
|
||||||
|
| 'progress'
|
||||||
|
| 'grouped_tool_use'
|
||||||
|
| 'collapsed_read_search'
|
||||||
|
|
||||||
/** A single content element inside message.content arrays. */
|
/** A single content element inside message.content arrays. */
|
||||||
export type ContentItem = ContentBlockParam | ContentBlock
|
export type ContentItem = ContentBlockParam | ContentBlock
|
||||||
|
|
@ -37,7 +44,14 @@ export type Message = {
|
||||||
isCompactSummary?: boolean
|
isCompactSummary?: boolean
|
||||||
toolUseResult?: unknown
|
toolUseResult?: unknown
|
||||||
isVisibleInTranscriptOnly?: boolean
|
isVisibleInTranscriptOnly?: boolean
|
||||||
attachment?: { type: string; toolUseID?: string; [key: string]: unknown; addedNames: string[]; addedLines: string[]; removedNames: string[] }
|
attachment?: {
|
||||||
|
type: string
|
||||||
|
toolUseID?: string
|
||||||
|
[key: string]: unknown
|
||||||
|
addedNames: string[]
|
||||||
|
addedLines: string[]
|
||||||
|
removedNames: string[]
|
||||||
|
}
|
||||||
message?: {
|
message?: {
|
||||||
role?: string
|
role?: string
|
||||||
id?: string
|
id?: string
|
||||||
|
|
@ -52,8 +66,12 @@ export type AssistantMessage = Message & {
|
||||||
type: 'assistant'
|
type: 'assistant'
|
||||||
message: NonNullable<Message['message']>
|
message: NonNullable<Message['message']>
|
||||||
}
|
}
|
||||||
export type AttachmentMessage<T = { type: string; [key: string]: unknown }> = Message & { type: 'attachment'; attachment: T }
|
export type AttachmentMessage<T = { type: string; [key: string]: unknown }> =
|
||||||
export type ProgressMessage<T = unknown> = Message & { type: 'progress'; data: T }
|
Message & { type: 'attachment'; attachment: T }
|
||||||
|
export type ProgressMessage<T = unknown> = Message & {
|
||||||
|
type: 'progress'
|
||||||
|
data: T
|
||||||
|
}
|
||||||
export type SystemLocalCommandMessage = Message & { type: 'system' }
|
export type SystemLocalCommandMessage = Message & { type: 'system' }
|
||||||
export type SystemMessage = Message & { type: 'system' }
|
export type SystemMessage = Message & { type: 'system' }
|
||||||
export type UserMessage = Message & {
|
export type UserMessage = Message & {
|
||||||
|
|
@ -78,7 +96,7 @@ export type SystemCompactBoundaryMessage = Message & {
|
||||||
}
|
}
|
||||||
export type TombstoneMessage = Message
|
export type TombstoneMessage = Message
|
||||||
export type ToolUseSummaryMessage = Message
|
export type ToolUseSummaryMessage = Message
|
||||||
export type MessageOrigin = string
|
export type MessageOrigin = string | { kind: string; [key: string]: unknown }
|
||||||
export type CompactMetadata = Record<string, unknown>
|
export type CompactMetadata = Record<string, unknown>
|
||||||
export type SystemAPIErrorMessage = Message & { type: 'system' }
|
export type SystemAPIErrorMessage = Message & { type: 'system' }
|
||||||
export type SystemFileSnapshotMessage = Message & { type: 'system' }
|
export type SystemFileSnapshotMessage = Message & { type: 'system' }
|
||||||
|
|
@ -126,7 +144,14 @@ export type RenderableMessage =
|
||||||
| AssistantMessage
|
| AssistantMessage
|
||||||
| UserMessage
|
| UserMessage
|
||||||
| (Message & { type: 'system' })
|
| (Message & { type: 'system' })
|
||||||
| (Message & { type: 'attachment'; attachment: { type: string; memories?: { path: string; content: string; mtimeMs: number }[]; [key: string]: unknown } })
|
| (Message & {
|
||||||
|
type: 'attachment'
|
||||||
|
attachment: {
|
||||||
|
type: string
|
||||||
|
memories?: { path: string; content: string; mtimeMs: number }[]
|
||||||
|
[key: string]: unknown
|
||||||
|
}
|
||||||
|
})
|
||||||
| (Message & { type: 'progress' })
|
| (Message & { type: 'progress' })
|
||||||
| GroupedToolUseMessage
|
| GroupedToolUseMessage
|
||||||
| CollapsedReadSearchGroup
|
| CollapsedReadSearchGroup
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,11 @@ import { env } from '../env.js'
|
||||||
|
|
||||||
export const COMPUTER_USE_MCP_SERVER_NAME = 'computer-use'
|
export const COMPUTER_USE_MCP_SERVER_NAME = 'computer-use'
|
||||||
|
|
||||||
|
type CliComputerUseCapabilities = {
|
||||||
|
screenshotFiltering: 'native' | 'none'
|
||||||
|
platform: 'darwin' | 'win32' | 'linux'
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sentinel bundle ID for the frontmost gate. Claude Code is a terminal — it has
|
* Sentinel bundle ID for the frontmost gate. Claude Code is a terminal — it has
|
||||||
* no window. This never matches a real `NSWorkspace.frontmostApplication`, so
|
* no window. This never matches a real `NSWorkspace.frontmostApplication`, so
|
||||||
|
|
@ -52,15 +57,14 @@ export function getTerminalBundleId(): string | null {
|
||||||
* takes this shape (no `hostBundleId`, no `teachMode`).
|
* takes this shape (no `hostBundleId`, no `teachMode`).
|
||||||
*/
|
*/
|
||||||
export const CLI_CU_CAPABILITIES = {
|
export const CLI_CU_CAPABILITIES = {
|
||||||
screenshotFiltering: (process.platform === 'darwin'
|
screenshotFiltering: process.platform === 'darwin' ? 'native' : 'none',
|
||||||
? 'native'
|
platform:
|
||||||
: 'none') as any,
|
process.platform === 'win32'
|
||||||
platform: (process.platform === 'win32'
|
? 'win32'
|
||||||
? 'win32'
|
: process.platform === 'linux'
|
||||||
: process.platform === 'linux'
|
? 'linux'
|
||||||
? 'linux'
|
: 'darwin',
|
||||||
: 'darwin') as any,
|
} satisfies CliComputerUseCapabilities
|
||||||
}
|
|
||||||
|
|
||||||
export function isComputerUseMCPServer(name: string): boolean {
|
export function isComputerUseMCPServer(name: string): boolean {
|
||||||
return normalizeNameForMCP(name) === COMPUTER_USE_MCP_SERVER_NAME
|
return normalizeNameForMCP(name) === COMPUTER_USE_MCP_SERVER_NAME
|
||||||
|
|
|
||||||
|
|
@ -94,11 +94,11 @@ export async function handleUrlSchemeLaunch(): Promise<number | null> {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { waitForUrlEvent } = await import('url-handler-napi')
|
const { waitForUrlEvent } = await import('url-handler-napi')
|
||||||
const url = (waitForUrlEvent as any)(5000)
|
const url = await waitForUrlEvent(5000)
|
||||||
if (!url) {
|
if (!url) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
return await handleDeepLinkUri(await url as string)
|
return await handleDeepLinkUri(url)
|
||||||
} catch {
|
} catch {
|
||||||
// NAPI module not available, or handleDeepLinkUri rejected — not a URL launch
|
// NAPI module not available, or handleDeepLinkUri rejected — not a URL launch
|
||||||
return null
|
return null
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ export function modelSupportsMaxEffort(_model: string): boolean {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function modelSupportsXhighEffort(_model: string): boolean {
|
export function modelSupportsXhighEffort(_model: string): boolean {
|
||||||
const supported3P = get3PModelCapabilityOverride(_model, 'xhigh_effort' as any)
|
const supported3P = get3PModelCapabilityOverride(_model, 'xhigh_effort')
|
||||||
if (supported3P !== undefined) {
|
if (supported3P !== undefined) {
|
||||||
return supported3P
|
return supported3P
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -368,7 +368,8 @@ export function isQueuedCommandEditable(cmd: QueuedCommand): boolean {
|
||||||
export function isQueuedCommandVisible(cmd: QueuedCommand): boolean {
|
export function isQueuedCommandVisible(cmd: QueuedCommand): boolean {
|
||||||
if (
|
if (
|
||||||
(feature('KAIROS') || feature('KAIROS_CHANNELS')) &&
|
(feature('KAIROS') || feature('KAIROS_CHANNELS')) &&
|
||||||
(cmd as any).origin?.kind === 'channel'
|
typeof cmd.origin === 'object' &&
|
||||||
|
cmd.origin?.kind === 'channel'
|
||||||
)
|
)
|
||||||
return true
|
return true
|
||||||
return isQueuedCommandEditable(cmd)
|
return isQueuedCommandEditable(cmd)
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { getAPIProvider } from './providers.js'
|
||||||
export type ModelCapabilityOverride =
|
export type ModelCapabilityOverride =
|
||||||
| 'effort'
|
| 'effort'
|
||||||
| 'max_effort'
|
| 'max_effort'
|
||||||
|
| 'xhigh_effort'
|
||||||
| 'thinking'
|
| 'thinking'
|
||||||
| 'adaptive_thinking'
|
| 'adaptive_thinking'
|
||||||
| 'interleaved_thinking'
|
| 'interleaved_thinking'
|
||||||
|
|
|
||||||
|
|
@ -362,19 +362,24 @@ export async function persistFileSnapshotIfRemote(): Promise<void> {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const snapshotFiles: SystemFileSnapshotMessage['snapshotFiles'] = []
|
type SnapshotFile = {
|
||||||
|
key: string
|
||||||
|
path: string
|
||||||
|
content: string
|
||||||
|
}
|
||||||
|
const snapshotFiles: SnapshotFile[] = []
|
||||||
|
|
||||||
// Snapshot plan file
|
// Snapshot plan file
|
||||||
const plan = getPlan()
|
const plan = getPlan()
|
||||||
if (plan) {
|
if (plan) {
|
||||||
(snapshotFiles as any[]).push({
|
snapshotFiles.push({
|
||||||
key: 'plan',
|
key: 'plan',
|
||||||
path: getPlanFilePath(),
|
path: getPlanFilePath(),
|
||||||
content: plan,
|
content: plan,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((snapshotFiles as any[]).length === 0) {
|
if (snapshotFiles.length === 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -184,11 +184,15 @@ export function getDeclaredMarketplaces(): Record<string, DeclaredMarketplace> {
|
||||||
// Lowest precedence: implicit < --add-dir < merged settings.
|
// Lowest precedence: implicit < --add-dir < merged settings.
|
||||||
// An explicit extraKnownMarketplaces entry for claude-plugins-official
|
// An explicit extraKnownMarketplaces entry for claude-plugins-official
|
||||||
// in --add-dir or settings wins.
|
// in --add-dir or settings wins.
|
||||||
return {
|
const declared = {
|
||||||
...implicit,
|
...implicit,
|
||||||
...getAddDirExtraMarketplaces(),
|
...(getAddDirExtraMarketplaces() as Record<string, DeclaredMarketplace>),
|
||||||
...(getInitialSettings().extraKnownMarketplaces ?? {}),
|
...((getInitialSettings().extraKnownMarketplaces ?? {}) as Record<
|
||||||
} as any
|
string,
|
||||||
|
DeclaredMarketplace
|
||||||
|
>),
|
||||||
|
} satisfies Record<string, DeclaredMarketplace>
|
||||||
|
return declared
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ import {
|
||||||
isDeferredTool,
|
isDeferredTool,
|
||||||
SEARCH_EXTRA_TOOLS_TOOL_NAME,
|
SEARCH_EXTRA_TOOLS_TOOL_NAME,
|
||||||
} from '@claude-code-best/builtin-tools/tools/SearchExtraToolsTool/prompt.js'
|
} from '@claude-code-best/builtin-tools/tools/SearchExtraToolsTool/prompt.js'
|
||||||
import type { Message } from '../types/message.js'
|
import type { Message, SystemCompactBoundaryMessage } from '../types/message.js'
|
||||||
import {
|
import {
|
||||||
countToolDefinitionTokens,
|
countToolDefinitionTokens,
|
||||||
TOOL_TOKEN_COUNT_OVERHEAD,
|
TOOL_TOKEN_COUNT_OVERHEAD,
|
||||||
|
|
@ -504,12 +504,15 @@ export function extractDiscoveredToolNames(messages: Message[]): Set<string> {
|
||||||
// check rather than isCompactBoundaryMessage — utils/messages.ts imports
|
// check rather than isCompactBoundaryMessage — utils/messages.ts imports
|
||||||
// from this file, so importing back would be circular.
|
// from this file, so importing back would be circular.
|
||||||
if (msg.type === 'system' && msg.subtype === 'compact_boundary') {
|
if (msg.type === 'system' && msg.subtype === 'compact_boundary') {
|
||||||
const carried = (msg as any).compactMetadata?.preCompactDiscoveredTools as
|
const boundary = msg as SystemCompactBoundaryMessage
|
||||||
| string[]
|
const carried = boundary.compactMetadata.preCompactDiscoveredTools
|
||||||
| undefined
|
|
||||||
if (carried) {
|
if (carried) {
|
||||||
for (const name of carried) discoveredTools.add(name)
|
if (Array.isArray(carried)) {
|
||||||
carriedFromBoundary += carried.length
|
for (const name of carried) {
|
||||||
|
if (typeof name === 'string') discoveredTools.add(name)
|
||||||
|
}
|
||||||
|
carriedFromBoundary += carried.length
|
||||||
|
}
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ import type {
|
||||||
ContextCollapseSnapshotEntry,
|
ContextCollapseSnapshotEntry,
|
||||||
PersistedWorktreeSession,
|
PersistedWorktreeSession,
|
||||||
} from '../types/logs.js'
|
} from '../types/logs.js'
|
||||||
import type { Message } from '../types/message.js'
|
import type { ContentItem, Message } from '../types/message.js'
|
||||||
import { renameRecordingForSession } from './asciicast.js'
|
import { renameRecordingForSession } from './asciicast.js'
|
||||||
import { clearMemoryFileCaches } from './claudemd.js'
|
import { clearMemoryFileCaches } from './claudemd.js'
|
||||||
import {
|
import {
|
||||||
|
|
@ -78,7 +78,9 @@ function extractTodosFromTranscript(messages: Message[]): TodoList {
|
||||||
for (let i = messages.length - 1; i >= 0; i--) {
|
for (let i = messages.length - 1; i >= 0; i--) {
|
||||||
const msg = messages[i]
|
const msg = messages[i]
|
||||||
if (msg?.type !== 'assistant') continue
|
if (msg?.type !== 'assistant') continue
|
||||||
const toolUse = (msg.message!.content as any[]).find(
|
const content = msg.message?.content
|
||||||
|
if (!Array.isArray(content)) continue
|
||||||
|
const toolUse = (content as ContentItem[]).find(
|
||||||
block => block.type === 'tool_use' && block.name === TODO_WRITE_TOOL_NAME,
|
block => block.type === 'tool_use' && block.name === TODO_WRITE_TOOL_NAME,
|
||||||
)
|
)
|
||||||
if (!toolUse || toolUse.type !== 'tool_use') continue
|
if (!toolUse || toolUse.type !== 'tool_use') continue
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import type { NonNullableUsage } from '../services/api/logging.js'
|
||||||
import type { Message, SystemAPIErrorMessage } from '../types/message.js'
|
import type { Message, SystemAPIErrorMessage } from '../types/message.js'
|
||||||
import { type CacheSafeParams, runForkedAgent } from './forkedAgent.js'
|
import { type CacheSafeParams, runForkedAgent } from './forkedAgent.js'
|
||||||
import { createUserMessage, extractTextContent } from './messages.js'
|
import { createUserMessage, extractTextContent } from './messages.js'
|
||||||
|
import type { APIError } from '@anthropic-ai/sdk'
|
||||||
|
|
||||||
// Pattern to detect "/btw" at start of input (case-insensitive, word boundary)
|
// Pattern to detect "/btw" at start of input (case-insensitive, word boundary)
|
||||||
const BTW_PATTERN = /^\/btw\b/gi
|
const BTW_PATTERN = /^\/btw\b/gi
|
||||||
|
|
@ -125,7 +126,12 @@ ${question}`
|
||||||
function extractSideQuestionResponse(messages: Message[]): string | null {
|
function extractSideQuestionResponse(messages: Message[]): string | null {
|
||||||
// Flatten all assistant content blocks across the per-block messages.
|
// Flatten all assistant content blocks across the per-block messages.
|
||||||
const assistantBlocks = messages.flatMap(m =>
|
const assistantBlocks = messages.flatMap(m =>
|
||||||
m.type === 'assistant' ? (m.message!.content as unknown as Array<{ type: string; [key: string]: unknown }>) : [],
|
m.type === 'assistant'
|
||||||
|
? (m.message!.content as unknown as Array<{
|
||||||
|
type: string
|
||||||
|
[key: string]: unknown
|
||||||
|
}>)
|
||||||
|
: [],
|
||||||
)
|
)
|
||||||
|
|
||||||
if (assistantBlocks.length > 0) {
|
if (assistantBlocks.length > 0) {
|
||||||
|
|
@ -136,7 +142,8 @@ function extractSideQuestionResponse(messages: Message[]): string | null {
|
||||||
// No text — check if the model tried to call a tool despite instructions.
|
// No text — check if the model tried to call a tool despite instructions.
|
||||||
const toolUse = assistantBlocks.find(b => b.type === 'tool_use')
|
const toolUse = assistantBlocks.find(b => b.type === 'tool_use')
|
||||||
if (toolUse) {
|
if (toolUse) {
|
||||||
const toolName = 'name' in toolUse ? (toolUse as any).name : 'a tool'
|
const toolName =
|
||||||
|
typeof toolUse.name === 'string' ? toolUse.name : 'a tool'
|
||||||
return `(The model tried to call ${toolName} instead of answering directly. Try rephrasing or ask in the main conversation.)`
|
return `(The model tried to call ${toolName} instead of answering directly. Try rephrasing or ask in the main conversation.)`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -148,7 +155,11 @@ function extractSideQuestionResponse(messages: Message[]): string | null {
|
||||||
m.type === 'system' && 'subtype' in m && m.subtype === 'api_error',
|
m.type === 'system' && 'subtype' in m && m.subtype === 'api_error',
|
||||||
)
|
)
|
||||||
if (apiErr) {
|
if (apiErr) {
|
||||||
return `(API error: ${formatAPIError(apiErr.error as any)})`
|
const error =
|
||||||
|
apiErr.error instanceof Error
|
||||||
|
? apiErr.error
|
||||||
|
: new Error(String(apiErr.error))
|
||||||
|
return `(API error: ${formatAPIError(error as APIError)})`
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import {
|
import {
|
||||||
type AnsiCode,
|
type AnsiCode,
|
||||||
|
type Char,
|
||||||
ansiCodesToString,
|
ansiCodesToString,
|
||||||
reduceAnsiCodes,
|
reduceAnsiCodes,
|
||||||
|
type Token,
|
||||||
tokenize,
|
tokenize,
|
||||||
undoAnsiCodes,
|
undoAnsiCodes,
|
||||||
} from '@alcalzone/ansi-tokenize'
|
} from '@alcalzone/ansi-tokenize'
|
||||||
|
|
@ -17,6 +19,10 @@ function filterStartCodes(codes: AnsiCode[]): AnsiCode[] {
|
||||||
return codes.filter(c => !isEndCode(c))
|
return codes.filter(c => !isEndCode(c))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isCharToken(token: Token): token is Char {
|
||||||
|
return token.type === 'char'
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Slice a string containing ANSI escape codes.
|
* Slice a string containing ANSI escape codes.
|
||||||
*
|
*
|
||||||
|
|
@ -43,7 +49,13 @@ export default function sliceAnsi(
|
||||||
// pass start/end in display cells (via stringWidth), so position must
|
// pass start/end in display cells (via stringWidth), so position must
|
||||||
// track the same units.
|
// track the same units.
|
||||||
const width =
|
const width =
|
||||||
token.type === 'ansi' ? 0 : token.type === 'char' ? (token.fullWidth ? 2 : stringWidth(token.value)) : 0
|
token.type === 'ansi'
|
||||||
|
? 0
|
||||||
|
: token.type === 'char'
|
||||||
|
? token.fullWidth
|
||||||
|
? 2
|
||||||
|
: stringWidth(token.value)
|
||||||
|
: 0
|
||||||
|
|
||||||
// Break AFTER trailing zero-width marks — a combining mark attaches to
|
// Break AFTER trailing zero-width marks — a combining mark attaches to
|
||||||
// the preceding base char, so "भा" (भ + ा, 1 display cell) sliced at
|
// the preceding base char, so "भा" (भ + ा, 1 display cell) sliced at
|
||||||
|
|
@ -77,7 +89,7 @@ export default function sliceAnsi(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (include) {
|
if (include) {
|
||||||
result += (token as any).value
|
result += isCharToken(token) ? token.value : token.code
|
||||||
}
|
}
|
||||||
|
|
||||||
position += width
|
position += width
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,12 @@ import {
|
||||||
} from '@alcalzone/ansi-tokenize'
|
} from '@alcalzone/ansi-tokenize'
|
||||||
import type { Theme } from './theme.js'
|
import type { Theme } from './theme.js'
|
||||||
|
|
||||||
|
type TextToken = Extract<Token, { type: 'char' }>
|
||||||
|
|
||||||
|
function isTextToken(token: Token): token is TextToken {
|
||||||
|
return token.type === 'char'
|
||||||
|
}
|
||||||
|
|
||||||
export type TextHighlight = {
|
export type TextHighlight = {
|
||||||
start: number
|
start: number
|
||||||
end: number
|
end: number
|
||||||
|
|
@ -126,19 +132,23 @@ class HighlightSegmenter {
|
||||||
this.codes.push(token)
|
this.codes.push(token)
|
||||||
this.stringPos += token.code.length
|
this.stringPos += token.code.length
|
||||||
this.tokenIdx++
|
this.tokenIdx++
|
||||||
} else {
|
} else if (isTextToken(token)) {
|
||||||
|
const tokenValue = token.value
|
||||||
const charsNeeded = targetVisiblePos - this.visiblePos
|
const charsNeeded = targetVisiblePos - this.visiblePos
|
||||||
const charsAvailable = (token as any).value.length - this.charIdx
|
const charsAvailable = tokenValue.length - this.charIdx
|
||||||
const charsToTake = Math.min(charsNeeded, charsAvailable)
|
const charsToTake = Math.min(charsNeeded, charsAvailable)
|
||||||
|
|
||||||
this.stringPos += charsToTake
|
this.stringPos += charsToTake
|
||||||
this.visiblePos += charsToTake
|
this.visiblePos += charsToTake
|
||||||
this.charIdx += charsToTake
|
this.charIdx += charsToTake
|
||||||
|
|
||||||
if (this.charIdx >= (token as any).value.length) {
|
if (this.charIdx >= tokenValue.length) {
|
||||||
this.tokenIdx++
|
this.tokenIdx++
|
||||||
this.charIdx = 0
|
this.charIdx = 0
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
this.stringPos += token.code.length
|
||||||
|
this.tokenIdx++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1290,10 +1290,10 @@ export async function execIntoTmuxWorktree(args: string[]): Promise<{
|
||||||
worktreeName,
|
worktreeName,
|
||||||
prNumber !== null ? { prNumber } : undefined,
|
prNumber !== null ? { prNumber } : undefined,
|
||||||
)
|
)
|
||||||
if (!result.existed) {
|
if (result.existed === false) {
|
||||||
// biome-ignore lint/suspicious/noConsole: intentional console output
|
// biome-ignore lint/suspicious/noConsole: intentional console output
|
||||||
console.log(
|
console.log(
|
||||||
`Created worktree: ${worktreeDir} (based on ${(result as any).baseBranch})`,
|
`Created worktree: ${worktreeDir} (based on ${result.baseBranch})`,
|
||||||
)
|
)
|
||||||
await performPostCreationSetup(repoRoot, worktreeDir)
|
await performPostCreationSetup(repoRoot, worktreeDir)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user