diff --git a/bun.lock b/bun.lock index 06021cca8..af8cd232e 100644 --- a/bun.lock +++ b/bun.lock @@ -17,7 +17,6 @@ "@anthropic-ai/sandbox-runtime": "^0.0.44", "@anthropic-ai/sdk": "^0.80.0", "@anthropic-ai/vertex-sdk": "^0.14.4", - "@anthropic/ink": "workspace:*", "@aws-sdk/client-bedrock": "^3.1020.0", "@aws-sdk/client-bedrock-runtime": "^3.1020.0", "@aws-sdk/client-sts": "^3.1020.0", @@ -139,29 +138,6 @@ "name": "@ant/computer-use-swift", "version": "1.0.0", }, - "packages/@ant/ink": { - "name": "@anthropic/ink", - "version": "1.0.0", - "dependencies": { - "auto-bind": "^5.0.1", - "bidi-js": "^1.0.3", - "chalk": "^5.6.2", - "cli-boxes": "^4.0.1", - "emoji-regex": "^10.6.0", - "figures": "^6.1.0", - "get-east-asian-width": "^1.5.0", - "indent-string": "^5.0.0", - "lodash-es": "^4.17.23", - "react": "^19.2.4", - "react-reconciler": "^0.33.0", - "signal-exit": "^4.1.0", - "strip-ansi": "^7.2.0", - "supports-hyperlinks": "^4.4.0", - "type-fest": "^5.5.0", - "usehooks-ts": "^3.1.1", - "wrap-ansi": "^10.0.0", - }, - }, "packages/audio-capture-napi": { "name": "audio-capture-napi", "version": "1.0.0", @@ -214,8 +190,6 @@ "@anthropic-ai/vertex-sdk": ["@anthropic-ai/vertex-sdk@0.14.4", "https://registry.npmmirror.com/@anthropic-ai/vertex-sdk/-/vertex-sdk-0.14.4.tgz", { "dependencies": { "@anthropic-ai/sdk": ">=0.50.3 <1", "google-auth-library": "^9.4.2" } }, "sha512-BZUPRWghZxfSFtAxU563wH+jfWBPoedAwsVxG35FhmNsjeV8tyfN+lFriWhCpcZApxA4NdT6Soov+PzfnxxD5g=="], - "@anthropic/ink": ["@anthropic/ink@workspace:packages/@ant/ink"], - "@aws-crypto/crc32": ["@aws-crypto/crc32@5.2.0", "https://registry.npmmirror.com/@aws-crypto/crc32/-/crc32-5.2.0.tgz", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg=="], "@aws-crypto/sha256-browser": ["@aws-crypto/sha256-browser@5.2.0", "https://registry.npmmirror.com/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", { "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", "@aws-crypto/supports-web-crypto": "^5.2.0", "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-locate-window": "^3.0.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw=="], @@ -1224,7 +1198,7 @@ "open": ["open@10.2.0", "https://registry.npmmirror.com/open/-/open-10.2.0.tgz", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="], - "openai": ["openai@6.33.0", "https://registry.npmmirror.com/openai/-/openai-6.33.0.tgz", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.25 || ^4.0" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-xAYN1W3YsDXJWA5F277135YfkEk6H7D3D6vWwRhJ3OEkzRgcyK8z/P5P9Gyi/wB4N8kK9kM5ZjprfvyHagKmpw=="], + "openai": ["openai@6.33.0", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.25 || ^4.0" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-xAYN1W3YsDXJWA5F277135YfkEk6H7D3D6vWwRhJ3OEkzRgcyK8z/P5P9Gyi/wB4N8kK9kM5ZjprfvyHagKmpw=="], "os-tmpdir": ["os-tmpdir@1.0.2", "https://registry.npmmirror.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz", {}, "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g=="], diff --git a/package.json b/package.json index 586b78fbe..6d066da69 100644 --- a/package.json +++ b/package.json @@ -131,7 +131,6 @@ "highlight.js": "^11.11.1", "https-proxy-agent": "^8.0.0", "ignore": "^7.0.5", - "@anthropic/ink": "workspace:*", "image-processor-napi": "workspace:*", "indent-string": "^5.0.0", "jsonc-parser": "^3.3.1", diff --git a/packages/@ant/ink/package.json b/packages/@ant/ink/package.json deleted file mode 100644 index aec1f4eeb..000000000 --- a/packages/@ant/ink/package.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "@anthropic/ink", - "version": "1.0.0", - "private": true, - "type": "module", - "main": "./src/index.ts", - "types": "./src/index.ts", - "exports": { - ".": "./src/index.ts" - }, - "dependencies": { - "auto-bind": "^5.0.1", - "bidi-js": "^1.0.3", - "chalk": "^5.6.2", - "cli-boxes": "^4.0.1", - "emoji-regex": "^10.6.0", - "figures": "^6.1.0", - "get-east-asian-width": "^1.5.0", - "indent-string": "^5.0.0", - "lodash-es": "^4.17.23", - "react": "^19.2.4", - "react-reconciler": "^0.33.0", - "signal-exit": "^4.1.0", - "strip-ansi": "^7.2.0", - "supports-hyperlinks": "^4.4.0", - "type-fest": "^5.5.0", - "usehooks-ts": "^3.1.1", - "wrap-ansi": "^10.0.0" - } -} diff --git a/packages/@ant/ink/src/core/utils/grapheme.ts b/packages/@ant/ink/src/core/utils/grapheme.ts deleted file mode 100644 index e010dc664..000000000 --- a/packages/@ant/ink/src/core/utils/grapheme.ts +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Shared Intl object instances with lazy initialization. - * - * Intl constructors are expensive (~0.05-0.1ms each), so we cache instances - * for reuse across the codebase instead of creating new ones each time. - * Lazy initialization ensures we only pay the cost when actually needed. - * - * Vendored from src/utils/intl.ts for package independence. - */ - -// Segmenters for Unicode text processing (lazily initialized) -let graphemeSegmenter: Intl.Segmenter | null = null -let wordSegmenter: Intl.Segmenter | null = null - -export function getGraphemeSegmenter(): Intl.Segmenter { - if (!graphemeSegmenter) { - graphemeSegmenter = new Intl.Segmenter(undefined, { - granularity: 'grapheme', - }) - } - return graphemeSegmenter -} - -/** - * Extract the first grapheme cluster from a string. - * Returns '' for empty strings. - */ -export function firstGrapheme(text: string): string { - if (!text) return '' - const segments = getGraphemeSegmenter().segment(text) - const first = segments[Symbol.iterator]().next().value - return first?.segment ?? '' -} - -/** - * Extract the last grapheme cluster from a string. - * Returns '' for empty strings. - */ -export function lastGrapheme(text: string): string { - if (!text) return '' - let last = '' - for (const { segment } of getGraphemeSegmenter().segment(text)) { - last = segment - } - return last -} - -export function getWordSegmenter(): Intl.Segmenter { - if (!wordSegmenter) { - wordSegmenter = new Intl.Segmenter(undefined, { granularity: 'word' }) - } - return wordSegmenter -} diff --git a/packages/@ant/ink/src/core/utils/sliceAnsi.ts b/packages/@ant/ink/src/core/utils/sliceAnsi.ts deleted file mode 100644 index 42abe7ac7..000000000 --- a/packages/@ant/ink/src/core/utils/sliceAnsi.ts +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Slice a string containing ANSI escape codes. - * - * Vendored from src/utils/sliceAnsi.ts for package independence. - * The only external dependency is stringWidth from the core package. - */ -import { - type AnsiCode, - ansiCodesToString, - reduceAnsiCodes, - tokenize, - undoAnsiCodes, -} from '@alcalzone/ansi-tokenize' -import { stringWidth } from '../stringWidth.js' - -// A code is an "end code" if its code equals its endCode (e.g., hyperlink close) -function isEndCode(code: AnsiCode): boolean { - return code.code === code.endCode -} - -// Filter to only include "start codes" (not end codes) -function filterStartCodes(codes: AnsiCode[]): AnsiCode[] { - return codes.filter(c => !isEndCode(c)) -} - -/** - * Slice a string containing ANSI escape codes. - * - * Unlike the slice-ansi package, this properly handles OSC 8 hyperlink - * sequences because @alcalzone/ansi-tokenize tokenizes them correctly. - */ -export default function sliceAnsi( - str: string, - start: number, - end?: number, -): string { - // Don't pass `end` to tokenize — it counts code units, not display cells, - // so it drops tokens early for text with zero-width combining marks. - const tokens = tokenize(str) - let activeCodes: AnsiCode[] = [] - let position = 0 - let result = '' - let include = false - - for (const token of tokens) { - // Advance by display width, not code units. Combining marks (Devanagari - // matras, virama, diacritics) are width 0 — counting them via .length - // advanced position past `end` early and truncated the slice. Callers - // pass start/end in display cells (via stringWidth), so position must - // track the same units. - const width = - 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 - // the preceding base char, so "भा" (भ + ा, 1 display cell) sliced at - // end=1 must include the ा. Breaking on position >= end BEFORE the - // zero-width check would drop it and render भ bare. ANSI codes are - // width 0 but must NOT be included past end (they open new style runs - // that leak into the undo sequence), so gate on char type too. The - // !include guard ensures empty slices (start===end) stay empty even - // when the string starts with a zero-width char (BOM, ZWJ). - if (end !== undefined && position >= end) { - if (token.type === 'ansi' || width > 0 || !include) break - } - - if (token.type === 'ansi') { - activeCodes.push(token) - if (include) { - // Emit all ANSI codes during the slice - result += token.code - } - } else { - if (!include && position >= start) { - // Skip leading zero-width marks at the start boundary — they belong - // to the preceding base char in the left half. Without this, the - // mark appears in BOTH halves: left+right ≠ original. Only applies - // when start > 0 (otherwise there's no preceding char to own it). - if (start > 0 && width === 0) continue - include = true - // Reduce and filter to only active start codes - activeCodes = filterStartCodes(reduceAnsiCodes(activeCodes)) - result = ansiCodesToString(activeCodes) - } - - if (include) { - result += (token as any).value - } - - position += width - } - } - - // Only undo start codes that are still active - const activeStartCodes = filterStartCodes(reduceAnsiCodes(activeCodes)) - result += ansiCodesToString(undoAnsiCodes(activeStartCodes)) - return result -} diff --git a/packages/@ant/ink/src/core/yoga-layout/enums.ts b/packages/@ant/ink/src/core/yoga-layout/enums.ts deleted file mode 100644 index 8cbb6ecff..000000000 --- a/packages/@ant/ink/src/core/yoga-layout/enums.ts +++ /dev/null @@ -1,134 +0,0 @@ -/** - * Yoga enums — ported from yoga-layout/src/generated/YGEnums.ts - * Kept as `const` objects (not TS enums) per repo convention. - * Values match upstream exactly so callers don't change. - */ - -export const Align = { - Auto: 0, - FlexStart: 1, - Center: 2, - FlexEnd: 3, - Stretch: 4, - Baseline: 5, - SpaceBetween: 6, - SpaceAround: 7, - SpaceEvenly: 8, -} as const -export type Align = (typeof Align)[keyof typeof Align] - -export const BoxSizing = { - BorderBox: 0, - ContentBox: 1, -} as const -export type BoxSizing = (typeof BoxSizing)[keyof typeof BoxSizing] - -export const Dimension = { - Width: 0, - Height: 1, -} as const -export type Dimension = (typeof Dimension)[keyof typeof Dimension] - -export const Direction = { - Inherit: 0, - LTR: 1, - RTL: 2, -} as const -export type Direction = (typeof Direction)[keyof typeof Direction] - -export const Display = { - Flex: 0, - None: 1, - Contents: 2, -} as const -export type Display = (typeof Display)[keyof typeof Display] - -export const Edge = { - Left: 0, - Top: 1, - Right: 2, - Bottom: 3, - Start: 4, - End: 5, - Horizontal: 6, - Vertical: 7, - All: 8, -} as const -export type Edge = (typeof Edge)[keyof typeof Edge] - -export const Errata = { - None: 0, - StretchFlexBasis: 1, - AbsolutePositionWithoutInsetsExcludesPadding: 2, - AbsolutePercentAgainstInnerSize: 4, - All: 2147483647, - Classic: 2147483646, -} as const -export type Errata = (typeof Errata)[keyof typeof Errata] - -export const ExperimentalFeature = { - WebFlexBasis: 0, -} as const -export type ExperimentalFeature = - (typeof ExperimentalFeature)[keyof typeof ExperimentalFeature] - -export const FlexDirection = { - Column: 0, - ColumnReverse: 1, - Row: 2, - RowReverse: 3, -} as const -export type FlexDirection = (typeof FlexDirection)[keyof typeof FlexDirection] - -export const Gutter = { - Column: 0, - Row: 1, - All: 2, -} as const -export type Gutter = (typeof Gutter)[keyof typeof Gutter] - -export const Justify = { - FlexStart: 0, - Center: 1, - FlexEnd: 2, - SpaceBetween: 3, - SpaceAround: 4, - SpaceEvenly: 5, -} as const -export type Justify = (typeof Justify)[keyof typeof Justify] - -export const MeasureMode = { - Undefined: 0, - Exactly: 1, - AtMost: 2, -} as const -export type MeasureMode = (typeof MeasureMode)[keyof typeof MeasureMode] - -export const Overflow = { - Visible: 0, - Hidden: 1, - Scroll: 2, -} as const -export type Overflow = (typeof Overflow)[keyof typeof Overflow] - -export const PositionType = { - Static: 0, - Relative: 1, - Absolute: 2, -} as const -export type PositionType = (typeof PositionType)[keyof typeof PositionType] - -export const Unit = { - Undefined: 0, - Point: 1, - Percent: 2, - Auto: 3, -} as const -export type Unit = (typeof Unit)[keyof typeof Unit] - -export const Wrap = { - NoWrap: 0, - Wrap: 1, - WrapReverse: 2, -} as const -export type Wrap = (typeof Wrap)[keyof typeof Wrap] diff --git a/packages/@ant/ink/src/core/yoga-layout/index.ts b/packages/@ant/ink/src/core/yoga-layout/index.ts deleted file mode 100644 index 49b9602be..000000000 --- a/packages/@ant/ink/src/core/yoga-layout/index.ts +++ /dev/null @@ -1,2578 +0,0 @@ -/** - * Pure-TypeScript port of yoga-layout (Meta's flexbox engine). - * - * This matches the `yoga-layout/load` API surface used by src/ink/layout/yoga.ts. - * The upstream C++ source is ~2500 lines in CalculateLayout.cpp alone; this port - * is a simplified single-pass flexbox implementation that covers the subset of - * features Ink actually uses: - * - flex-direction (row/column + reverse) - * - flex-grow / flex-shrink / flex-basis - * - align-items / align-self (stretch, flex-start, center, flex-end) - * - justify-content (all six values) - * - margin / padding / border / gap - * - width / height / min / max (point, percent, auto) - * - position: relative / absolute - * - display: flex / none - * - measure functions (for text nodes) - * - * Also implemented for spec parity (not used by Ink): - * - margin: auto (main + cross axis, overrides justify/align) - * - multi-pass flex clamping when children hit min/max constraints - * - flex-grow/shrink against container min/max when size is indefinite - * - * Also implemented for spec parity (not used by Ink): - * - flex-wrap: wrap / wrap-reverse (multi-line flex) - * - align-content (positions wrapped lines on cross axis) - * - * Also implemented for spec parity (not used by Ink): - * - display: contents (children lifted to grandparent, box removed) - * - * Also implemented for spec parity (not used by Ink): - * - baseline alignment (align-items/align-self: baseline) - * - * Not implemented (not used by Ink): - * - aspect-ratio - * - box-sizing: content-box - * - RTL direction (Ink always passes Direction.LTR) - * - * Upstream: https://github.com/facebook/yoga - */ - -import { - Align, - BoxSizing, - Dimension, - Direction, - Display, - Edge, - Errata, - ExperimentalFeature, - FlexDirection, - Gutter, - Justify, - MeasureMode, - Overflow, - PositionType, - Unit, - Wrap, -} from './enums.js' - -export { - Align, - BoxSizing, - Dimension, - Direction, - Display, - Edge, - Errata, - ExperimentalFeature, - FlexDirection, - Gutter, - Justify, - MeasureMode, - Overflow, - PositionType, - Unit, - Wrap, -} - -// -- -// Value types - -export type Value = { - unit: Unit - value: number -} - -const UNDEFINED_VALUE: Value = { unit: Unit.Undefined, value: NaN } -const AUTO_VALUE: Value = { unit: Unit.Auto, value: NaN } - -function pointValue(v: number): Value { - return { unit: Unit.Point, value: v } -} -function percentValue(v: number): Value { - return { unit: Unit.Percent, value: v } -} - -function resolveValue(v: Value, ownerSize: number): number { - switch (v.unit) { - case Unit.Point: - return v.value - case Unit.Percent: - return isNaN(ownerSize) ? NaN : (v.value * ownerSize) / 100 - default: - return NaN - } -} - -function isDefined(n: number): boolean { - return !isNaN(n) -} - -// NaN-safe equality for layout-cache input comparison -function sameFloat(a: number, b: number): boolean { - return a === b || (a !== a && b !== b) -} - -// -- -// Layout result (computed values) - -type Layout = { - left: number - top: number - width: number - height: number - // Computed per-edge values (resolved to physical edges) - border: [number, number, number, number] // left, top, right, bottom - padding: [number, number, number, number] - margin: [number, number, number, number] -} - -// -- -// Style (input values) - -type Style = { - direction: Direction - flexDirection: FlexDirection - justifyContent: Justify - alignItems: Align - alignSelf: Align - alignContent: Align - flexWrap: Wrap - overflow: Overflow - display: Display - positionType: PositionType - - flexGrow: number - flexShrink: number - flexBasis: Value - - // 9-edge arrays indexed by Edge enum - margin: Value[] - padding: Value[] - border: Value[] - position: Value[] - - // 3-gutter array indexed by Gutter enum - gap: Value[] - - width: Value - height: Value - minWidth: Value - minHeight: Value - maxWidth: Value - maxHeight: Value -} - -function defaultStyle(): Style { - return { - direction: Direction.Inherit, - flexDirection: FlexDirection.Column, - justifyContent: Justify.FlexStart, - alignItems: Align.Stretch, - alignSelf: Align.Auto, - alignContent: Align.FlexStart, - flexWrap: Wrap.NoWrap, - overflow: Overflow.Visible, - display: Display.Flex, - positionType: PositionType.Relative, - flexGrow: 0, - flexShrink: 0, - flexBasis: AUTO_VALUE, - margin: new Array(9).fill(UNDEFINED_VALUE), - padding: new Array(9).fill(UNDEFINED_VALUE), - border: new Array(9).fill(UNDEFINED_VALUE), - position: new Array(9).fill(UNDEFINED_VALUE), - gap: new Array(3).fill(UNDEFINED_VALUE), - width: AUTO_VALUE, - height: AUTO_VALUE, - minWidth: UNDEFINED_VALUE, - minHeight: UNDEFINED_VALUE, - maxWidth: UNDEFINED_VALUE, - maxHeight: UNDEFINED_VALUE, - } -} - -// -- -// Edge resolution — yoga's 9-edge model collapsed to 4 physical edges - -const EDGE_LEFT = 0 -const EDGE_TOP = 1 -const EDGE_RIGHT = 2 -const EDGE_BOTTOM = 3 - -function resolveEdge( - edges: Value[], - physicalEdge: number, - ownerSize: number, - // For margin/position we allow auto; for padding/border auto resolves to 0 - allowAuto = false, -): number { - // Precedence: specific edge > horizontal/vertical > all - let v = edges[physicalEdge]! - if (v.unit === Unit.Undefined) { - if (physicalEdge === EDGE_LEFT || physicalEdge === EDGE_RIGHT) { - v = edges[Edge.Horizontal]! - } else { - v = edges[Edge.Vertical]! - } - } - if (v.unit === Unit.Undefined) { - v = edges[Edge.All]! - } - // Start/End map to Left/Right for LTR (Ink is always LTR) - if (v.unit === Unit.Undefined) { - if (physicalEdge === EDGE_LEFT) v = edges[Edge.Start]! - if (physicalEdge === EDGE_RIGHT) v = edges[Edge.End]! - } - if (v.unit === Unit.Undefined) return 0 - if (v.unit === Unit.Auto) return allowAuto ? NaN : 0 - return resolveValue(v, ownerSize) -} - -function resolveEdgeRaw(edges: Value[], physicalEdge: number): Value { - let v = edges[physicalEdge]! - if (v.unit === Unit.Undefined) { - if (physicalEdge === EDGE_LEFT || physicalEdge === EDGE_RIGHT) { - v = edges[Edge.Horizontal]! - } else { - v = edges[Edge.Vertical]! - } - } - if (v.unit === Unit.Undefined) v = edges[Edge.All]! - if (v.unit === Unit.Undefined) { - if (physicalEdge === EDGE_LEFT) v = edges[Edge.Start]! - if (physicalEdge === EDGE_RIGHT) v = edges[Edge.End]! - } - return v -} - -function isMarginAuto(edges: Value[], physicalEdge: number): boolean { - return resolveEdgeRaw(edges, physicalEdge).unit === Unit.Auto -} - -// Setter helpers for the _hasAutoMargin / _hasPosition fast-path flags. -// Unit.Undefined = 0, Unit.Auto = 3. -function hasAnyAutoEdge(edges: Value[]): boolean { - for (let i = 0; i < 9; i++) if (edges[i]!.unit === 3) return true - return false -} -function hasAnyDefinedEdge(edges: Value[]): boolean { - for (let i = 0; i < 9; i++) if (edges[i]!.unit !== 0) return true - return false -} - -// Hot path: resolve all 4 physical edges in one pass, writing into `out`. -// Equivalent to calling resolveEdge() 4× with allowAuto=false, but hoists the -// shared fallback lookups (Horizontal/Vertical/All/Start/End) and avoids -// allocating a fresh 4-array on every layoutNode() call. -function resolveEdges4Into( - edges: Value[], - ownerSize: number, - out: [number, number, number, number], -): void { - // Hoist fallbacks once — the 4 per-edge chains share these reads. - const eH = edges[6]! // Edge.Horizontal - const eV = edges[7]! // Edge.Vertical - const eA = edges[8]! // Edge.All - const eS = edges[4]! // Edge.Start - const eE = edges[5]! // Edge.End - const pctDenom = isNaN(ownerSize) ? NaN : ownerSize / 100 - - // Left: edges[0] → Horizontal → All → Start - let v = edges[0]! - if (v.unit === 0) v = eH - if (v.unit === 0) v = eA - if (v.unit === 0) v = eS - out[0] = v.unit === 1 ? v.value : v.unit === 2 ? v.value * pctDenom : 0 - - // Top: edges[1] → Vertical → All - v = edges[1]! - if (v.unit === 0) v = eV - if (v.unit === 0) v = eA - out[1] = v.unit === 1 ? v.value : v.unit === 2 ? v.value * pctDenom : 0 - - // Right: edges[2] → Horizontal → All → End - v = edges[2]! - if (v.unit === 0) v = eH - if (v.unit === 0) v = eA - if (v.unit === 0) v = eE - out[2] = v.unit === 1 ? v.value : v.unit === 2 ? v.value * pctDenom : 0 - - // Bottom: edges[3] → Vertical → All - v = edges[3]! - if (v.unit === 0) v = eV - if (v.unit === 0) v = eA - out[3] = v.unit === 1 ? v.value : v.unit === 2 ? v.value * pctDenom : 0 -} - -// -- -// Axis helpers - -function isRow(dir: FlexDirection): boolean { - return dir === FlexDirection.Row || dir === FlexDirection.RowReverse -} -function isReverse(dir: FlexDirection): boolean { - return dir === FlexDirection.RowReverse || dir === FlexDirection.ColumnReverse -} -function crossAxis(dir: FlexDirection): FlexDirection { - return isRow(dir) ? FlexDirection.Column : FlexDirection.Row -} -function leadingEdge(dir: FlexDirection): number { - switch (dir) { - case FlexDirection.Row: - return EDGE_LEFT - case FlexDirection.RowReverse: - return EDGE_RIGHT - case FlexDirection.Column: - return EDGE_TOP - case FlexDirection.ColumnReverse: - return EDGE_BOTTOM - } -} -function trailingEdge(dir: FlexDirection): number { - switch (dir) { - case FlexDirection.Row: - return EDGE_RIGHT - case FlexDirection.RowReverse: - return EDGE_LEFT - case FlexDirection.Column: - return EDGE_BOTTOM - case FlexDirection.ColumnReverse: - return EDGE_TOP - } -} - -// -- -// Public types - -export type MeasureFunction = ( - width: number, - widthMode: MeasureMode, - height: number, - heightMode: MeasureMode, -) => { width: number; height: number } - -export type Size = { width: number; height: number } - -// -- -// Config - -export type Config = { - pointScaleFactor: number - errata: Errata - useWebDefaults: boolean - free(): void - isExperimentalFeatureEnabled(_: ExperimentalFeature): boolean - setExperimentalFeatureEnabled(_: ExperimentalFeature, __: boolean): void - setPointScaleFactor(factor: number): void - getErrata(): Errata - setErrata(errata: Errata): void - setUseWebDefaults(v: boolean): void -} - -function createConfig(): Config { - const config: Config = { - pointScaleFactor: 1, - errata: Errata.None, - useWebDefaults: false, - free() {}, - isExperimentalFeatureEnabled() { - return false - }, - setExperimentalFeatureEnabled() {}, - setPointScaleFactor(f) { - config.pointScaleFactor = f - }, - getErrata() { - return config.errata - }, - setErrata(e) { - config.errata = e - }, - setUseWebDefaults(v) { - config.useWebDefaults = v - }, - } - return config -} - -// -- -// Node implementation - -export class Node { - style: Style - layout: Layout - parent: Node | null - children: Node[] - measureFunc: MeasureFunction | null - config: Config - isDirty_: boolean - isReferenceBaseline_: boolean - - // Per-layout scratch (not public API) - _flexBasis = 0 - _mainSize = 0 - _crossSize = 0 - _lineIndex = 0 - // Fast-path flags maintained by style setters. Per CPU profile, the - // positioning loop calls isMarginAuto 6× and resolveEdgeRaw(position) 4× - // per child per layout pass — ~11k calls for the 1000-node bench, nearly - // all of which return false/undefined since most nodes have no auto - // margins and no position insets. These flags let us skip straight to - // the common case with a single branch. - _hasAutoMargin = false - _hasPosition = false - // Same pattern for the 3× resolveEdges4Into calls at the top of every - // layoutNode(). In the 1000-node bench ~67% of those calls operate on - // all-undefined edge arrays (most nodes have no border; only cols have - // padding; only leaf cells have margin) — a single-branch skip beats - // ~20 property reads + ~15 compares + 4 writes of zeros. - _hasPadding = false - _hasBorder = false - _hasMargin = false - // -- Dirty-flag layout cache. Mirrors upstream CalculateLayout.cpp's - // layoutNodeInternal: skip a subtree entirely when it's clean and we're - // asking the same question we cached the answer to. Two slots since - // each node typically sees a measure call (performLayout=false, from - // computeFlexBasis) followed by a layout call (performLayout=true) with - // different inputs per parent pass — a single slot thrashes. Re-layout - // bench (dirty one leaf, recompute root) went 2.7x→1.1x with this: - // clean siblings skip straight through, only the dirty chain recomputes. - _lW = NaN - _lH = NaN - _lWM: MeasureMode = 0 - _lHM: MeasureMode = 0 - _lOW = NaN - _lOH = NaN - _lFW = false - _lFH = false - // _hasL stores INPUTS early (before compute) but layout.width/height are - // mutated by the multi-entry cache and by subsequent compute calls with - // different inputs. Without storing OUTPUTS, a _hasL hit returns whatever - // layout.width/height happened to be left by the last call — the scrollbox - // vpH=33→2624 bug. Store + restore outputs like the multi-entry cache does. - _lOutW = NaN - _lOutH = NaN - _hasL = false - _mW = NaN - _mH = NaN - _mWM: MeasureMode = 0 - _mHM: MeasureMode = 0 - _mOW = NaN - _mOH = NaN - _mOutW = NaN - _mOutH = NaN - _hasM = false - // Cached computeFlexBasis result. For clean children, basis only depends - // on the container's inner dimensions — if those haven't changed, skip the - // layoutNode(performLayout=false) recursion entirely. This is the hot path - // for scroll: 500-message content container is dirty, its 499 clean - // children each get measured ~20× as the dirty chain's measure/layout - // passes cascade. Basis cache short-circuits at the child boundary. - _fbBasis = NaN - _fbOwnerW = NaN - _fbOwnerH = NaN - _fbAvailMain = NaN - _fbAvailCross = NaN - _fbCrossMode: MeasureMode = 0 - // Generation at which _fbBasis was written. Dirty nodes from a PREVIOUS - // generation have stale cache (subtree changed), but within the SAME - // generation the cache is fresh — the dirty chain's measure→layout - // cascade invokes computeFlexBasis ≥2^depth times per calculateLayout on - // fresh-mounted items, and the subtree doesn't change between calls. - // Gating on generation instead of isDirty_ lets fresh mounts (virtual - // scroll) cache-hit after first compute: 105k visits → ~10k. - _fbGen = -1 - // Multi-entry layout cache — stores (inputs → computed w,h) so hits with - // different inputs than _hasL can restore the right dimensions. Upstream - // yoga uses 16; 4 covers Ink's dirty-chain depth. Packed as flat arrays - // to avoid per-entry object allocs. Slot i uses indices [i*8, i*8+8) in - // _cIn (aW,aH,wM,hM,oW,oH,fW,fH) and [i*2, i*2+2) in _cOut (w,h). - _cIn: Float64Array | null = null - _cOut: Float64Array | null = null - _cGen = -1 - _cN = 0 - _cWr = 0 - - constructor(config?: Config) { - this.style = defaultStyle() - this.layout = { - left: 0, - top: 0, - width: 0, - height: 0, - border: [0, 0, 0, 0], - padding: [0, 0, 0, 0], - margin: [0, 0, 0, 0], - } - this.parent = null - this.children = [] - this.measureFunc = null - this.config = config ?? DEFAULT_CONFIG - this.isDirty_ = true - this.isReferenceBaseline_ = false - _yogaLiveNodes++ - } - - // -- Tree - - insertChild(child: Node, index: number): void { - child.parent = this - this.children.splice(index, 0, child) - this.markDirty() - } - removeChild(child: Node): void { - const idx = this.children.indexOf(child) - if (idx >= 0) { - this.children.splice(idx, 1) - child.parent = null - this.markDirty() - } - } - getChild(index: number): Node { - return this.children[index]! - } - getChildCount(): number { - return this.children.length - } - getParent(): Node | null { - return this.parent - } - - // -- Lifecycle - - free(): void { - this.parent = null - this.children = [] - this.measureFunc = null - this._cIn = null - this._cOut = null - _yogaLiveNodes-- - } - freeRecursive(): void { - for (const c of this.children) c.freeRecursive() - this.free() - } - reset(): void { - this.style = defaultStyle() - this.children = [] - this.parent = null - this.measureFunc = null - this.isDirty_ = true - this._hasAutoMargin = false - this._hasPosition = false - this._hasPadding = false - this._hasBorder = false - this._hasMargin = false - this._hasL = false - this._hasM = false - this._cN = 0 - this._cWr = 0 - this._fbBasis = NaN - } - - // -- Dirty tracking - - markDirty(): void { - this.isDirty_ = true - if (this.parent && !this.parent.isDirty_) this.parent.markDirty() - } - isDirty(): boolean { - return this.isDirty_ - } - hasNewLayout(): boolean { - return true - } - markLayoutSeen(): void {} - - // -- Measure function - - setMeasureFunc(fn: MeasureFunction | null): void { - this.measureFunc = fn - this.markDirty() - } - unsetMeasureFunc(): void { - this.measureFunc = null - this.markDirty() - } - - // -- Computed layout getters - - getComputedLeft(): number { - return this.layout.left - } - getComputedTop(): number { - return this.layout.top - } - getComputedWidth(): number { - return this.layout.width - } - getComputedHeight(): number { - return this.layout.height - } - getComputedRight(): number { - const p = this.parent - return p ? p.layout.width - this.layout.left - this.layout.width : 0 - } - getComputedBottom(): number { - const p = this.parent - return p ? p.layout.height - this.layout.top - this.layout.height : 0 - } - getComputedLayout(): { - left: number - top: number - right: number - bottom: number - width: number - height: number - } { - return { - left: this.layout.left, - top: this.layout.top, - right: this.getComputedRight(), - bottom: this.getComputedBottom(), - width: this.layout.width, - height: this.layout.height, - } - } - getComputedBorder(edge: Edge): number { - return this.layout.border[physicalEdge(edge)]! - } - getComputedPadding(edge: Edge): number { - return this.layout.padding[physicalEdge(edge)]! - } - getComputedMargin(edge: Edge): number { - return this.layout.margin[physicalEdge(edge)]! - } - - // -- Style setters: dimensions - - setWidth(v: number | 'auto' | string | undefined): void { - this.style.width = parseDimension(v) - this.markDirty() - } - setWidthPercent(v: number): void { - this.style.width = percentValue(v) - this.markDirty() - } - setWidthAuto(): void { - this.style.width = AUTO_VALUE - this.markDirty() - } - setHeight(v: number | 'auto' | string | undefined): void { - this.style.height = parseDimension(v) - this.markDirty() - } - setHeightPercent(v: number): void { - this.style.height = percentValue(v) - this.markDirty() - } - setHeightAuto(): void { - this.style.height = AUTO_VALUE - this.markDirty() - } - setMinWidth(v: number | string | undefined): void { - this.style.minWidth = parseDimension(v) - this.markDirty() - } - setMinWidthPercent(v: number): void { - this.style.minWidth = percentValue(v) - this.markDirty() - } - setMinHeight(v: number | string | undefined): void { - this.style.minHeight = parseDimension(v) - this.markDirty() - } - setMinHeightPercent(v: number): void { - this.style.minHeight = percentValue(v) - this.markDirty() - } - setMaxWidth(v: number | string | undefined): void { - this.style.maxWidth = parseDimension(v) - this.markDirty() - } - setMaxWidthPercent(v: number): void { - this.style.maxWidth = percentValue(v) - this.markDirty() - } - setMaxHeight(v: number | string | undefined): void { - this.style.maxHeight = parseDimension(v) - this.markDirty() - } - setMaxHeightPercent(v: number): void { - this.style.maxHeight = percentValue(v) - this.markDirty() - } - - // -- Style setters: flex - - setFlexDirection(dir: FlexDirection): void { - this.style.flexDirection = dir - this.markDirty() - } - setFlexGrow(v: number | undefined): void { - this.style.flexGrow = v ?? 0 - this.markDirty() - } - setFlexShrink(v: number | undefined): void { - this.style.flexShrink = v ?? 0 - this.markDirty() - } - setFlex(v: number | undefined): void { - if (v === undefined || isNaN(v)) { - this.style.flexGrow = 0 - this.style.flexShrink = 0 - } else if (v > 0) { - this.style.flexGrow = v - this.style.flexShrink = 1 - this.style.flexBasis = pointValue(0) - } else if (v < 0) { - this.style.flexGrow = 0 - this.style.flexShrink = -v - } else { - this.style.flexGrow = 0 - this.style.flexShrink = 0 - } - this.markDirty() - } - setFlexBasis(v: number | 'auto' | string | undefined): void { - this.style.flexBasis = parseDimension(v) - this.markDirty() - } - setFlexBasisPercent(v: number): void { - this.style.flexBasis = percentValue(v) - this.markDirty() - } - setFlexBasisAuto(): void { - this.style.flexBasis = AUTO_VALUE - this.markDirty() - } - setFlexWrap(wrap: Wrap): void { - this.style.flexWrap = wrap - this.markDirty() - } - - // -- Style setters: alignment - - setAlignItems(a: Align): void { - this.style.alignItems = a - this.markDirty() - } - setAlignSelf(a: Align): void { - this.style.alignSelf = a - this.markDirty() - } - setAlignContent(a: Align): void { - this.style.alignContent = a - this.markDirty() - } - setJustifyContent(j: Justify): void { - this.style.justifyContent = j - this.markDirty() - } - - // -- Style setters: display / position / overflow - - setDisplay(d: Display): void { - this.style.display = d - this.markDirty() - } - getDisplay(): Display { - return this.style.display - } - setPositionType(t: PositionType): void { - this.style.positionType = t - this.markDirty() - } - setPosition(edge: Edge, v: number | string | undefined): void { - this.style.position[edge] = parseDimension(v) - this._hasPosition = hasAnyDefinedEdge(this.style.position) - this.markDirty() - } - setPositionPercent(edge: Edge, v: number): void { - this.style.position[edge] = percentValue(v) - this._hasPosition = true - this.markDirty() - } - setPositionAuto(edge: Edge): void { - this.style.position[edge] = AUTO_VALUE - this._hasPosition = true - this.markDirty() - } - setOverflow(o: Overflow): void { - this.style.overflow = o - this.markDirty() - } - setDirection(d: Direction): void { - this.style.direction = d - this.markDirty() - } - setBoxSizing(_: BoxSizing): void { - // Not implemented — Ink doesn't use content-box - } - - // -- Style setters: spacing - - setMargin(edge: Edge, v: number | 'auto' | string | undefined): void { - const val = parseDimension(v) - this.style.margin[edge] = val - if (val.unit === Unit.Auto) this._hasAutoMargin = true - else this._hasAutoMargin = hasAnyAutoEdge(this.style.margin) - this._hasMargin = - this._hasAutoMargin || hasAnyDefinedEdge(this.style.margin) - this.markDirty() - } - setMarginPercent(edge: Edge, v: number): void { - this.style.margin[edge] = percentValue(v) - this._hasAutoMargin = hasAnyAutoEdge(this.style.margin) - this._hasMargin = true - this.markDirty() - } - setMarginAuto(edge: Edge): void { - this.style.margin[edge] = AUTO_VALUE - this._hasAutoMargin = true - this._hasMargin = true - this.markDirty() - } - setPadding(edge: Edge, v: number | string | undefined): void { - this.style.padding[edge] = parseDimension(v) - this._hasPadding = hasAnyDefinedEdge(this.style.padding) - this.markDirty() - } - setPaddingPercent(edge: Edge, v: number): void { - this.style.padding[edge] = percentValue(v) - this._hasPadding = true - this.markDirty() - } - setBorder(edge: Edge, v: number | undefined): void { - this.style.border[edge] = v === undefined ? UNDEFINED_VALUE : pointValue(v) - this._hasBorder = hasAnyDefinedEdge(this.style.border) - this.markDirty() - } - setGap(gutter: Gutter, v: number | string | undefined): void { - this.style.gap[gutter] = parseDimension(v) - this.markDirty() - } - setGapPercent(gutter: Gutter, v: number): void { - this.style.gap[gutter] = percentValue(v) - this.markDirty() - } - - // -- Style getters (partial — only what tests need) - - getFlexDirection(): FlexDirection { - return this.style.flexDirection - } - getJustifyContent(): Justify { - return this.style.justifyContent - } - getAlignItems(): Align { - return this.style.alignItems - } - getAlignSelf(): Align { - return this.style.alignSelf - } - getAlignContent(): Align { - return this.style.alignContent - } - getFlexGrow(): number { - return this.style.flexGrow - } - getFlexShrink(): number { - return this.style.flexShrink - } - getFlexBasis(): Value { - return this.style.flexBasis - } - getFlexWrap(): Wrap { - return this.style.flexWrap - } - getWidth(): Value { - return this.style.width - } - getHeight(): Value { - return this.style.height - } - getOverflow(): Overflow { - return this.style.overflow - } - getPositionType(): PositionType { - return this.style.positionType - } - getDirection(): Direction { - return this.style.direction - } - - // -- Unused API stubs (present for API parity) - - copyStyle(_: Node): void {} - setDirtiedFunc(_: unknown): void {} - unsetDirtiedFunc(): void {} - setIsReferenceBaseline(v: boolean): void { - this.isReferenceBaseline_ = v - this.markDirty() - } - isReferenceBaseline(): boolean { - return this.isReferenceBaseline_ - } - setAspectRatio(_: number | undefined): void {} - getAspectRatio(): number { - return NaN - } - setAlwaysFormsContainingBlock(_: boolean): void {} - - // -- Layout entry point - - calculateLayout( - ownerWidth: number | undefined, - ownerHeight: number | undefined, - _direction?: Direction, - ): void { - _yogaNodesVisited = 0 - _yogaMeasureCalls = 0 - _yogaCacheHits = 0 - _generation++ - const w = ownerWidth === undefined ? NaN : ownerWidth - const h = ownerHeight === undefined ? NaN : ownerHeight - layoutNode( - this, - w, - h, - isDefined(w) ? MeasureMode.Exactly : MeasureMode.Undefined, - isDefined(h) ? MeasureMode.Exactly : MeasureMode.Undefined, - w, - h, - true, - ) - // Root's own position = margin + position insets (yoga applies position - // to the root even without a parent container; this matters for rounding - // since the root's abs top/left seeds the pixel-grid walk). - const mar = this.layout.margin - const posL = resolveValue( - resolveEdgeRaw(this.style.position, EDGE_LEFT), - isDefined(w) ? w : 0, - ) - const posT = resolveValue( - resolveEdgeRaw(this.style.position, EDGE_TOP), - isDefined(w) ? w : 0, - ) - this.layout.left = mar[EDGE_LEFT] + (isDefined(posL) ? posL : 0) - this.layout.top = mar[EDGE_TOP] + (isDefined(posT) ? posT : 0) - roundLayout(this, this.config.pointScaleFactor, 0, 0) - } -} - -const DEFAULT_CONFIG = createConfig() - -const CACHE_SLOTS = 4 -function cacheWrite( - node: Node, - aW: number, - aH: number, - wM: MeasureMode, - hM: MeasureMode, - oW: number, - oH: number, - fW: boolean, - fH: boolean, - wasDirty: boolean, -): void { - if (!node._cIn) { - node._cIn = new Float64Array(CACHE_SLOTS * 8) - node._cOut = new Float64Array(CACHE_SLOTS * 2) - } - // First write after a dirty clears stale entries from before the dirty. - // _cGen < _generation means entries are from a previous calculateLayout; - // if wasDirty, the subtree changed since then → old dimensions invalid. - // Clean nodes' old entries stay — same subtree → same result for same - // inputs, so cross-generation caching works (the scroll hot path where - // 499 clean messages cache-hit while one dirty leaf recomputes). - if (wasDirty && node._cGen !== _generation) { - node._cN = 0 - node._cWr = 0 - } - // LRU write index wraps; _cN stays at CACHE_SLOTS so the read scan always - // checks all populated slots (not just those since last wrap). - const i = node._cWr++ % CACHE_SLOTS - if (node._cN < CACHE_SLOTS) node._cN = node._cWr - const o = i * 8 - const cIn = node._cIn - cIn[o] = aW - cIn[o + 1] = aH - cIn[o + 2] = wM - cIn[o + 3] = hM - cIn[o + 4] = oW - cIn[o + 5] = oH - cIn[o + 6] = fW ? 1 : 0 - cIn[o + 7] = fH ? 1 : 0 - node._cOut![i * 2] = node.layout.width - node._cOut![i * 2 + 1] = node.layout.height - node._cGen = _generation -} - -// Store computed layout.width/height into the single-slot cache output fields. -// _hasL/_hasM inputs are committed at the TOP of layoutNode (before compute); -// outputs must be committed HERE (after compute) so a cache hit can restore -// the correct dimensions. Without this, a _hasL hit returns whatever -// layout.width/height was left by the last call — which may be the intrinsic -// content height from a heightMode=Undefined measure pass rather than the -// constrained viewport height from the layout pass. That's the scrollbox -// vpH=33→2624 bug: scrollTop clamps to 0, viewport goes blank. -function commitCacheOutputs(node: Node, performLayout: boolean): void { - if (performLayout) { - node._lOutW = node.layout.width - node._lOutH = node.layout.height - } else { - node._mOutW = node.layout.width - node._mOutH = node.layout.height - } -} - -// -- -// Core flexbox algorithm - -// Profiling counters — reset per calculateLayout, read via getYogaCounters. -// Incremented on each calculateLayout(). Nodes stamp _fbGen/_cGen when -// their cache is written; a cache entry with gen === _generation was -// computed THIS pass and is fresh regardless of isDirty_ state. -let _generation = 0 -let _yogaNodesVisited = 0 -let _yogaMeasureCalls = 0 -let _yogaCacheHits = 0 -let _yogaLiveNodes = 0 -export function getYogaCounters(): { - visited: number - measured: number - cacheHits: number - live: number -} { - return { - visited: _yogaNodesVisited, - measured: _yogaMeasureCalls, - cacheHits: _yogaCacheHits, - live: _yogaLiveNodes, - } -} - -function layoutNode( - node: Node, - availableWidth: number, - availableHeight: number, - widthMode: MeasureMode, - heightMode: MeasureMode, - ownerWidth: number, - ownerHeight: number, - performLayout: boolean, - // When true, ignore style dimension on this axis — the flex container - // has already determined the main size (flex-basis + grow/shrink result). - forceWidth = false, - forceHeight = false, -): void { - _yogaNodesVisited++ - const style = node.style - const layout = node.layout - - // Dirty-flag skip: clean subtree + matching inputs → layout object already - // holds the answer. A cached layout result also satisfies a measure request - // (positions are a superset of dimensions); the reverse does not hold. - // Same-generation entries are fresh regardless of isDirty_ — they were - // computed THIS calculateLayout, the subtree hasn't changed since. - // Previous-generation entries need !isDirty_ (a dirty node's cache from - // before the dirty is stale). - // sameGen bypass only for MEASURE calls — a layout-pass cache hit would - // skip the child-positioning recursion (STEP 5), leaving children at - // stale positions. Measure calls only need w/h which the cache stores. - const sameGen = node._cGen === _generation && !performLayout - if (!node.isDirty_ || sameGen) { - if ( - !node.isDirty_ && - node._hasL && - node._lWM === widthMode && - node._lHM === heightMode && - node._lFW === forceWidth && - node._lFH === forceHeight && - sameFloat(node._lW, availableWidth) && - sameFloat(node._lH, availableHeight) && - sameFloat(node._lOW, ownerWidth) && - sameFloat(node._lOH, ownerHeight) - ) { - _yogaCacheHits++ - layout.width = node._lOutW - layout.height = node._lOutH - return - } - // Multi-entry cache: scan for matching inputs, restore cached w/h on hit. - // Covers the scroll case where a dirty ancestor's measure→layout cascade - // produces N>1 distinct input combos per clean child — the single _hasL - // slot thrashed, forcing full subtree recursion. With 500-message - // scrollbox and one dirty leaf, this took dirty-leaf relayout from - // 76k layoutNode calls (21.7×nodes) to 4k (1.2×nodes), 6.86ms → 550µs. - // Same-generation check covers fresh-mounted (dirty) nodes during - // virtual scroll — the dirty chain invokes them ≥2^depth times, first - // call writes cache, rest hit: 105k visits → ~10k for 1593-node tree. - if (node._cN > 0 && (sameGen || !node.isDirty_)) { - const cIn = node._cIn! - for (let i = 0; i < node._cN; i++) { - const o = i * 8 - if ( - cIn[o + 2] === widthMode && - cIn[o + 3] === heightMode && - cIn[o + 6] === (forceWidth ? 1 : 0) && - cIn[o + 7] === (forceHeight ? 1 : 0) && - sameFloat(cIn[o]!, availableWidth) && - sameFloat(cIn[o + 1]!, availableHeight) && - sameFloat(cIn[o + 4]!, ownerWidth) && - sameFloat(cIn[o + 5]!, ownerHeight) - ) { - layout.width = node._cOut![i * 2]! - layout.height = node._cOut![i * 2 + 1]! - _yogaCacheHits++ - return - } - } - } - if ( - !node.isDirty_ && - !performLayout && - node._hasM && - node._mWM === widthMode && - node._mHM === heightMode && - sameFloat(node._mW, availableWidth) && - sameFloat(node._mH, availableHeight) && - sameFloat(node._mOW, ownerWidth) && - sameFloat(node._mOH, ownerHeight) - ) { - layout.width = node._mOutW - layout.height = node._mOutH - _yogaCacheHits++ - return - } - } - // Commit cache inputs up front so every return path leaves a valid entry. - // Only clear isDirty_ on the LAYOUT pass — the measure pass (computeFlexBasis - // → layoutNode(performLayout=false)) runs before the layout pass in the same - // calculateLayout call. Clearing dirty during measure lets the subsequent - // layout pass hit the STALE _hasL cache from the previous calculateLayout - // (before children were inserted), so ScrollBox content height never grows - // and sticky-scroll never follows new content. A dirty node's _hasL entry is - // stale by definition — invalidate it so the layout pass recomputes. - const wasDirty = node.isDirty_ - if (performLayout) { - node._lW = availableWidth - node._lH = availableHeight - node._lWM = widthMode - node._lHM = heightMode - node._lOW = ownerWidth - node._lOH = ownerHeight - node._lFW = forceWidth - node._lFH = forceHeight - node._hasL = true - node.isDirty_ = false - // Previous approach cleared _cN here to prevent stale pre-dirty entries - // from hitting (long-continuous blank-screen bug). Now replaced by - // generation stamping: the cache check requires sameGen || !isDirty_, so - // previous-generation entries from a dirty node can't hit. Clearing here - // would wipe fresh same-generation entries from an earlier measure call, - // forcing recompute on the layout call. - if (wasDirty) node._hasM = false - } else { - node._mW = availableWidth - node._mH = availableHeight - node._mWM = widthMode - node._mHM = heightMode - node._mOW = ownerWidth - node._mOH = ownerHeight - node._hasM = true - // Don't clear isDirty_. For DIRTY nodes, invalidate _hasL so the upcoming - // performLayout=true call recomputes with the new child set (otherwise - // sticky-scroll never follows new content — the bug from 4557bc9f9c). - // Clean nodes keep _hasL: their layout from the previous generation is - // still valid, they're only here because an ancestor is dirty and called - // with different inputs than cached. - if (wasDirty) node._hasL = false - } - - // Resolve padding/border/margin against ownerWidth (yoga uses ownerWidth for %) - // Write directly into the pre-allocated layout arrays — avoids 3 allocs per - // layoutNode call and 12 resolveEdge calls (was the #1 hotspot per CPU profile). - // Skip entirely when no edges are set — the 4-write zero is cheaper than - // the ~20 reads + ~15 compares resolveEdges4Into does to produce zeros. - const pad = layout.padding - const bor = layout.border - const mar = layout.margin - if (node._hasPadding) resolveEdges4Into(style.padding, ownerWidth, pad) - else pad[0] = pad[1] = pad[2] = pad[3] = 0 - if (node._hasBorder) resolveEdges4Into(style.border, ownerWidth, bor) - else bor[0] = bor[1] = bor[2] = bor[3] = 0 - if (node._hasMargin) resolveEdges4Into(style.margin, ownerWidth, mar) - else mar[0] = mar[1] = mar[2] = mar[3] = 0 - - const paddingBorderWidth = pad[0] + pad[2] + bor[0] + bor[2] - const paddingBorderHeight = pad[1] + pad[3] + bor[1] + bor[3] - - // Resolve style dimensions - const styleWidth = forceWidth ? NaN : resolveValue(style.width, ownerWidth) - const styleHeight = forceHeight - ? NaN - : resolveValue(style.height, ownerHeight) - - // If style dimension is defined, it overrides the available size - let width = availableWidth - let height = availableHeight - let wMode = widthMode - let hMode = heightMode - if (isDefined(styleWidth)) { - width = styleWidth - wMode = MeasureMode.Exactly - } - if (isDefined(styleHeight)) { - height = styleHeight - hMode = MeasureMode.Exactly - } - - // Apply min/max constraints to the node's own dimensions - width = boundAxis(style, true, width, ownerWidth, ownerHeight) - height = boundAxis(style, false, height, ownerWidth, ownerHeight) - - // Measure-func leaf node - if (node.measureFunc && node.children.length === 0) { - const innerW = - wMode === MeasureMode.Undefined - ? NaN - : Math.max(0, width - paddingBorderWidth) - const innerH = - hMode === MeasureMode.Undefined - ? NaN - : Math.max(0, height - paddingBorderHeight) - _yogaMeasureCalls++ - const measured = node.measureFunc(innerW, wMode, innerH, hMode) - node.layout.width = - wMode === MeasureMode.Exactly - ? width - : boundAxis( - style, - true, - (measured.width ?? 0) + paddingBorderWidth, - ownerWidth, - ownerHeight, - ) - node.layout.height = - hMode === MeasureMode.Exactly - ? height - : boundAxis( - style, - false, - (measured.height ?? 0) + paddingBorderHeight, - ownerWidth, - ownerHeight, - ) - commitCacheOutputs(node, performLayout) - // Write cache even for dirty nodes — fresh-mounted items during virtual - // scroll are dirty on first layout, but the dirty chain's measure→layout - // cascade invokes them ≥2^depth times per calculateLayout. Writing here - // lets the 2nd+ calls hit cache (isDirty_ was cleared in the layout pass - // above). Measured: 105k visits → 10k for a 1593-node fresh-mount tree. - cacheWrite( - node, - availableWidth, - availableHeight, - widthMode, - heightMode, - ownerWidth, - ownerHeight, - forceWidth, - forceHeight, - wasDirty, - ) - return - } - - // Leaf node with no children and no measure func - if (node.children.length === 0) { - node.layout.width = - wMode === MeasureMode.Exactly - ? width - : boundAxis(style, true, paddingBorderWidth, ownerWidth, ownerHeight) - node.layout.height = - hMode === MeasureMode.Exactly - ? height - : boundAxis(style, false, paddingBorderHeight, ownerWidth, ownerHeight) - commitCacheOutputs(node, performLayout) - // Write cache even for dirty nodes — fresh-mounted items during virtual - // scroll are dirty on first layout, but the dirty chain's measure→layout - // cascade invokes them ≥2^depth times per calculateLayout. Writing here - // lets the 2nd+ calls hit cache (isDirty_ was cleared in the layout pass - // above). Measured: 105k visits → 10k for a 1593-node fresh-mount tree. - cacheWrite( - node, - availableWidth, - availableHeight, - widthMode, - heightMode, - ownerWidth, - ownerHeight, - forceWidth, - forceHeight, - wasDirty, - ) - return - } - - // Container with children — run flexbox algorithm - const mainAxis = style.flexDirection - const crossAx = crossAxis(mainAxis) - const isMainRow = isRow(mainAxis) - - const mainSize = isMainRow ? width : height - const crossSize = isMainRow ? height : width - const mainMode = isMainRow ? wMode : hMode - const crossMode = isMainRow ? hMode : wMode - const mainPadBorder = isMainRow ? paddingBorderWidth : paddingBorderHeight - const crossPadBorder = isMainRow ? paddingBorderHeight : paddingBorderWidth - - const innerMainSize = isDefined(mainSize) - ? Math.max(0, mainSize - mainPadBorder) - : NaN - const innerCrossSize = isDefined(crossSize) - ? Math.max(0, crossSize - crossPadBorder) - : NaN - - // Resolve gap - const gapMain = resolveGap( - style, - isMainRow ? Gutter.Column : Gutter.Row, - innerMainSize, - ) - - // Partition children into flow vs absolute. display:contents nodes are - // transparent — their children are lifted into the grandparent's child list - // (recursively), and the contents node itself gets zero layout. - const flowChildren: Node[] = [] - const absChildren: Node[] = [] - collectLayoutChildren(node, flowChildren, absChildren) - - // ownerW/H are the reference sizes for resolving children's percentage - // values. Per CSS, a % width resolves against the parent's content-box - // width. If this node's width is indefinite, children's % widths are also - // indefinite — do NOT fall through to the grandparent's size. - const ownerW = isDefined(width) ? width : NaN - const ownerH = isDefined(height) ? height : NaN - const isWrap = style.flexWrap !== Wrap.NoWrap - const gapCross = resolveGap( - style, - isMainRow ? Gutter.Row : Gutter.Column, - innerCrossSize, - ) - - // STEP 1: Compute flex-basis for each flow child and break into lines. - // Single-line (NoWrap) containers always get one line; multi-line containers - // break when accumulated basis+margin+gap exceeds innerMainSize. - for (const c of flowChildren) { - c._flexBasis = computeFlexBasis( - c, - mainAxis, - innerMainSize, - innerCrossSize, - crossMode, - ownerW, - ownerH, - ) - } - const lines: Node[][] = [] - if (!isWrap || !isDefined(innerMainSize) || flowChildren.length === 0) { - for (const c of flowChildren) c._lineIndex = 0 - lines.push(flowChildren) - } else { - // Line-break decisions use the min/max-clamped basis (flexbox spec §9.3.5: - // "hypothetical main size"), not the raw flex-basis. - let lineStart = 0 - let lineLen = 0 - for (let i = 0; i < flowChildren.length; i++) { - const c = flowChildren[i]! - const hypo = boundAxis(c.style, isMainRow, c._flexBasis, ownerW, ownerH) - const outer = Math.max(0, hypo) + childMarginForAxis(c, mainAxis, ownerW) - const withGap = i > lineStart ? gapMain : 0 - if (i > lineStart && lineLen + withGap + outer > innerMainSize) { - lines.push(flowChildren.slice(lineStart, i)) - lineStart = i - lineLen = outer - } else { - lineLen += withGap + outer - } - c._lineIndex = lines.length - } - lines.push(flowChildren.slice(lineStart)) - } - const lineCount = lines.length - const isBaseline = isBaselineLayout(node, flowChildren) - - // STEP 2+3: For each line, resolve flexible lengths and lay out children to - // measure cross sizes. Track per-line consumed main and max cross. - const lineConsumedMain: number[] = new Array(lineCount) - const lineCrossSizes: number[] = new Array(lineCount) - // Baseline layout tracks max ascent (baseline + leading margin) per line so - // baseline-aligned items can be positioned at maxAscent - childBaseline. - const lineMaxAscent: number[] = isBaseline ? new Array(lineCount).fill(0) : [] - let maxLineMain = 0 - let totalLinesCross = 0 - for (let li = 0; li < lineCount; li++) { - const line = lines[li]! - const lineGap = line.length > 1 ? gapMain * (line.length - 1) : 0 - let lineBasis = lineGap - for (const c of line) { - lineBasis += c._flexBasis + childMarginForAxis(c, mainAxis, ownerW) - } - // Resolve flexible lengths against available inner main. For indefinite - // containers with min/max, flex against the clamped size. - let availMain = innerMainSize - if (!isDefined(availMain)) { - const mainOwner = isMainRow ? ownerWidth : ownerHeight - const minM = resolveValue( - isMainRow ? style.minWidth : style.minHeight, - mainOwner, - ) - const maxM = resolveValue( - isMainRow ? style.maxWidth : style.maxHeight, - mainOwner, - ) - if (isDefined(maxM) && lineBasis > maxM - mainPadBorder) { - availMain = Math.max(0, maxM - mainPadBorder) - } else if (isDefined(minM) && lineBasis < minM - mainPadBorder) { - availMain = Math.max(0, minM - mainPadBorder) - } - } - resolveFlexibleLengths( - line, - availMain, - lineBasis, - isMainRow, - ownerW, - ownerH, - ) - - // Lay out each child in this line to measure cross - let lineCross = 0 - for (const c of line) { - const cStyle = c.style - const childAlign = - cStyle.alignSelf === Align.Auto ? style.alignItems : cStyle.alignSelf - const cMarginCross = childMarginForAxis(c, crossAx, ownerW) - let childCrossSize = NaN - let childCrossMode: MeasureMode = MeasureMode.Undefined - const resolvedCrossStyle = resolveValue( - isMainRow ? cStyle.height : cStyle.width, - isMainRow ? ownerH : ownerW, - ) - const crossLeadE = isMainRow ? EDGE_TOP : EDGE_LEFT - const crossTrailE = isMainRow ? EDGE_BOTTOM : EDGE_RIGHT - const hasCrossAutoMargin = - c._hasAutoMargin && - (isMarginAuto(cStyle.margin, crossLeadE) || - isMarginAuto(cStyle.margin, crossTrailE)) - // Single-line stretch goes directly to the container cross size. - // Multi-line wrap measures intrinsic cross (Undefined mode) so - // flex-grow grandchildren don't expand to the container — the line - // cross size is determined first, then items are re-stretched. - if (isDefined(resolvedCrossStyle)) { - childCrossSize = resolvedCrossStyle - childCrossMode = MeasureMode.Exactly - } else if ( - childAlign === Align.Stretch && - !hasCrossAutoMargin && - !isWrap && - isDefined(innerCrossSize) && - crossMode === MeasureMode.Exactly - ) { - childCrossSize = Math.max(0, innerCrossSize - cMarginCross) - childCrossMode = MeasureMode.Exactly - } else if (!isWrap && isDefined(innerCrossSize)) { - childCrossSize = Math.max(0, innerCrossSize - cMarginCross) - childCrossMode = MeasureMode.AtMost - } - const cw = isMainRow ? c._mainSize : childCrossSize - const ch = isMainRow ? childCrossSize : c._mainSize - layoutNode( - c, - cw, - ch, - isMainRow ? MeasureMode.Exactly : childCrossMode, - isMainRow ? childCrossMode : MeasureMode.Exactly, - ownerW, - ownerH, - performLayout, - isMainRow, - !isMainRow, - ) - c._crossSize = isMainRow ? c.layout.height : c.layout.width - lineCross = Math.max(lineCross, c._crossSize + cMarginCross) - } - // Baseline layout: line cross size must fit maxAscent + maxDescent of - // baseline-aligned children (yoga STEP 8). Only applies to row direction. - if (isBaseline) { - let maxAscent = 0 - let maxDescent = 0 - for (const c of line) { - if (resolveChildAlign(node, c) !== Align.Baseline) continue - const mTop = resolveEdge(c.style.margin, EDGE_TOP, ownerW) - const mBot = resolveEdge(c.style.margin, EDGE_BOTTOM, ownerW) - const ascent = calculateBaseline(c) + mTop - const descent = c.layout.height + mTop + mBot - ascent - if (ascent > maxAscent) maxAscent = ascent - if (descent > maxDescent) maxDescent = descent - } - lineMaxAscent[li] = maxAscent - if (maxAscent + maxDescent > lineCross) { - lineCross = maxAscent + maxDescent - } - } - // layoutNode(c) at line ~1117 above already resolved c.layout.margin[] via - // resolveEdges4Into with the same ownerW — read directly instead of - // re-resolving through childMarginForAxis → 2× resolveEdge. - const mainLead = leadingEdge(mainAxis) - const mainTrail = trailingEdge(mainAxis) - let consumed = lineGap - for (const c of line) { - const cm = c.layout.margin - consumed += c._mainSize + cm[mainLead]! + cm[mainTrail]! - } - lineConsumedMain[li] = consumed - lineCrossSizes[li] = lineCross - maxLineMain = Math.max(maxLineMain, consumed) - totalLinesCross += lineCross - } - const totalCrossGap = lineCount > 1 ? gapCross * (lineCount - 1) : 0 - totalLinesCross += totalCrossGap - - // STEP 4: Determine container dimensions. Per yoga's STEP 9, for both - // AtMost (FitContent) and Undefined (MaxContent) the node sizes to its - // content — AtMost is NOT a hard clamp, items may overflow the available - // space (CSS "fit-content" behavior). Only Scroll overflow clamps to the - // available size. Wrap containers that broke into multiple lines under - // AtMost fill the available main size since they wrapped at that boundary. - const isScroll = style.overflow === Overflow.Scroll - const contentMain = maxLineMain + mainPadBorder - const finalMainSize = - mainMode === MeasureMode.Exactly - ? mainSize - : mainMode === MeasureMode.AtMost && isScroll - ? Math.max(Math.min(mainSize, contentMain), mainPadBorder) - : isWrap && lineCount > 1 && mainMode === MeasureMode.AtMost - ? mainSize - : contentMain - const contentCross = totalLinesCross + crossPadBorder - const finalCrossSize = - crossMode === MeasureMode.Exactly - ? crossSize - : crossMode === MeasureMode.AtMost && isScroll - ? Math.max(Math.min(crossSize, contentCross), crossPadBorder) - : contentCross - node.layout.width = boundAxis( - style, - true, - isMainRow ? finalMainSize : finalCrossSize, - ownerWidth, - ownerHeight, - ) - node.layout.height = boundAxis( - style, - false, - isMainRow ? finalCrossSize : finalMainSize, - ownerWidth, - ownerHeight, - ) - commitCacheOutputs(node, performLayout) - // Write cache even for dirty nodes — fresh-mounted items during virtual scroll - cacheWrite( - node, - availableWidth, - availableHeight, - widthMode, - heightMode, - ownerWidth, - ownerHeight, - forceWidth, - forceHeight, - wasDirty, - ) - - if (!performLayout) return - - // STEP 5: Position lines (align-content) and children (justify-content + - // align-items + auto margins). - const actualInnerMain = - (isMainRow ? node.layout.width : node.layout.height) - mainPadBorder - const actualInnerCross = - (isMainRow ? node.layout.height : node.layout.width) - crossPadBorder - const mainLeadEdgePhys = leadingEdge(mainAxis) - const mainTrailEdgePhys = trailingEdge(mainAxis) - const crossLeadEdgePhys = isMainRow ? EDGE_TOP : EDGE_LEFT - const crossTrailEdgePhys = isMainRow ? EDGE_BOTTOM : EDGE_RIGHT - const reversed = isReverse(mainAxis) - const mainContainerSize = isMainRow ? node.layout.width : node.layout.height - const crossLead = pad[crossLeadEdgePhys]! + bor[crossLeadEdgePhys]! - - // Align-content: distribute free cross space among lines. Single-line - // containers use the full cross size for the one line (align-items handles - // positioning within it). - let lineCrossOffset = crossLead - let betweenLines = gapCross - const freeCross = actualInnerCross - totalLinesCross - if (lineCount === 1 && !isWrap && !isBaseline) { - lineCrossSizes[0] = actualInnerCross - } else { - const remCross = Math.max(0, freeCross) - switch (style.alignContent) { - case Align.FlexStart: - break - case Align.Center: - lineCrossOffset += freeCross / 2 - break - case Align.FlexEnd: - lineCrossOffset += freeCross - break - case Align.Stretch: - if (lineCount > 0 && remCross > 0) { - const add = remCross / lineCount - for (let i = 0; i < lineCount; i++) lineCrossSizes[i]! += add - } - break - case Align.SpaceBetween: - if (lineCount > 1) betweenLines += remCross / (lineCount - 1) - break - case Align.SpaceAround: - if (lineCount > 0) { - betweenLines += remCross / lineCount - lineCrossOffset += remCross / lineCount / 2 - } - break - case Align.SpaceEvenly: - if (lineCount > 0) { - betweenLines += remCross / (lineCount + 1) - lineCrossOffset += remCross / (lineCount + 1) - } - break - default: - break - } - } - - // For wrap-reverse, lines stack from the trailing cross edge. Walk lines in - // order but flip the cross position within the container. - const wrapReverse = style.flexWrap === Wrap.WrapReverse - const crossContainerSize = isMainRow ? node.layout.height : node.layout.width - let lineCrossPos = lineCrossOffset - for (let li = 0; li < lineCount; li++) { - const line = lines[li]! - const lineCross = lineCrossSizes[li]! - const consumedMain = lineConsumedMain[li]! - const n = line.length - - // Re-stretch children whose cross is auto and align is stretch, now that - // the line cross size is known. Needed for multi-line wrap (line cross - // wasn't known during initial measure) AND single-line when the container - // cross was not Exactly (initial stretch at ~line 1250 was skipped because - // innerCrossSize wasn't defined — the container sized to max child cross). - if (isWrap || crossMode !== MeasureMode.Exactly) { - for (const c of line) { - const cStyle = c.style - const childAlign = - cStyle.alignSelf === Align.Auto ? style.alignItems : cStyle.alignSelf - const crossStyleDef = isDefined( - resolveValue( - isMainRow ? cStyle.height : cStyle.width, - isMainRow ? ownerH : ownerW, - ), - ) - const hasCrossAutoMargin = - c._hasAutoMargin && - (isMarginAuto(cStyle.margin, crossLeadEdgePhys) || - isMarginAuto(cStyle.margin, crossTrailEdgePhys)) - if ( - childAlign === Align.Stretch && - !crossStyleDef && - !hasCrossAutoMargin - ) { - const cMarginCross = childMarginForAxis(c, crossAx, ownerW) - const target = Math.max(0, lineCross - cMarginCross) - if (c._crossSize !== target) { - const cw = isMainRow ? c._mainSize : target - const ch = isMainRow ? target : c._mainSize - layoutNode( - c, - cw, - ch, - MeasureMode.Exactly, - MeasureMode.Exactly, - ownerW, - ownerH, - performLayout, - isMainRow, - !isMainRow, - ) - c._crossSize = target - } - } - } - } - - // Justify-content + auto margins for this line - let mainOffset = pad[mainLeadEdgePhys]! + bor[mainLeadEdgePhys]! - let betweenMain = gapMain - let numAutoMarginsMain = 0 - for (const c of line) { - if (!c._hasAutoMargin) continue - if (isMarginAuto(c.style.margin, mainLeadEdgePhys)) numAutoMarginsMain++ - if (isMarginAuto(c.style.margin, mainTrailEdgePhys)) numAutoMarginsMain++ - } - const freeMain = actualInnerMain - consumedMain - const remainingMain = Math.max(0, freeMain) - const autoMarginMainSize = - numAutoMarginsMain > 0 && remainingMain > 0 - ? remainingMain / numAutoMarginsMain - : 0 - if (numAutoMarginsMain === 0) { - switch (style.justifyContent) { - case Justify.FlexStart: - break - case Justify.Center: - mainOffset += freeMain / 2 - break - case Justify.FlexEnd: - mainOffset += freeMain - break - case Justify.SpaceBetween: - if (n > 1) betweenMain += remainingMain / (n - 1) - break - case Justify.SpaceAround: - if (n > 0) { - betweenMain += remainingMain / n - mainOffset += remainingMain / n / 2 - } - break - case Justify.SpaceEvenly: - if (n > 0) { - betweenMain += remainingMain / (n + 1) - mainOffset += remainingMain / (n + 1) - } - break - } - } - - const effectiveLineCrossPos = wrapReverse - ? crossContainerSize - lineCrossPos - lineCross - : lineCrossPos - - let pos = mainOffset - for (const c of line) { - const cMargin = c.style.margin - // c.layout.margin[] was populated by resolveEdges4Into inside the - // layoutNode(c) call above (same ownerW). Read resolved values directly - // instead of re-running the edge fallback chain 4× via resolveEdge. - // Auto margins resolve to 0 in layout.margin, so autoMarginMainSize - // substitution still uses the isMarginAuto check against style. - const cLayoutMargin = c.layout.margin - let autoMainLead = false - let autoMainTrail = false - let autoCrossLead = false - let autoCrossTrail = false - let mMainLead: number - let mMainTrail: number - let mCrossLead: number - let mCrossTrail: number - if (c._hasAutoMargin) { - autoMainLead = isMarginAuto(cMargin, mainLeadEdgePhys) - autoMainTrail = isMarginAuto(cMargin, mainTrailEdgePhys) - autoCrossLead = isMarginAuto(cMargin, crossLeadEdgePhys) - autoCrossTrail = isMarginAuto(cMargin, crossTrailEdgePhys) - mMainLead = autoMainLead - ? autoMarginMainSize - : cLayoutMargin[mainLeadEdgePhys]! - mMainTrail = autoMainTrail - ? autoMarginMainSize - : cLayoutMargin[mainTrailEdgePhys]! - mCrossLead = autoCrossLead ? 0 : cLayoutMargin[crossLeadEdgePhys]! - mCrossTrail = autoCrossTrail ? 0 : cLayoutMargin[crossTrailEdgePhys]! - } else { - // Fast path: no auto margins — read resolved values directly. - mMainLead = cLayoutMargin[mainLeadEdgePhys]! - mMainTrail = cLayoutMargin[mainTrailEdgePhys]! - mCrossLead = cLayoutMargin[crossLeadEdgePhys]! - mCrossTrail = cLayoutMargin[crossTrailEdgePhys]! - } - - const mainPos = reversed - ? mainContainerSize - (pos + mMainLead) - c._mainSize - : pos + mMainLead - - const childAlign = - c.style.alignSelf === Align.Auto ? style.alignItems : c.style.alignSelf - let crossPos = effectiveLineCrossPos + mCrossLead - const crossFree = lineCross - c._crossSize - mCrossLead - mCrossTrail - if (autoCrossLead && autoCrossTrail) { - crossPos += Math.max(0, crossFree) / 2 - } else if (autoCrossLead) { - crossPos += Math.max(0, crossFree) - } else if (autoCrossTrail) { - // stays at leading - } else { - switch (childAlign) { - case Align.FlexStart: - case Align.Stretch: - if (wrapReverse) crossPos += crossFree - break - case Align.Center: - crossPos += crossFree / 2 - break - case Align.FlexEnd: - if (!wrapReverse) crossPos += crossFree - break - case Align.Baseline: - // Row direction only (isBaselineLayout checked this). Position so - // the child's baseline aligns with the line's max ascent. Per - // yoga: top = currentLead + maxAscent - childBaseline + leadingPosition. - if (isBaseline) { - crossPos = - effectiveLineCrossPos + - lineMaxAscent[li]! - - calculateBaseline(c) - } - break - default: - break - } - } - - // Relative position offsets. Fast path: no position insets set → - // skip 4× resolveEdgeRaw + 4× resolveValue + 4× isDefined. - let relX = 0 - let relY = 0 - if (c._hasPosition) { - const relLeft = resolveValue( - resolveEdgeRaw(c.style.position, EDGE_LEFT), - ownerW, - ) - const relRight = resolveValue( - resolveEdgeRaw(c.style.position, EDGE_RIGHT), - ownerW, - ) - const relTop = resolveValue( - resolveEdgeRaw(c.style.position, EDGE_TOP), - ownerW, - ) - const relBottom = resolveValue( - resolveEdgeRaw(c.style.position, EDGE_BOTTOM), - ownerW, - ) - relX = isDefined(relLeft) - ? relLeft - : isDefined(relRight) - ? -relRight - : 0 - relY = isDefined(relTop) - ? relTop - : isDefined(relBottom) - ? -relBottom - : 0 - } - - if (isMainRow) { - c.layout.left = mainPos + relX - c.layout.top = crossPos + relY - } else { - c.layout.left = crossPos + relX - c.layout.top = mainPos + relY - } - pos += c._mainSize + mMainLead + mMainTrail + betweenMain - } - lineCrossPos += lineCross + betweenLines - } - - // STEP 6: Absolute-positioned children - for (const c of absChildren) { - layoutAbsoluteChild( - node, - c, - node.layout.width, - node.layout.height, - pad, - bor, - ) - } -} - -function layoutAbsoluteChild( - parent: Node, - child: Node, - parentWidth: number, - parentHeight: number, - pad: [number, number, number, number], - bor: [number, number, number, number], -): void { - const cs = child.style - const posLeft = resolveEdgeRaw(cs.position, EDGE_LEFT) - const posRight = resolveEdgeRaw(cs.position, EDGE_RIGHT) - const posTop = resolveEdgeRaw(cs.position, EDGE_TOP) - const posBottom = resolveEdgeRaw(cs.position, EDGE_BOTTOM) - - const rLeft = resolveValue(posLeft, parentWidth) - const rRight = resolveValue(posRight, parentWidth) - const rTop = resolveValue(posTop, parentHeight) - const rBottom = resolveValue(posBottom, parentHeight) - - // Absolute children's percentage dimensions resolve against the containing - // block's padding-box (parent size minus border), per CSS §10.1. - const paddingBoxW = parentWidth - bor[0] - bor[2] - const paddingBoxH = parentHeight - bor[1] - bor[3] - let cw = resolveValue(cs.width, paddingBoxW) - let ch = resolveValue(cs.height, paddingBoxH) - - // If both left+right defined and width not, derive width - if (!isDefined(cw) && isDefined(rLeft) && isDefined(rRight)) { - cw = paddingBoxW - rLeft - rRight - } - if (!isDefined(ch) && isDefined(rTop) && isDefined(rBottom)) { - ch = paddingBoxH - rTop - rBottom - } - - layoutNode( - child, - cw, - ch, - isDefined(cw) ? MeasureMode.Exactly : MeasureMode.Undefined, - isDefined(ch) ? MeasureMode.Exactly : MeasureMode.Undefined, - paddingBoxW, - paddingBoxH, - true, - ) - - // Margin of absolute child (applied in addition to insets) - const mL = resolveEdge(cs.margin, EDGE_LEFT, parentWidth) - const mT = resolveEdge(cs.margin, EDGE_TOP, parentWidth) - const mR = resolveEdge(cs.margin, EDGE_RIGHT, parentWidth) - const mB = resolveEdge(cs.margin, EDGE_BOTTOM, parentWidth) - - const mainAxis = parent.style.flexDirection - const reversed = isReverse(mainAxis) - const mainRow = isRow(mainAxis) - const wrapReverse = parent.style.flexWrap === Wrap.WrapReverse - // alignSelf overrides alignItems for absolute children (same as flow items) - const alignment = - cs.alignSelf === Align.Auto ? parent.style.alignItems : cs.alignSelf - - // Position - let left: number - if (isDefined(rLeft)) { - left = bor[0] + rLeft + mL - } else if (isDefined(rRight)) { - left = parentWidth - bor[2] - rRight - child.layout.width - mR - } else if (mainRow) { - // Main axis — justify-content, flipped for reversed - const lead = pad[0] + bor[0] - const trail = parentWidth - pad[2] - bor[2] - left = reversed - ? trail - child.layout.width - mR - : justifyAbsolute( - parent.style.justifyContent, - lead, - trail, - child.layout.width, - ) + mL - } else { - left = - alignAbsolute( - alignment, - pad[0] + bor[0], - parentWidth - pad[2] - bor[2], - child.layout.width, - wrapReverse, - ) + mL - } - - let top: number - if (isDefined(rTop)) { - top = bor[1] + rTop + mT - } else if (isDefined(rBottom)) { - top = parentHeight - bor[3] - rBottom - child.layout.height - mB - } else if (mainRow) { - top = - alignAbsolute( - alignment, - pad[1] + bor[1], - parentHeight - pad[3] - bor[3], - child.layout.height, - wrapReverse, - ) + mT - } else { - const lead = pad[1] + bor[1] - const trail = parentHeight - pad[3] - bor[3] - top = reversed - ? trail - child.layout.height - mB - : justifyAbsolute( - parent.style.justifyContent, - lead, - trail, - child.layout.height, - ) + mT - } - - child.layout.left = left - child.layout.top = top -} - -function justifyAbsolute( - justify: Justify, - leadEdge: number, - trailEdge: number, - childSize: number, -): number { - switch (justify) { - case Justify.Center: - return leadEdge + (trailEdge - leadEdge - childSize) / 2 - case Justify.FlexEnd: - return trailEdge - childSize - default: - return leadEdge - } -} - -function alignAbsolute( - align: Align, - leadEdge: number, - trailEdge: number, - childSize: number, - wrapReverse: boolean, -): number { - // Wrap-reverse flips the cross axis: flex-start/stretch go to trailing, - // flex-end goes to leading (yoga's absoluteLayoutChild flips the align value - // when the containing block has wrap-reverse). - switch (align) { - case Align.Center: - return leadEdge + (trailEdge - leadEdge - childSize) / 2 - case Align.FlexEnd: - return wrapReverse ? leadEdge : trailEdge - childSize - default: - return wrapReverse ? trailEdge - childSize : leadEdge - } -} - -function computeFlexBasis( - child: Node, - mainAxis: FlexDirection, - availableMain: number, - availableCross: number, - crossMode: MeasureMode, - ownerWidth: number, - ownerHeight: number, -): number { - // Same-generation cache hit: basis was computed THIS calculateLayout, so - // it's fresh regardless of isDirty_. Covers both clean children (scrolling - // past unchanged messages) AND fresh-mounted dirty children (virtual - // scroll mounts new items — the dirty chain's measure→layout cascade - // invokes this ≥2^depth times, but the child's subtree doesn't change - // between calls within one calculateLayout). For clean children with - // cache from a PREVIOUS generation, also hit if inputs match — isDirty_ - // gates since a dirty child's previous-gen cache is stale. - const sameGen = child._fbGen === _generation - if ( - (sameGen || !child.isDirty_) && - child._fbCrossMode === crossMode && - sameFloat(child._fbOwnerW, ownerWidth) && - sameFloat(child._fbOwnerH, ownerHeight) && - sameFloat(child._fbAvailMain, availableMain) && - sameFloat(child._fbAvailCross, availableCross) - ) { - return child._fbBasis - } - const cs = child.style - const isMainRow = isRow(mainAxis) - - // Explicit flex-basis - const basis = resolveValue(cs.flexBasis, availableMain) - if (isDefined(basis)) { - const b = Math.max(0, basis) - child._fbBasis = b - child._fbOwnerW = ownerWidth - child._fbOwnerH = ownerHeight - child._fbAvailMain = availableMain - child._fbAvailCross = availableCross - child._fbCrossMode = crossMode - child._fbGen = _generation - return b - } - - // Style dimension on main axis - const mainStyleDim = isMainRow ? cs.width : cs.height - const mainOwner = isMainRow ? ownerWidth : ownerHeight - const resolved = resolveValue(mainStyleDim, mainOwner) - if (isDefined(resolved)) { - const b = Math.max(0, resolved) - child._fbBasis = b - child._fbOwnerW = ownerWidth - child._fbOwnerH = ownerHeight - child._fbAvailMain = availableMain - child._fbAvailCross = availableCross - child._fbCrossMode = crossMode - child._fbGen = _generation - return b - } - - // Need to measure the child to get its natural size - const crossStyleDim = isMainRow ? cs.height : cs.width - const crossOwner = isMainRow ? ownerHeight : ownerWidth - let crossConstraint = resolveValue(crossStyleDim, crossOwner) - let crossConstraintMode: MeasureMode = isDefined(crossConstraint) - ? MeasureMode.Exactly - : MeasureMode.Undefined - if (!isDefined(crossConstraint) && isDefined(availableCross)) { - crossConstraint = availableCross - crossConstraintMode = - crossMode === MeasureMode.Exactly && isStretchAlign(child) - ? MeasureMode.Exactly - : MeasureMode.AtMost - } - - // Upstream yoga (YGNodeComputeFlexBasisForChild) passes the available inner - // width with mode AtMost when the subtree will call a measure-func — so text - // nodes don't report unconstrained intrinsic width as flex-basis, which - // would force siblings to shrink and the text to wrap at the wrong width. - // Passing Undefined here made Ink's inside get - // width = intrinsic instead of available, dropping chars at wrap boundaries. - // - // Two constraints on when this applies: - // - Width only. Height is never constrained during basis measurement — - // column containers must measure children at natural height so - // scrollable content can overflow (constraining height clips ScrollBox). - // - Subtree has a measure-func. Pure layout subtrees (no measure-func) - // with flex-grow children would grow into the AtMost constraint, - // inflating the basis (breaks YGMinMaxDimensionTest flex_grow_in_at_most - // where a flexGrow:1 child should stay at basis 0, not grow to 100). - let mainConstraint = NaN - let mainConstraintMode: MeasureMode = MeasureMode.Undefined - if (isMainRow && isDefined(availableMain) && hasMeasureFuncInSubtree(child)) { - mainConstraint = availableMain - mainConstraintMode = MeasureMode.AtMost - } - - const mw = isMainRow ? mainConstraint : crossConstraint - const mh = isMainRow ? crossConstraint : mainConstraint - const mwMode = isMainRow ? mainConstraintMode : crossConstraintMode - const mhMode = isMainRow ? crossConstraintMode : mainConstraintMode - - layoutNode(child, mw, mh, mwMode, mhMode, ownerWidth, ownerHeight, false) - const b = isMainRow ? child.layout.width : child.layout.height - child._fbBasis = b - child._fbOwnerW = ownerWidth - child._fbOwnerH = ownerHeight - child._fbAvailMain = availableMain - child._fbAvailCross = availableCross - child._fbCrossMode = crossMode - child._fbGen = _generation - return b -} - -function hasMeasureFuncInSubtree(node: Node): boolean { - if (node.measureFunc) return true - for (const c of node.children) { - if (hasMeasureFuncInSubtree(c)) return true - } - return false -} - -function resolveFlexibleLengths( - children: Node[], - availableInnerMain: number, - totalFlexBasis: number, - isMainRow: boolean, - ownerW: number, - ownerH: number, -): void { - // Multi-pass flex distribution per CSS flexbox spec §9.7 "Resolving Flexible - // Lengths": distribute free space, detect min/max violations, freeze all - // violators, redistribute among unfrozen children. Repeat until stable. - const n = children.length - const frozen: boolean[] = new Array(n).fill(false) - const initialFree = isDefined(availableInnerMain) - ? availableInnerMain - totalFlexBasis - : 0 - // Freeze inflexible items at their clamped basis - for (let i = 0; i < n; i++) { - const c = children[i]! - const clamped = boundAxis(c.style, isMainRow, c._flexBasis, ownerW, ownerH) - const inflexible = - !isDefined(availableInnerMain) || - (initialFree >= 0 ? c.style.flexGrow === 0 : c.style.flexShrink === 0) - if (inflexible) { - c._mainSize = Math.max(0, clamped) - frozen[i] = true - } else { - c._mainSize = c._flexBasis - } - } - // Iteratively distribute until no violations. Free space is recomputed each - // pass: initial free space minus the delta frozen children consumed beyond - // (or below) their basis. - const unclamped: number[] = new Array(n) - for (let iter = 0; iter <= n; iter++) { - let frozenDelta = 0 - let totalGrow = 0 - let totalShrinkScaled = 0 - let unfrozenCount = 0 - for (let i = 0; i < n; i++) { - const c = children[i]! - if (frozen[i]) { - frozenDelta += c._mainSize - c._flexBasis - } else { - totalGrow += c.style.flexGrow - totalShrinkScaled += c.style.flexShrink * c._flexBasis - unfrozenCount++ - } - } - if (unfrozenCount === 0) break - let remaining = initialFree - frozenDelta - // Spec §9.7 step 4c: if sum of flex factors < 1, only distribute - // initialFree × sum, not the full remaining space (partial flex). - if (remaining > 0 && totalGrow > 0 && totalGrow < 1) { - const scaled = initialFree * totalGrow - if (scaled < remaining) remaining = scaled - } else if (remaining < 0 && totalShrinkScaled > 0) { - let totalShrink = 0 - for (let i = 0; i < n; i++) { - if (!frozen[i]) totalShrink += children[i]!.style.flexShrink - } - if (totalShrink < 1) { - const scaled = initialFree * totalShrink - if (scaled > remaining) remaining = scaled - } - } - // Compute targets + violations for all unfrozen children - let totalViolation = 0 - for (let i = 0; i < n; i++) { - if (frozen[i]) continue - const c = children[i]! - let t = c._flexBasis - if (remaining > 0 && totalGrow > 0) { - t += (remaining * c.style.flexGrow) / totalGrow - } else if (remaining < 0 && totalShrinkScaled > 0) { - t += - (remaining * (c.style.flexShrink * c._flexBasis)) / totalShrinkScaled - } - unclamped[i] = t - const clamped = Math.max( - 0, - boundAxis(c.style, isMainRow, t, ownerW, ownerH), - ) - c._mainSize = clamped - totalViolation += clamped - t - } - // Freeze per spec §9.7 step 5: if totalViolation is zero freeze all; if - // positive freeze min-violators; if negative freeze max-violators. - if (totalViolation === 0) break - let anyFrozen = false - for (let i = 0; i < n; i++) { - if (frozen[i]) continue - const v = children[i]!._mainSize - unclamped[i]! - if ((totalViolation > 0 && v > 0) || (totalViolation < 0 && v < 0)) { - frozen[i] = true - anyFrozen = true - } - } - if (!anyFrozen) break - } -} - -function isStretchAlign(child: Node): boolean { - const p = child.parent - if (!p) return false - const align = - child.style.alignSelf === Align.Auto - ? p.style.alignItems - : child.style.alignSelf - return align === Align.Stretch -} - -function resolveChildAlign(parent: Node, child: Node): Align { - return child.style.alignSelf === Align.Auto - ? parent.style.alignItems - : child.style.alignSelf -} - -// Baseline of a node per CSS Flexbox §8.5 / yoga's YGBaseline. Leaf nodes -// (no children) use their own height. Containers recurse into the first -// baseline-aligned child on the first line (or the first flow child if none -// are baseline-aligned), returning that child's baseline + its top offset. -function calculateBaseline(node: Node): number { - let baselineChild: Node | null = null - for (const c of node.children) { - if (c._lineIndex > 0) break - if (c.style.positionType === PositionType.Absolute) continue - if (c.style.display === Display.None) continue - if ( - resolveChildAlign(node, c) === Align.Baseline || - c.isReferenceBaseline_ - ) { - baselineChild = c - break - } - if (baselineChild === null) baselineChild = c - } - if (baselineChild === null) return node.layout.height - return calculateBaseline(baselineChild) + baselineChild.layout.top -} - -// A container uses baseline layout only for row direction, when either -// align-items is baseline or any flow child has align-self: baseline. -function isBaselineLayout(node: Node, flowChildren: Node[]): boolean { - if (!isRow(node.style.flexDirection)) return false - if (node.style.alignItems === Align.Baseline) return true - for (const c of flowChildren) { - if (c.style.alignSelf === Align.Baseline) return true - } - return false -} - -function childMarginForAxis( - child: Node, - axis: FlexDirection, - ownerWidth: number, -): number { - if (!child._hasMargin) return 0 - const lead = resolveEdge(child.style.margin, leadingEdge(axis), ownerWidth) - const trail = resolveEdge(child.style.margin, trailingEdge(axis), ownerWidth) - return lead + trail -} - -function resolveGap(style: Style, gutter: Gutter, ownerSize: number): number { - let v = style.gap[gutter]! - if (v.unit === Unit.Undefined) v = style.gap[Gutter.All]! - const r = resolveValue(v, ownerSize) - return isDefined(r) ? Math.max(0, r) : 0 -} - -function boundAxis( - style: Style, - isWidth: boolean, - value: number, - ownerWidth: number, - ownerHeight: number, -): number { - const minV = isWidth ? style.minWidth : style.minHeight - const maxV = isWidth ? style.maxWidth : style.maxHeight - const minU = minV.unit - const maxU = maxV.unit - // Fast path: no min/max constraints set. Per CPU profile this is the - // overwhelmingly common case (~32k calls/layout on the 1000-node bench, - // nearly all with undefined min/max) — skipping 2× resolveValue + 2× isNaN - // that always no-op. Unit.Undefined = 0. - if (minU === 0 && maxU === 0) return value - const owner = isWidth ? ownerWidth : ownerHeight - let v = value - // Inlined resolveValue: Unit.Point=1, Unit.Percent=2. `m === m` is !isNaN. - if (maxU === 1) { - if (v > maxV.value) v = maxV.value - } else if (maxU === 2) { - const m = (maxV.value * owner) / 100 - if (m === m && v > m) v = m - } - if (minU === 1) { - if (v < minV.value) v = minV.value - } else if (minU === 2) { - const m = (minV.value * owner) / 100 - if (m === m && v < m) v = m - } - return v -} - -function zeroLayoutRecursive(node: Node): void { - for (const c of node.children) { - c.layout.left = 0 - c.layout.top = 0 - c.layout.width = 0 - c.layout.height = 0 - // Invalidate layout cache — without this, unhide → calculateLayout finds - // the child clean (!isDirty_) with _hasL intact, hits the cache at line - // ~1086, restores stale _lOutW/_lOutH, and returns early — skipping the - // child-positioning recursion. Grandchildren stay at (0,0,0,0) from the - // zeroing above and render invisible. isDirty_=true also gates _cN and - // _fbBasis via their (sameGen || !isDirty_) checks — _cGen/_fbGen freeze - // during hide so sameGen is false on unhide. - c.isDirty_ = true - c._hasL = false - c._hasM = false - zeroLayoutRecursive(c) - } -} - -function collectLayoutChildren(node: Node, flow: Node[], abs: Node[]): void { - // Partition a node's children into flow and absolute lists, flattening - // display:contents subtrees so their children are laid out as direct - // children of this node (per CSS display:contents spec — the box is removed - // from the layout tree but its children remain, lifted to the grandparent). - for (const c of node.children) { - const disp = c.style.display - if (disp === Display.None) { - c.layout.left = 0 - c.layout.top = 0 - c.layout.width = 0 - c.layout.height = 0 - zeroLayoutRecursive(c) - } else if (disp === Display.Contents) { - c.layout.left = 0 - c.layout.top = 0 - c.layout.width = 0 - c.layout.height = 0 - // Recurse — nested display:contents lifts all the way up. The contents - // node's own margin/padding/position/dimensions are ignored. - collectLayoutChildren(c, flow, abs) - } else if (c.style.positionType === PositionType.Absolute) { - abs.push(c) - } else { - flow.push(c) - } - } -} - -function roundLayout( - node: Node, - scale: number, - absLeft: number, - absTop: number, -): void { - if (scale === 0) return - const l = node.layout - const nodeLeft = l.left - const nodeTop = l.top - const nodeWidth = l.width - const nodeHeight = l.height - - const absNodeLeft = absLeft + nodeLeft - const absNodeTop = absTop + nodeTop - - // Upstream YGRoundValueToPixelGrid: text nodes (has measureFunc) floor their - // positions so wrapped text never starts past its allocated column. Width - // uses ceil-if-fractional to avoid clipping the last glyph. Non-text nodes - // use standard round. Matches yoga's PixelGrid.cpp — without this, justify - // center/space-evenly positions are off-by-one vs WASM and flex-shrink - // overflow places siblings at the wrong column. - const isText = node.measureFunc !== null - l.left = roundValue(nodeLeft, scale, false, isText) - l.top = roundValue(nodeTop, scale, false, isText) - - // Width/height rounded via absolute edges to avoid cumulative drift - const absRight = absNodeLeft + nodeWidth - const absBottom = absNodeTop + nodeHeight - const hasFracW = !isWholeNumber(nodeWidth * scale) - const hasFracH = !isWholeNumber(nodeHeight * scale) - l.width = - roundValue(absRight, scale, isText && hasFracW, isText && !hasFracW) - - roundValue(absNodeLeft, scale, false, isText) - l.height = - roundValue(absBottom, scale, isText && hasFracH, isText && !hasFracH) - - roundValue(absNodeTop, scale, false, isText) - - for (const c of node.children) { - roundLayout(c, scale, absNodeLeft, absNodeTop) - } -} - -function isWholeNumber(v: number): boolean { - const frac = v - Math.floor(v) - return frac < 0.0001 || frac > 0.9999 -} - -function roundValue( - v: number, - scale: number, - forceCeil: boolean, - forceFloor: boolean, -): number { - let scaled = v * scale - let frac = scaled - Math.floor(scaled) - if (frac < 0) frac += 1 - // Float-epsilon tolerance matches upstream YGDoubleEqual (1e-4) - if (frac < 0.0001) { - scaled = Math.floor(scaled) - } else if (frac > 0.9999) { - scaled = Math.ceil(scaled) - } else if (forceCeil) { - scaled = Math.ceil(scaled) - } else if (forceFloor) { - scaled = Math.floor(scaled) - } else { - // Round half-up (>= 0.5 goes up), per upstream - scaled = Math.floor(scaled) + (frac >= 0.4999 ? 1 : 0) - } - return scaled / scale -} - -// -- -// Helpers - -function parseDimension(v: number | string | undefined): Value { - if (v === undefined) return UNDEFINED_VALUE - if (v === 'auto') return AUTO_VALUE - if (typeof v === 'number') { - // WASM yoga's YGFloatIsUndefined treats NaN and ±Infinity as undefined. - // Ink passes height={Infinity} (e.g. LogSelector maxHeight default) and - // expects it to mean "unconstrained" — storing it as a literal point value - // makes the node height Infinity and breaks all downstream layout. - return Number.isFinite(v) ? pointValue(v) : UNDEFINED_VALUE - } - if (typeof v === 'string' && v.endsWith('%')) { - return percentValue(parseFloat(v)) - } - const n = parseFloat(v) - return isNaN(n) ? UNDEFINED_VALUE : pointValue(n) -} - -function physicalEdge(edge: Edge): number { - switch (edge) { - case Edge.Left: - case Edge.Start: - return EDGE_LEFT - case Edge.Top: - return EDGE_TOP - case Edge.Right: - case Edge.End: - return EDGE_RIGHT - case Edge.Bottom: - return EDGE_BOTTOM - default: - return EDGE_LEFT - } -} - -// -- -// Module API matching yoga-layout/load - -export type Yoga = { - Config: { - create(): Config - destroy(config: Config): void - } - Node: { - create(config?: Config): Node - createDefault(): Node - createWithConfig(config: Config): Node - destroy(node: Node): void - } -} - -const YOGA_INSTANCE: Yoga = { - Config: { - create: createConfig, - destroy() {}, - }, - Node: { - create: (config?: Config) => new Node(config), - createDefault: () => new Node(), - createWithConfig: (config: Config) => new Node(config), - destroy() {}, - }, -} - -export function loadYoga(): Promise { - return Promise.resolve(YOGA_INSTANCE) -} - -export default YOGA_INSTANCE diff --git a/packages/@ant/ink/src/hooks/useExitOnCtrlCD.ts b/packages/@ant/ink/src/hooks/useExitOnCtrlCD.ts deleted file mode 100644 index 442d4fad3..000000000 --- a/packages/@ant/ink/src/hooks/useExitOnCtrlCD.ts +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Minimal stub of useExitOnCtrlCD + useExitOnCtrlCDWithKeybindings. - * - * The original hooks depend on the keybinding system and useApp() exit. - * This stub provides the same interface with simplified Ctrl+C/D handling - * via useInput, suitable for the standalone @anthropic/ink package. - */ - -import { useCallback, useState } from 'react' -import useInput from './use-input.js' - -export type ExitState = { - pending: boolean - keyName: 'Ctrl-C' | 'Ctrl-D' | null -} - -/** - * Minimal double-press exit handler. - * First Ctrl+C/D shows pending state, second press within timeout fires onExit. - */ -const DOUBLE_PRESS_TIMEOUT_MS = 800 - -function useDoublePress( - setPending: (pending: boolean) => void, - onDoublePress: () => void, -): () => void { - let lastPress = 0 - let timeout: ReturnType | undefined - - return () => { - const now = Date.now() - const timeSince = now - lastPress - const isDouble = - timeSince <= DOUBLE_PRESS_TIMEOUT_MS && timeout !== undefined - - if (isDouble) { - clearTimeout(timeout) - timeout = undefined - setPending(false) - onDoublePress() - } else { - setPending(true) - clearTimeout(timeout) - timeout = setTimeout(() => { - setPending(false) - timeout = undefined - }, DOUBLE_PRESS_TIMEOUT_MS) - } - lastPress = now - } -} - -/** - * Stub that provides ExitState for Ctrl+C/D double-press UI. - * In the standalone package, this uses useInput directly rather than the - * keybinding system. - */ -export function useExitOnCtrlCDWithKeybindings( - _onExit?: () => void, - _onInterrupt?: () => boolean, - isActive: boolean = true, -): ExitState { - const [exitState, setExitState] = useState({ - pending: false, - keyName: null, - }) - - const handleCtrlC = useDoublePress( - (pending: boolean) => - setExitState({ pending, keyName: pending ? 'Ctrl-C' : null }), - () => process.exit(0), - ) - - const handleCtrlD = useDoublePress( - (pending: boolean) => - setExitState({ pending, keyName: pending ? 'Ctrl-D' : null }), - () => process.exit(0), - ) - - const handleInput = useCallback( - (_input: string, key: { ctrl?: boolean; name?: string }) => { - if (!isActive) return - if (key.ctrl && key.name === 'c') { - handleCtrlC() - } else if (key.ctrl && key.name === 'd') { - handleCtrlD() - } - }, - [isActive, handleCtrlC, handleCtrlD], - ) - - useInput(handleInput, { isActive }) - - return exitState -} diff --git a/packages/@ant/ink/src/hooks/useSearchInput.ts b/packages/@ant/ink/src/hooks/useSearchInput.ts deleted file mode 100644 index 9935256c7..000000000 --- a/packages/@ant/ink/src/hooks/useSearchInput.ts +++ /dev/null @@ -1,222 +0,0 @@ -/** - * Minimal stub of useSearchInput for the standalone @anthropic/ink package. - * - * Provides the same interface as the full implementation but without - * kill-ring / yank support. Suitable for FuzzyPicker and other theme - * components that need text input handling. - */ - -import { useCallback, useState } from 'react' -import type { KeyboardEvent } from '../core/events/keyboard-event.js' -import useInput from './use-input.js' -import { useTerminalSize } from '../hooks/useTerminalSize.js' - -type UseSearchInputOptions = { - isActive: boolean - onExit: () => void - onCancel?: () => void - onExitUp?: () => void - columns?: number - passthroughCtrlKeys?: string[] - initialQuery?: string - backspaceExitsOnEmpty?: boolean -} - -type UseSearchInputReturn = { - query: string - setQuery: (q: string) => void - cursorOffset: number - handleKeyDown: (e: KeyboardEvent) => void -} - -const UNHANDLED_SPECIAL_KEYS = new Set([ - 'pageup', - 'pagedown', - 'insert', - 'wheelup', - 'wheeldown', - 'mouse', - 'f1', - 'f2', - 'f3', - 'f4', - 'f5', - 'f6', - 'f7', - 'f8', - 'f9', - 'f10', - 'f11', - 'f12', -]) - -export function useSearchInput({ - isActive, - onExit, - onCancel, - onExitUp, - columns, - initialQuery = '', - backspaceExitsOnEmpty = true, -}: UseSearchInputOptions): UseSearchInputReturn { - const { columns: terminalColumns } = useTerminalSize() - const _effectiveColumns = columns ?? terminalColumns - const [query, setQueryState] = useState(initialQuery) - const [cursorOffset, setCursorOffset] = useState(initialQuery.length) - - const setQuery = useCallback((q: string) => { - setQueryState(q) - setCursorOffset(q.length) - }, []) - - const handleKeyDown = (e: KeyboardEvent): void => { - if (!isActive) return - - if (e.key === 'return' || e.key === 'down') { - e.preventDefault() - onExit() - return - } - if (e.key === 'up') { - e.preventDefault() - onExitUp?.() - return - } - if (e.key === 'escape') { - e.preventDefault() - if (onCancel) { - onCancel() - } else if (query.length > 0) { - setQueryState('') - setCursorOffset(0) - } else { - onExit() - } - return - } - if (e.key === 'backspace') { - e.preventDefault() - if (query.length === 0) { - if (backspaceExitsOnEmpty) (onCancel ?? onExit)() - return - } - const newOffset = Math.max(0, cursorOffset - 1) - setQueryState(query.slice(0, newOffset) + query.slice(cursorOffset)) - setCursorOffset(newOffset) - return - } - if (e.key === 'delete') { - e.preventDefault() - if (cursorOffset < query.length) { - setQueryState(query.slice(0, cursorOffset) + query.slice(cursorOffset + 1)) - } - return - } - if (e.key === 'left') { - e.preventDefault() - setCursorOffset(Math.max(0, cursorOffset - 1)) - return - } - if (e.key === 'right') { - e.preventDefault() - setCursorOffset(Math.min(query.length, cursorOffset + 1)) - return - } - if (e.key === 'home') { - e.preventDefault() - setCursorOffset(0) - return - } - if (e.key === 'end') { - e.preventDefault() - setCursorOffset(query.length) - return - } - if (e.ctrl) { - switch (e.key.toLowerCase()) { - case 'a': - e.preventDefault() - setCursorOffset(0) - return - case 'e': - e.preventDefault() - setCursorOffset(query.length) - return - case 'b': - e.preventDefault() - setCursorOffset(Math.max(0, cursorOffset - 1)) - return - case 'f': - e.preventDefault() - setCursorOffset(Math.min(query.length, cursorOffset + 1)) - return - case 'd': { - e.preventDefault() - if (query.length === 0) { - ;(onCancel ?? onExit)() - return - } - if (cursorOffset < query.length) { - setQueryState(query.slice(0, cursorOffset) + query.slice(cursorOffset + 1)) - } - return - } - case 'h': { - e.preventDefault() - if (query.length === 0) { - if (backspaceExitsOnEmpty) (onCancel ?? onExit)() - return - } - const newOffset = Math.max(0, cursorOffset - 1) - setQueryState(query.slice(0, newOffset) + query.slice(cursorOffset)) - setCursorOffset(newOffset) - return - } - case 'c': - e.preventDefault() - onCancel?.() - return - case 'u': - e.preventDefault() - setQueryState(query.slice(cursorOffset)) - setCursorOffset(0) - return - case 'k': - e.preventDefault() - setQueryState(query.slice(0, cursorOffset)) - return - case 'w': { - e.preventDefault() - // Delete word before cursor - const before = query.slice(0, cursorOffset) - const after = query.slice(cursorOffset) - const trimmed = before.replace(/\S+\s*$/, '') - setQueryState(trimmed + after) - setCursorOffset(trimmed.length) - return - } - } - return - } - if (e.key === 'tab') { - return - } - - // Regular character input - if (e.key.length >= 1 && !UNHANDLED_SPECIAL_KEYS.has(e.key)) { - e.preventDefault() - setQueryState(query.slice(0, cursorOffset) + e.key + query.slice(cursorOffset)) - setCursorOffset(cursorOffset + 1) - } - } - - // Bridge: subscribe via useInput and adapt to KeyboardEvent - useInput( - (_input: string, _key: unknown, event: { keypress: string }) => { - handleKeyDown(new KeyboardEvent(event.keypress)) - }, - { isActive }, - ) - - return { query, setQuery, cursorOffset, handleKeyDown } -} diff --git a/packages/@ant/ink/src/hooks/useTerminalSize.ts b/packages/@ant/ink/src/hooks/useTerminalSize.ts deleted file mode 100644 index bccc5bef6..000000000 --- a/packages/@ant/ink/src/hooks/useTerminalSize.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { useContext } from 'react' -import { - type TerminalSize, - TerminalSizeContext, -} from '../components/TerminalSizeContext.js' - -export function useTerminalSize(): TerminalSize { - const size = useContext(TerminalSizeContext) - - if (!size) { - throw new Error('useTerminalSize must be used within an Ink App component') - } - - return size -} diff --git a/packages/@ant/ink/src/index.ts b/packages/@ant/ink/src/index.ts deleted file mode 100644 index ac92daabc..000000000 --- a/packages/@ant/ink/src/index.ts +++ /dev/null @@ -1,126 +0,0 @@ -/** - * @anthropic/ink — Terminal React rendering framework - * - * Three-layer architecture: - * core/ — Rendering engine (reconciler, layout, terminal I/O, screen buffer) - * components/ — UI primitives (Box, Text, ScrollBox, App, hooks) - * theme/ — Theme system (ThemeProvider, ThemedBox, ThemedText, design-system) - */ - -// ============================================================ -// Core API (render/createRoot) -// ============================================================ -export { default as wrappedRender, renderSync, createRoot } from './core/root.js' -export type { RenderOptions, Instance, Root } from './core/root.js' - -// InkCore class -export { default as Ink } from './core/ink.js' - -// ============================================================ -// Core types -// ============================================================ -export type { DOMElement, TextNode, ElementNames, DOMNodeAttribute } from './core/dom.js' -export type { Styles, TextStyles, Color, RGBColor, HexColor, Ansi256Color, AnsiColor } from './core/styles.js' -export type { Key } from './core/events/input-event.js' -export type { FlickerReason, FrameEvent } from './core/frame.js' -export type { MatchPosition } from './core/render-to-screen.js' -export type { SelectionState, FocusMove } from './core/selection.js' -export type { Progress } from './core/terminal.js' - -// ============================================================ -// Core modules -// ============================================================ -export { ClickEvent } from './core/events/click-event.js' -export { EventEmitter } from './core/events/emitter.js' -export { Event } from './core/events/event.js' -export { InputEvent } from './core/events/input-event.js' -export { TerminalFocusEvent, type TerminalFocusEventType } from './core/events/terminal-focus-event.js' -export { KeyboardEvent } from './core/events/keyboard-event.js' -export { FocusEvent } from './core/events/focus-event.js' -export { FocusManager } from './core/focus.js' -export { Ansi } from './core/Ansi.js' -export { stringWidth } from './core/stringWidth.js' -export { default as wrapText } from './core/wrap-text.js' -export { default as measureElement } from './core/measure-element.js' -export { supportsTabStatus } from './core/termio/osc.js' -export { setClipboard, getClipboardPath, CLEAR_ITERM2_PROGRESS, CLEAR_TAB_STATUS, CLEAR_TERMINAL_TITLE, wrapForMultiplexer } from './core/termio/osc.js' -export { DISABLE_KITTY_KEYBOARD, DISABLE_MODIFY_OTHER_KEYS } from './core/termio/csi.js' -export { SHOW_CURSOR, DBP, DFE, DISABLE_MOUSE_TRACKING, EXIT_ALT_SCREEN, HIDE_CURSOR, ENTER_ALT_SCREEN, ENABLE_MOUSE_TRACKING } from './core/termio/dec.js' -export { default as instances } from './core/instances.js' -export { default as renderBorder, type BorderTextOptions } from './core/render-border.js' -export { isSynchronizedOutputSupported, isXtermJs, hasCursorUpViewportYankBug, writeDiffToTerminal } from './core/terminal.js' -export { colorize, applyColor, applyTextStyles, type ColorType } from './core/colorize.js' -export { wrapAnsi } from './core/wrapAnsi.js' -export { default as styles } from './core/styles.js' -export { clamp } from './core/layout/geometry.js' -export { getTerminalFocusState, getTerminalFocused, subscribeTerminalFocus } from './core/terminal-focus-state.js' -export { supportsHyperlinks } from './core/supports-hyperlinks.js' - -// ============================================================ -// Components (Layer 2) -// ============================================================ -export { default as BaseBox } from './components/Box.js' -export type { Props as BaseBoxProps } from './components/Box.js' -export { default as BaseText } from './components/Text.js' -export type { Props as BaseTextProps } from './components/Text.js' -export { default as Button, type ButtonState, type Props as ButtonProps } from './components/Button.js' -export { default as Link } from './components/Link.js' -export type { Props as LinkProps } from './components/Link.js' -export { default as Newline } from './components/Newline.js' -export type { Props as NewlineProps } from './components/Newline.js' -export { default as Spacer } from './components/Spacer.js' -export { NoSelect } from './components/NoSelect.js' -export { RawAnsi } from './components/RawAnsi.js' -export { default as ScrollBox, type ScrollBoxHandle } from './components/ScrollBox.js' -export { AlternateScreen } from './components/AlternateScreen.js' - -// App types -export type { Props as AppProps } from './components/AppContext.js' -export type { Props as StdinProps } from './components/StdinContext.js' -export { TerminalSizeContext, type TerminalSize } from './components/TerminalSizeContext.js' - -// ============================================================ -// Hooks -// ============================================================ -export { default as useApp } from './hooks/use-app.js' -export { default as useInput } from './hooks/use-input.js' -export { useAnimationFrame } from './hooks/use-animation-frame.js' -export { useAnimationTimer, useInterval } from './hooks/use-interval.js' -export { useSelection, useHasSelection } from './hooks/use-selection.js' -export { default as useStdin } from './hooks/use-stdin.js' -export { useTabStatus, type TabStatusKind } from './hooks/use-tab-status.js' -export { useTerminalFocus } from './hooks/use-terminal-focus.js' -export { useTerminalTitle } from './hooks/use-terminal-title.js' -export { useTerminalViewport } from './hooks/use-terminal-viewport.js' -export { useSearchHighlight } from './hooks/use-search-highlight.js' -export { useDeclaredCursor } from './hooks/use-declared-cursor.js' -export { TerminalWriteProvider, useTerminalNotification, type TerminalNotification } from './hooks/useTerminalNotification.js' - -// ============================================================ -// Theme (Layer 3) -// ============================================================ -export { - ThemeProvider, - usePreviewTheme, - useTheme, - useThemeSetting, -} from './theme/ThemeProvider.js' -export { default as Box } from './theme/ThemedBox.js' -export type { Props as BoxProps } from './theme/ThemedBox.js' -export { default as Text } from './theme/ThemedText.js' -export type { Props as TextProps } from './theme/ThemedText.js' -export { color } from './theme/color.js' - -// Theme sub-components -export { Dialog } from './theme/Dialog.js' -export { Divider } from './theme/Divider.js' -export { FuzzyPicker } from './theme/FuzzyPicker.js' -export { ListItem } from './theme/ListItem.js' -export { LoadingState } from './theme/LoadingState.js' -export { Pane } from './theme/Pane.js' -export { ProgressBar } from './theme/ProgressBar.js' -export { Ratchet } from './theme/Ratchet.js' -export { StatusIcon } from './theme/StatusIcon.js' -export { Tabs } from './theme/Tabs.js' -export { Byline } from './theme/Byline.js' -export { KeyboardShortcutHint } from './theme/KeyboardShortcutHint.js' diff --git a/packages/@ant/ink/src/theme/Byline.tsx b/packages/@ant/ink/src/theme/Byline.tsx deleted file mode 100644 index bb34e52fc..000000000 --- a/packages/@ant/ink/src/theme/Byline.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import React, { Children, isValidElement } from 'react' -import { Text } from '../index.js' - -type Props = { - /** The items to join with a middot separator */ - children: React.ReactNode -} - -/** - * Joins children with a middot separator (" · ") for inline metadata display. - * - * Named after the publishing term "byline" - the line of metadata typically - * shown below a title (e.g., "John Doe · 5 min read · Mar 12"). - * - * Automatically filters out null/undefined/false children and only renders - * separators between valid elements. - * - * @example - * // Basic usage: "Enter to confirm · Esc to cancel" - * - * - * - * - * - * - * - * @example - * // With conditional children: "Esc to cancel" (only one item shown) - * - * - * {showEnter && } - * - * - * - * - */ -export function Byline({ children }: Props): React.ReactNode { - // Children.toArray already filters out null, undefined, and booleans - const validChildren = Children.toArray(children) - - if (validChildren.length === 0) { - return null - } - - return ( - <> - {validChildren.map((child, index) => ( - - {index > 0 && · } - {child} - - ))} - - ) -} diff --git a/packages/@ant/ink/src/theme/ConfigurableShortcutHint.tsx b/packages/@ant/ink/src/theme/ConfigurableShortcutHint.tsx deleted file mode 100644 index 85911d387..000000000 --- a/packages/@ant/ink/src/theme/ConfigurableShortcutHint.tsx +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Simplified ConfigurableShortcutHint for the standalone @anthropic/ink package. - * - * The full version reads user-configured keybindings via useShortcutDisplay. - * This stub just renders the fallback shortcut — sufficient for the package's - * internal theme components. - */ - -import React from 'react' -import { KeyboardShortcutHint } from './KeyboardShortcutHint.js' - -type Props = { - action: string - context: string - fallback: string - description: string - parens?: boolean - bold?: boolean -} - -export function ConfigurableShortcutHint({ - fallback, - description, - parens, - bold, -}: Props): React.ReactNode { - return ( - - ) -} diff --git a/packages/@ant/ink/src/theme/Dialog.tsx b/packages/@ant/ink/src/theme/Dialog.tsx deleted file mode 100644 index 3b60b3587..000000000 --- a/packages/@ant/ink/src/theme/Dialog.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import React from 'react' -import { - type ExitState, - useExitOnCtrlCDWithKeybindings, -} from '../hooks/useExitOnCtrlCD.js' -import { Box, Text } from '../index.js' -import { useKeybinding } from './keybindings.js' -import type { Theme } from './theme-types.js' -import { ConfigurableShortcutHint } from './ConfigurableShortcutHint.js' -import { Byline } from './Byline.js' -import { KeyboardShortcutHint } from './KeyboardShortcutHint.js' -import { Pane } from './Pane.js' - -type DialogProps = { - title: React.ReactNode - subtitle?: React.ReactNode - children: React.ReactNode - onCancel: () => void - color?: keyof Theme - hideInputGuide?: boolean - hideBorder?: boolean - /** Custom input guide content. Receives exitState for Ctrl+C/D pending display. */ - inputGuide?: (exitState: ExitState) => React.ReactNode - /** - * Controls whether Dialog's built-in confirm:no (Esc/n) and app:exit/interrupt - * (Ctrl-C/D) keybindings are active. Set to `false` while an embedded text - * field is being edited so those keys reach the field instead of being - * consumed by Dialog. TextInput has its own ctrl+c/d handlers (cancel on - * press, delete-forward on ctrl+d with text). Defaults to `true`. - */ - isCancelActive?: boolean -} - -export function Dialog({ - title, - subtitle, - children, - onCancel, - color = 'permission', - hideInputGuide, - hideBorder, - inputGuide, - isCancelActive = true, -}: DialogProps): React.ReactNode { - const exitState = useExitOnCtrlCDWithKeybindings( - undefined, - undefined, - isCancelActive, - ) - - // Use configurable keybinding for ESC to cancel. - // isCancelActive lets consumers (e.g. ElicitationDialog) disable this while - // an embedded TextInput is focused, so that keys like 'n' reach the field - // instead of being consumed here. - useKeybinding('confirm:no', onCancel, { - context: 'Confirmation', - isActive: isCancelActive, - }) - - const defaultInputGuide = exitState.pending ? ( - Press {exitState.keyName} again to exit - ) : ( - - - - - ) - - const content = ( - <> - - - - {title} - - {subtitle && {subtitle}} - - {children} - - {!hideInputGuide && ( - - - {inputGuide ? inputGuide(exitState) : defaultInputGuide} - - - )} - - ) - - if (hideBorder) { - return content - } - - return {content} -} diff --git a/packages/@ant/ink/src/theme/Divider.tsx b/packages/@ant/ink/src/theme/Divider.tsx deleted file mode 100644 index 077546a75..000000000 --- a/packages/@ant/ink/src/theme/Divider.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import React from 'react' -import { useTerminalSize } from '../hooks/useTerminalSize.js' -import { stringWidth } from '../core/stringWidth.js' -import { Ansi, Text } from '../index.js' -import type { Theme } from './theme-types.js' - -type DividerProps = { - /** - * Width of the divider in characters. - * Defaults to terminal width. - */ - width?: number - - /** - * Theme color for the divider. - * If not provided, dimColor is used. - */ - color?: keyof Theme - - /** - * Character to use for the divider line. - * @default '─' - */ - char?: string - - /** - * Padding to subtract from the width (e.g., for indentation). - * @default 0 - */ - padding?: number - - /** - * Title shown in the middle of the divider. - * May contain ANSI codes (e.g., chalk-styled text). - * - * @example - * // ─────────── Title ─────────── - * - */ - title?: string -} - -/** - * A horizontal divider line. - * - * @example - * // Full-width dimmed divider - * - * - * @example - * // Colored divider - * - * - * @example - * // Fixed width - * - * - * @example - * // Full width minus padding (for indented content) - * - * - * @example - * // With centered title - * - */ -export function Divider({ - width, - color, - char = '─', - padding = 0, - title, -}: DividerProps): React.ReactNode { - const { columns: terminalWidth } = useTerminalSize() - const effectiveWidth = Math.max(0, (width ?? terminalWidth) - padding) - - if (title) { - const titleWidth = stringWidth(title) + 2 // +2 for spaces around title - const sideWidth = Math.max(0, effectiveWidth - titleWidth) - const leftWidth = Math.floor(sideWidth / 2) - const rightWidth = sideWidth - leftWidth - return ( - - {char.repeat(leftWidth)}{' '} - - {title} - {' '} - {char.repeat(rightWidth)} - - ) - } - - return ( - - {char.repeat(effectiveWidth)} - - ) -} diff --git a/packages/@ant/ink/src/theme/FuzzyPicker.tsx b/packages/@ant/ink/src/theme/FuzzyPicker.tsx deleted file mode 100644 index 642c09e7d..000000000 --- a/packages/@ant/ink/src/theme/FuzzyPicker.tsx +++ /dev/null @@ -1,350 +0,0 @@ -import * as React from 'react' -import { useEffect, useState } from 'react' -import { useSearchInput } from '../hooks/useSearchInput.js' -import { useTerminalSize } from '../hooks/useTerminalSize.js' -import type { KeyboardEvent } from '../core/events/keyboard-event.js' -import { clamp } from '../core/layout/geometry.js' -import { Box, Text, useTerminalFocus } from '../index.js' -import { SearchBox } from './SearchBox.js' -import { Byline } from './Byline.js' -import { KeyboardShortcutHint } from './KeyboardShortcutHint.js' -import { ListItem } from './ListItem.js' -import { Pane } from './Pane.js' - -type PickerAction = { - /** Hint label shown in the byline, e.g. "mention" → "Tab to mention". */ - action: string - handler: (item: T) => void -} - -type Props = { - title: string - placeholder?: string - initialQuery?: string - items: readonly T[] - getKey: (item: T) => string - /** Keep to one line — preview handles overflow. */ - renderItem: (item: T, isFocused: boolean) => React.ReactNode - renderPreview?: (item: T) => React.ReactNode - /** 'right' keeps hints stable (no bounce), but needs width. */ - previewPosition?: 'bottom' | 'right' - visibleCount?: number - /** - * 'up' puts items[0] at the bottom next to the input (atuin-style). Arrows - * always match screen direction — ↑ walks visually up regardless. - */ - direction?: 'down' | 'up' - /** Caller owns filtering: re-filter on each call and pass new items. */ - onQueryChange: (query: string) => void - /** Enter key. Primary action. */ - onSelect: (item: T) => void - /** - * Tab key. If provided, Tab no longer aliases Enter — it gets its own - * handler and hint. Shift+Tab falls through to this if onShiftTab is unset. - */ - onTab?: PickerAction - /** Shift+Tab key. Gets its own hint. */ - onShiftTab?: PickerAction - /** - * Fires when the focused item changes (via arrows or when items reset). - * Useful for async preview loading — keeps I/O out of renderPreview. - */ - onFocus?: (item: T | undefined) => void - onCancel: () => void - /** Shown when items is empty. Caller bakes loading/searching state into this. */ - emptyMessage?: string | ((query: string) => string) - /** - * Status line below the list, e.g. "500+ matches" or "42 matches…". - * Caller decides when to show it — pass undefined to hide. - */ - matchLabel?: string - selectAction?: string - extraHints?: React.ReactNode -} - -const DEFAULT_VISIBLE = 8 -// Pane (paddingTop + Divider) + title + 3 gaps + SearchBox (rounded border = 3 -// rows) + hints. matchLabel adds +1 when present, accounted for separately. -const CHROME_ROWS = 10 -const MIN_VISIBLE = 2 - -export function FuzzyPicker({ - title, - placeholder = 'Type to search…', - initialQuery, - items, - getKey, - renderItem, - renderPreview, - previewPosition = 'bottom', - visibleCount: requestedVisible = DEFAULT_VISIBLE, - direction = 'down', - onQueryChange, - onSelect, - onTab, - onShiftTab, - onFocus, - onCancel, - emptyMessage = 'No results', - matchLabel, - selectAction = 'select', - extraHints, -}: Props): React.ReactNode { - const isTerminalFocused = useTerminalFocus() - const { rows, columns } = useTerminalSize() - const [focusedIndex, setFocusedIndex] = useState(0) - - // Cap visibleCount so the picker never exceeds the terminal height. When it - // overflows, each re-render (arrow key, ctrl+p) mis-positions the cursor-up - // by the overflow amount and a previously-drawn line flashes blank. - const visibleCount = Math.max( - MIN_VISIBLE, - Math.min(requestedVisible, rows - CHROME_ROWS - (matchLabel ? 1 : 0)), - ) - - // Full hint row with onTab+onShiftTab is ~100 chars and wraps inconsistently - // below that. Compact mode drops shift+tab and shortens labels. - const compact = columns < 120 - - const step = (delta: 1 | -1) => { - setFocusedIndex(i => clamp(i + delta, 0, items.length - 1)) - } - - // onKeyDown fires after useSearchInput's useInput, so onExit must be a - // no-op — return/downArrow are handled by handleKeyDown below. onCancel - // still covers escape/ctrl+c/ctrl+d. Backspace-on-empty is disabled so - // a held backspace doesn't eject the user from the dialog. - const { query, cursorOffset } = useSearchInput({ - isActive: true, - onExit: () => {}, - onCancel, - initialQuery, - backspaceExitsOnEmpty: false, - }) - - const handleKeyDown = (e: KeyboardEvent) => { - if (e.key === 'up' || (e.ctrl && e.key === 'p')) { - e.preventDefault() - e.stopImmediatePropagation() - step(direction === 'up' ? 1 : -1) - return - } - if (e.key === 'down' || (e.ctrl && e.key === 'n')) { - e.preventDefault() - e.stopImmediatePropagation() - step(direction === 'up' ? -1 : 1) - return - } - if (e.key === 'return') { - e.preventDefault() - e.stopImmediatePropagation() - const selected = items[focusedIndex] - if (selected) onSelect(selected) - return - } - if (e.key === 'tab') { - e.preventDefault() - e.stopImmediatePropagation() - const selected = items[focusedIndex] - if (!selected) return - const tabAction = e.shift ? (onShiftTab ?? onTab) : onTab - if (tabAction) { - tabAction.handler(selected) - } else { - onSelect(selected) - } - } - } - - useEffect(() => { - onQueryChange(query) - setFocusedIndex(0) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [query]) - - useEffect(() => { - setFocusedIndex(i => clamp(i, 0, items.length - 1)) - }, [items.length]) - - const focused = items[focusedIndex] - useEffect(() => { - onFocus?.(focused) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [focused]) - - const windowStart = clamp( - focusedIndex - visibleCount + 1, - 0, - items.length - visibleCount, - ) - const visible = items.slice(windowStart, windowStart + visibleCount) - - const emptyText = - typeof emptyMessage === 'function' ? emptyMessage(query) : emptyMessage - - const searchBox = ( - - ) - - const listBlock = ( - - ) - - const preview = - renderPreview && focused ? ( - - {renderPreview(focused)} - - ) : null - - // Structure must not depend on preview truthiness — when focused goes - // undefined (e.g. delete clears matches), switching row→fragment would - // change both layout AND gap count, bouncing the searchBox below. - const listGroup = - renderPreview && previewPosition === 'right' ? ( - - - {listBlock} - {matchLabel && {matchLabel}} - - {preview ?? } - - ) : ( - // Box (not fragment) so the outer gap={1} doesn't insert a blank line - // between list/matchLabel/preview — that read as extra space above the - // prompt in direction='up'. - - {listBlock} - {matchLabel && {matchLabel}} - {preview} - - ) - - const inputAbove = direction !== 'up' - return ( - - - - {title} - - {inputAbove && searchBox} - {listGroup} - {!inputAbove && searchBox} - - - - - {onTab && ( - - )} - {onShiftTab && !compact && ( - - )} - - {extraHints} - - - - - ) -} - -type ListProps = Pick< - Props, - 'visibleCount' | 'direction' | 'getKey' | 'renderItem' -> & { - visible: readonly T[] - windowStart: number - total: number - focusedIndex: number - emptyText: string -} - -function List({ - visible, - windowStart, - visibleCount, - total, - focusedIndex, - direction, - getKey, - renderItem, - emptyText, -}: ListProps): React.ReactNode { - if (visible.length === 0) { - return ( - - {emptyText} - - ) - } - - const rows = visible.map((item, i) => { - const actualIndex = windowStart + i - const isFocused = actualIndex === focusedIndex - const atLowEdge = i === 0 && windowStart > 0 - const atHighEdge = - i === visible.length - 1 && windowStart + visibleCount! < total - return ( - - {renderItem(item, isFocused)} - - ) - }) - - return ( - - {rows} - - ) -} - -function firstWord(s: string): string { - const i = s.indexOf(' ') - return i === -1 ? s : s.slice(0, i) -} diff --git a/packages/@ant/ink/src/theme/KeyboardShortcutHint.tsx b/packages/@ant/ink/src/theme/KeyboardShortcutHint.tsx deleted file mode 100644 index 30d4d2ccc..000000000 --- a/packages/@ant/ink/src/theme/KeyboardShortcutHint.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import React from 'react' -import Text from '../components/Text.js' - -type Props = { - /** The key or chord to display (e.g., "ctrl+o", "Enter", "↑/↓") */ - shortcut: string - /** The action the key performs (e.g., "expand", "select", "navigate") */ - action: string - /** Whether to wrap the hint in parentheses. Default: false */ - parens?: boolean - /** Whether to render the shortcut in bold. Default: false */ - bold?: boolean -} - -/** - * Renders a keyboard shortcut hint like "ctrl+o to expand" or "(tab to toggle)" - * - * Wrap in for the common dim styling. - * - * @example - * // Simple hint wrapped in dim Text - * - * - * // With parentheses: "(ctrl+o to expand)" - * - * - * // With bold shortcut: "Enter to confirm" (Enter is bold) - * - * - * // Multiple hints with middot separator - use Byline - * - * - * - * - * - * - */ -export function KeyboardShortcutHint({ - shortcut, - action, - parens = false, - bold = false, -}: Props): React.ReactNode { - const shortcutText = bold ? {shortcut} : shortcut - - if (parens) { - return ( - - ({shortcutText} to {action}) - - ) - } - return ( - - {shortcutText} to {action} - - ) -} diff --git a/packages/@ant/ink/src/theme/ListItem.tsx b/packages/@ant/ink/src/theme/ListItem.tsx deleted file mode 100644 index b74619de1..000000000 --- a/packages/@ant/ink/src/theme/ListItem.tsx +++ /dev/null @@ -1,188 +0,0 @@ -import figures from 'figures' -import type { ReactNode } from 'react' -import React from 'react' -import { useDeclaredCursor } from '../hooks/use-declared-cursor.js' -import { Box, Text } from '../index.js' - -type ListItemProps = { - /** - * Whether this item is currently focused (keyboard selection). - * Shows the pointer indicator (❯) when true. - */ - isFocused: boolean - - /** - * Whether this item is selected (chosen/checked). - * Shows the checkmark indicator (✓) when true. - * @default false - */ - isSelected?: boolean - - /** - * The content to display for this item. - */ - children: ReactNode - - /** - * Optional description text displayed below the main content. - */ - description?: string - - /** - * Show a down arrow indicator instead of pointer (for scroll hints). - * Only applies when not focused. - */ - showScrollDown?: boolean - - /** - * Show an up arrow indicator instead of pointer (for scroll hints). - * Only applies when not focused. - */ - showScrollUp?: boolean - - /** - * Whether to apply automatic styling to the children based on focus/selection state. - * - When true (default): children are wrapped in Text with state-based colors - * - When false: children are rendered as-is, allowing custom styling - * @default true - */ - styled?: boolean - - /** - * Whether this item is disabled. Disabled items show dimmed text and no indicators. - * @default false - */ - disabled?: boolean - - /** - * Whether this ListItem should declare the terminal cursor position. - * Set false when a child (e.g. BaseTextInput) declares its own cursor. - * @default true - */ - declareCursor?: boolean -} - -/** - * A list item component for selection UIs (dropdowns, multi-selects, menus). - * - * Handles the common pattern of: - * - Pointer indicator (❯) for focused items - * - Checkmark indicator (✓) for selected items - * - Scroll indicators (↓↑) for truncated lists - * - Color states for focus/selection - * - * @example - * // Basic usage in a selection list - * {options.map((option, i) => ( - * - * {option.label} - * - * ))} - * - * @example - * // With scroll indicators - * First visible item - * ... - * Last visible item - * - * @example - * // With description - * - * Primary text - * - * - * @example - * // Custom children styling (styled=false) - * - * Custom styled content - * - */ -export function ListItem({ - isFocused, - isSelected = false, - children, - description, - showScrollDown, - showScrollUp, - styled = true, - disabled = false, - declareCursor, -}: ListItemProps): React.ReactNode { - // Determine which indicator to show - function renderIndicator(): ReactNode { - if (disabled) { - return - } - - if (isFocused) { - return {figures.pointer} - } - - if (showScrollDown) { - return {figures.arrowDown} - } - - if (showScrollUp) { - return {figures.arrowUp} - } - - return - } - - // Determine text color based on state - function getTextColor(): 'success' | 'suggestion' | 'inactive' | undefined { - if (disabled) { - return 'inactive' - } - - if (!styled) { - return undefined - } - - if (isSelected) { - return 'success' - } - - if (isFocused) { - return 'suggestion' - } - - return undefined - } - - const textColor = getTextColor() - - // Park the native terminal cursor on the pointer indicator so screen - // readers / magnifiers track the focused item. (0,0) is the top-left of - // this Box, where the pointer renders. - const cursorRef = useDeclaredCursor({ - line: 0, - column: 0, - active: isFocused && !disabled && declareCursor !== false, - }) - - return ( - - - {renderIndicator()} - {styled ? ( - - {children} - - ) : ( - children - )} - {isSelected && !disabled && {figures.tick}} - - {description && ( - - {description} - - )} - - ) -} diff --git a/packages/@ant/ink/src/theme/LoadingState.tsx b/packages/@ant/ink/src/theme/LoadingState.tsx deleted file mode 100644 index ec1459cee..000000000 --- a/packages/@ant/ink/src/theme/LoadingState.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import React from 'react' -import { Box, Text } from '../index.js' -import { Spinner } from './Spinner.js' - -type LoadingStateProps = { - /** - * The loading message to display next to the spinner. - */ - message: string - - /** - * Display the message in bold. - * @default false - */ - bold?: boolean - - /** - * Display the message in dimmed color. - * @default false - */ - dimColor?: boolean - - /** - * Optional subtitle displayed below the main message. - */ - subtitle?: string -} - -/** - * A spinner with loading message for async operations. - * - * @example - * // Basic loading - * - * - * @example - * // Bold loading message - * - * - * @example - * // With subtitle - * - */ -export function LoadingState({ - message, - bold = false, - dimColor = false, - subtitle, -}: LoadingStateProps): React.ReactNode { - return ( - - - - - {' '} - {message} - - - {subtitle && {subtitle}} - - ) -} diff --git a/packages/@ant/ink/src/theme/Pane.tsx b/packages/@ant/ink/src/theme/Pane.tsx deleted file mode 100644 index d6868faec..000000000 --- a/packages/@ant/ink/src/theme/Pane.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import React from 'react' -import { useIsInsideModal } from './modalContext.js' -import { Box } from '../index.js' -import type { Theme } from './theme-types.js' -import { Divider } from './Divider.js' - -type PaneProps = { - children: React.ReactNode - /** - * Theme color for the top border line. - */ - color?: keyof Theme -} - -/** - * A pane — a region of the terminal that appears below the REPL prompt, - * bounded by a colored top line with a one-row gap above and horizontal - * padding. Used by all slash-command screens: /config, /help, /plugins, - * /sandbox, /stats, /permissions. - * - * For confirm/cancel dialogs (Esc to dismiss, Enter to confirm), use - * `` instead — it registers its own keybindings. For a full - * rounded-border card, use ``. - * - * Submenus rendered inside a Pane should use `hideBorder` on their Dialog - * so the Pane's border remains the single frame. - * - * @example - * - * ... - * - */ -export function Pane({ children, color }: PaneProps): React.ReactNode { - // When rendered inside FullscreenLayout's modal slot, its ▔ divider IS - // the frame. Skip our own Divider (would double-frame) and the extra top - // padding. This lets slash-command screens that wrap in Pane (e.g. - // /model → ModelPicker) route through the modal slot unchanged. - if (useIsInsideModal()) { - // flexShrink=0: the modal slot's absolute Box has no explicit height - // (grows to fit, maxHeight cap). With flexGrow=1, re-renders cause - // yoga to resolve this Box's height to 0 against the undetermined - // parent — /permissions body blanks on Down arrow. See #23592. - return ( - - {children} - - ) - } - return ( - - - - {children} - - - ) -} diff --git a/packages/@ant/ink/src/theme/ProgressBar.tsx b/packages/@ant/ink/src/theme/ProgressBar.tsx deleted file mode 100644 index 1d5c1f674..000000000 --- a/packages/@ant/ink/src/theme/ProgressBar.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import React from 'react' -import { Text } from '../index.js' -import type { Theme } from './theme-types.js' - -type Props = { - /** - * How much progress to display, between 0 and 1 inclusive - */ - ratio: number // [0, 1] - - /** - * How many characters wide to draw the progress bar - */ - width: number // how many characters wide - - /** - * Optional color for the filled portion of the bar - */ - fillColor?: keyof Theme - - /** - * Optional color for the empty portion of the bar - */ - emptyColor?: keyof Theme -} - -const BLOCKS = [' ', '▏', '▎', '▍', '▌', '▋', '▊', '▉', '█'] - -export function ProgressBar({ - ratio: inputRatio, - width, - fillColor, - emptyColor, -}: Props): React.ReactNode { - const ratio = Math.min(1, Math.max(0, inputRatio)) - const whole = Math.floor(ratio * width) - const segments = [BLOCKS[BLOCKS.length - 1]!.repeat(whole)] - if (whole < width) { - const remainder = ratio * width - whole - const middle = Math.floor(remainder * BLOCKS.length) - segments.push(BLOCKS[middle]!) - - const empty = width - whole - 1 - if (empty > 0) { - segments.push(BLOCKS[0]!.repeat(empty)) - } - } - - return ( - - {segments.join('')} - - ) -} diff --git a/packages/@ant/ink/src/theme/Ratchet.tsx b/packages/@ant/ink/src/theme/Ratchet.tsx deleted file mode 100644 index dbb80c3f4..000000000 --- a/packages/@ant/ink/src/theme/Ratchet.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import React, { useCallback, useLayoutEffect, useRef, useState } from 'react' -import { useTerminalSize } from '../hooks/useTerminalSize.js' -import { useTerminalViewport } from '../hooks/use-terminal-viewport.js' -import { Box, type DOMElement, measureElement } from '../index.js' - -type Props = { - children: React.ReactNode - lock?: 'always' | 'offscreen' -} - -export function Ratchet({ children, lock = 'always' }: Props): React.ReactNode { - const [viewportRef, { isVisible }] = useTerminalViewport() - const { rows } = useTerminalSize() - const innerRef = useRef(null) - const maxHeight = useRef(0) - const [minHeight, setMinHeight] = useState(0) - - const outerRef = useCallback( - (el: DOMElement | null) => { - viewportRef(el) - }, - [viewportRef], - ) - - const engaged = lock === 'always' || !isVisible - - useLayoutEffect(() => { - if (!innerRef.current) { - return - } - const { height } = measureElement(innerRef.current) - if (height > maxHeight.current) { - maxHeight.current = Math.min(height, rows) - setMinHeight(maxHeight.current) - } - }) - - return ( - - - {children} - - - ) -} diff --git a/packages/@ant/ink/src/theme/SearchBox.tsx b/packages/@ant/ink/src/theme/SearchBox.tsx deleted file mode 100644 index bf716be9e..000000000 --- a/packages/@ant/ink/src/theme/SearchBox.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import React from 'react' -import { Box, Text } from '../index.js' - -type Props = { - query: string - placeholder?: string - isFocused: boolean - isTerminalFocused: boolean - prefix?: string - width?: number | string - cursorOffset?: number - borderless?: boolean -} - -export function SearchBox({ - query, - placeholder = 'Search…', - isFocused, - isTerminalFocused, - prefix = '\u2315', - width, - cursorOffset, - borderless = false, -}: Props): React.ReactNode { - const offset = cursorOffset ?? query.length - - return ( - - - {prefix}{' '} - {isFocused ? ( - <> - {query ? ( - isTerminalFocused ? ( - <> - {query.slice(0, offset)} - - {offset < query.length ? query[offset] : ' '} - - {offset < query.length && ( - {query.slice(offset + 1)} - )} - - ) : ( - {query} - ) - ) : isTerminalFocused ? ( - <> - {placeholder.charAt(0)} - {placeholder.slice(1)} - - ) : ( - {placeholder} - )} - - ) : query ? ( - {query} - ) : ( - {placeholder} - )} - - - ) -} diff --git a/packages/@ant/ink/src/theme/Spinner.tsx b/packages/@ant/ink/src/theme/Spinner.tsx deleted file mode 100644 index 2d85ec08c..000000000 --- a/packages/@ant/ink/src/theme/Spinner.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React, { useState, useEffect } from 'react' -import { Text } from '../index.js' - -const FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'] - -/** - * A simple animated spinner for loading states. - */ -export function Spinner(): React.ReactNode { - const [frame, setFrame] = useState(0) - - useEffect(() => { - const timer = setInterval(() => { - setFrame(f => (f + 1) % FRAMES.length) - }, 80) - return () => clearInterval(timer) - }, []) - - return {FRAMES[frame]} -} diff --git a/packages/@ant/ink/src/theme/StatusIcon.tsx b/packages/@ant/ink/src/theme/StatusIcon.tsx deleted file mode 100644 index 943d6be25..000000000 --- a/packages/@ant/ink/src/theme/StatusIcon.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import figures from 'figures' -import React from 'react' -import { Text } from '../index.js' - -type Status = 'success' | 'error' | 'warning' | 'info' | 'pending' | 'loading' - -type Props = { - /** - * The status to display. Determines both the icon and color. - * - * - `success`: Green checkmark (✓) - * - `error`: Red cross (✗) - * - `warning`: Yellow warning symbol (⚠) - * - `info`: Blue info symbol (ℹ) - * - `pending`: Dimmed circle (○) - * - `loading`: Dimmed ellipsis (…) - */ - status: Status - /** - * Include a trailing space after the icon. Useful when followed by text. - * @default false - */ - withSpace?: boolean -} - -const STATUS_CONFIG: Record< - Status, - { - icon: string - color: 'success' | 'error' | 'warning' | 'suggestion' | undefined - } -> = { - success: { icon: figures.tick, color: 'success' }, - error: { icon: figures.cross, color: 'error' }, - warning: { icon: figures.warning, color: 'warning' }, - info: { icon: figures.info, color: 'suggestion' }, - pending: { icon: figures.circle, color: undefined }, - loading: { icon: '…', color: undefined }, -} - -/** - * Renders a status indicator icon with appropriate color. - * - * @example - * // Success indicator - * - * - * @example - * // Error with trailing space for text - * Failed to connect - * - * @example - * // Status line pattern - * - * - * Waiting for response - * - */ -export function StatusIcon({ - status, - withSpace = false, -}: Props): React.ReactNode { - const config = STATUS_CONFIG[status] - - return ( - - {config.icon} - {withSpace && ' '} - - ) -} diff --git a/packages/@ant/ink/src/theme/Tabs.tsx b/packages/@ant/ink/src/theme/Tabs.tsx deleted file mode 100644 index cec38cf7e..000000000 --- a/packages/@ant/ink/src/theme/Tabs.tsx +++ /dev/null @@ -1,339 +0,0 @@ -import React, { - createContext, - useCallback, - useContext, - useEffect, - useState, -} from 'react' -import { - useIsInsideModal, - useModalScrollRef, -} from './modalContext.js' -import { useTerminalSize } from '../hooks/useTerminalSize.js' -import ScrollBox from '../components/ScrollBox.js' -import type { KeyboardEvent } from '../core/events/keyboard-event.js' -import { stringWidth } from '../core/stringWidth.js' -import { Box, Text } from '../index.js' -import { useKeybindings } from './keybindings.js' -import type { Theme } from './theme-types.js' - -type TabsProps = { - children: Array> - title?: string - color?: keyof Theme - defaultTab?: string - hidden?: boolean - useFullWidth?: boolean - /** Controlled mode: current selected tab id/title */ - selectedTab?: string - /** Controlled mode: callback when tab changes */ - onTabChange?: (tabId: string) => void - /** Optional banner to display below tabs header */ - banner?: React.ReactNode - /** Disable keyboard navigation (e.g. when a child component handles arrow keys) */ - disableNavigation?: boolean - /** - * Initial focus state for the tab header row. Defaults to true (header - * focused, nav always works). Keep the default for Select/list content — - * those only use up/down so there's no conflict; pass - * isDisabled={headerFocused} to the Select instead. Only set false when - * content actually binds left/right/tab (e.g. enum cycling), and show a - * "↑ tabs" footer hint — without it tabs look broken. - */ - initialHeaderFocused?: boolean - /** - * Fixed height for the content area. When set, all tabs render within the - * same height (overflow hidden) so switching tabs doesn't cause layout - * shifts. Shorter tabs get whitespace; taller tabs are clipped. - */ - contentHeight?: number - /** - * Let Tab/←/→ switch tabs from focused content. Opt-in since some - * content uses those keys; pass a reactive boolean to cede them when - * needed. Switching from content focuses the header. - */ - navFromContent?: boolean -} - -type TabsContextValue = { - selectedTab: string | undefined - width: number | undefined - headerFocused: boolean - focusHeader: () => void - blurHeader: () => void - registerOptIn: () => () => void -} - -const TabsContext = createContext({ - selectedTab: undefined, - width: undefined, - // Default for components rendered outside a Tabs (tests, standalone): - // content has focus, focusHeader is a no-op. - headerFocused: false, - focusHeader: () => {}, - blurHeader: () => {}, - registerOptIn: () => () => {}, -}) - -export function Tabs({ - title, - color, - defaultTab, - children, - hidden, - useFullWidth, - selectedTab: controlledSelectedTab, - onTabChange, - banner, - disableNavigation, - initialHeaderFocused = true, - contentHeight, - navFromContent = false, -}: TabsProps): React.ReactNode { - const { columns: terminalWidth } = useTerminalSize() - const tabs = children.map(child => [ - child.props.id ?? child.props.title, - child.props.title, - ]) - const defaultTabIndex = defaultTab - ? tabs.findIndex(tab => defaultTab === tab[0]) - : 0 - - // Support both controlled and uncontrolled modes - const isControlled = controlledSelectedTab !== undefined - const [internalSelectedTab, setInternalSelectedTab] = useState( - defaultTabIndex !== -1 ? defaultTabIndex : 0, - ) - - // In controlled mode, find the index of the controlled tab - const controlledTabIndex = isControlled - ? tabs.findIndex(tab => tab[0] === controlledSelectedTab) - : -1 - const selectedTabIndex = isControlled - ? controlledTabIndex !== -1 - ? controlledTabIndex - : 0 - : internalSelectedTab - - const modalScrollRef = useModalScrollRef() - - // Header focus: left/right/tab only switch tabs when the header row is - // focused. Children with interactive content call focusHeader() (via - // useTabHeaderFocus) on up-arrow to hand focus back here; down-arrow - // returns it. Tabs that never call the hook see no behavior change — - // initialHeaderFocused defaults to true so nav always works. - const [headerFocused, setHeaderFocused] = useState(initialHeaderFocused) - const focusHeader = useCallback(() => setHeaderFocused(true), []) - const blurHeader = useCallback(() => setHeaderFocused(false), []) - // Count of mounted children using useTabHeaderFocus(). Down-arrow blur and - // the ↓ hint only engage when at least one child has opted in — otherwise - // pressing down on a legacy tab would strand the user with nav disabled. - const [optInCount, setOptInCount] = useState(0) - const registerOptIn = useCallback(() => { - setOptInCount(n => n + 1) - return () => setOptInCount(n => n - 1) - }, []) - const optedIn = optInCount > 0 - - const handleTabChange = (offset: number) => { - const newIndex = (selectedTabIndex + tabs.length + offset) % tabs.length - const newTabId = tabs[newIndex]?.[0] - - if (isControlled && onTabChange && newTabId) { - onTabChange(newTabId) - } else { - setInternalSelectedTab(newIndex) - } - // Tab switching is a header action — stay focused so the user can keep - // cycling. The newly mounted tab can blur via its own interaction. - setHeaderFocused(true) - } - - useKeybindings( - { - 'tabs:next': () => handleTabChange(1), - 'tabs:previous': () => handleTabChange(-1), - }, - { - context: 'Tabs', - isActive: !hidden && !disableNavigation && headerFocused, - }, - ) - - // When the header is focused, down-arrow returns focus to content. Only - // active when the selected tab has opted in via useTabHeaderFocus() — - // legacy tabs have nowhere to return focus to. - const handleKeyDown = (e: KeyboardEvent) => { - if (!headerFocused || !optedIn || hidden) return - if (e.key === 'down') { - e.preventDefault() - setHeaderFocused(false) - } - } - - // Opt-in: same tabs:next/previous actions, active from content. Focuses - // the header so subsequent presses cycle via the handler above. - useKeybindings( - { - 'tabs:next': () => { - handleTabChange(1) - setHeaderFocused(true) - }, - 'tabs:previous': () => { - handleTabChange(-1) - setHeaderFocused(true) - }, - }, - { - context: 'Tabs', - isActive: - navFromContent && - !headerFocused && - optedIn && - !hidden && - !disableNavigation, - }, - ) - - // Calculate spacing to fill the available width. No keyboard hint in the - // header row — content footers own hints (see useTabHeaderFocus docs). - const titleWidth = title ? stringWidth(title) + 1 : 0 // +1 for gap - const tabsWidth = tabs.reduce( - (sum, [, tabTitle]) => sum + (tabTitle ? stringWidth(tabTitle) : 0) + 2 + 1, // +2 for padding, +1 for gap - 0, - ) - const usedWidth = titleWidth + tabsWidth - const spacerWidth = useFullWidth ? Math.max(0, terminalWidth - usedWidth) : 0 - - const contentWidth = useFullWidth ? terminalWidth : undefined - - return ( - - - {!hidden && ( - - {title !== undefined && ( - - {title} - - )} - {tabs.map(([id, title], i) => { - const isCurrent = selectedTabIndex === i - const hasColorCursor = color && isCurrent && headerFocused - return ( - - {' '} - {title}{' '} - - ) - })} - {spacerWidth > 0 && {' '.repeat(spacerWidth)}} - - )} - {banner} - {modalScrollRef ? ( - // Inside the modal slot: own the ScrollBox here so the tabs - // header row above sits OUTSIDE the scroll area — it can never - // scroll off. The ref reaches REPL's ScrollKeybindingHandler via - // ModalContext. Keyed by selectedTabIndex → remounts on tab - // switch, resetting scrollTop to 0 without scrollTo() timing games. - - - {children} - - - ) : ( - - {children} - - )} - - - ) -} - -type TabProps = { - title: string - id?: string - children: React.ReactNode -} - -export function Tab({ title, id, children }: TabProps): React.ReactNode { - const { selectedTab, width } = useContext(TabsContext) - const insideModal = useIsInsideModal() - if (selectedTab !== (id ?? title)) { - return null - } - - return ( - - {children} - - ) -} - -export function useTabsWidth(): number | undefined { - const { width } = useContext(TabsContext) - return width -} - -/** - * Opt into header-focus gating. Returns the current header focus state and a - * callback to hand focus back to the tab row. For a Select, pass - * `isDisabled={headerFocused}` and `onUpFromFirstItem={focusHeader}`; keep the - * parent Tabs' initialHeaderFocused at its default so tab/←/→ work on mount. - * - * Calling this hook registers a ↓-blurs-header opt-in on mount. Don't call it - * above an early return that renders static text — ↓ will blur the header with - * no onUpFromFirstItem to recover. Split the component so the hook only runs - * when the Select renders. - */ -export function useTabHeaderFocus(): { - headerFocused: boolean - focusHeader: () => void - blurHeader: () => void -} { - const { headerFocused, focusHeader, blurHeader, registerOptIn } = - useContext(TabsContext) - useEffect(registerOptIn, [registerOptIn]) - return { headerFocused, focusHeader, blurHeader } -} diff --git a/packages/@ant/ink/src/theme/ThemeProvider.tsx b/packages/@ant/ink/src/theme/ThemeProvider.tsx deleted file mode 100644 index bfd6a4307..000000000 --- a/packages/@ant/ink/src/theme/ThemeProvider.tsx +++ /dev/null @@ -1,172 +0,0 @@ -import { feature } from 'bun:bundle' -import React, { - createContext, - useContext, - useEffect, - useMemo, - useState, -} from 'react' -import useStdin from '../hooks/use-stdin.js' -import { getSystemThemeName, type SystemTheme } from './systemTheme.js' -import type { ThemeName, ThemeSetting } from './theme-types.js' - -// -- Config persistence injection -- -// Business layer provides these via setThemeConfigCallbacks(). -// Defaults read/write from a simple module-level store. - -let _loadTheme: () => ThemeSetting = () => 'dark' -let _saveTheme: (setting: ThemeSetting) => void = () => {} - -/** Inject config persistence from the business layer. Call once at startup. */ -export function setThemeConfigCallbacks(opts: { - loadTheme: () => ThemeSetting - saveTheme: (setting: ThemeSetting) => void -}): void { - _loadTheme = opts.loadTheme - _saveTheme = opts.saveTheme -} - -type ThemeContextValue = { - /** The saved user preference. May be 'auto'. */ - themeSetting: ThemeSetting - setThemeSetting: (setting: ThemeSetting) => void - setPreviewTheme: (setting: ThemeSetting) => void - savePreview: () => void - cancelPreview: () => void - /** The resolved theme to render with. Never 'auto'. */ - currentTheme: ThemeName -} - -// Non-'auto' default so useTheme() works without a provider (tests, tooling). -const DEFAULT_THEME: ThemeName = 'dark' - -const ThemeContext = createContext({ - themeSetting: DEFAULT_THEME, - setThemeSetting: () => {}, - setPreviewTheme: () => {}, - savePreview: () => {}, - cancelPreview: () => {}, - currentTheme: DEFAULT_THEME, -}) - -type Props = { - children: React.ReactNode - initialState?: ThemeSetting - onThemeSave?: (setting: ThemeSetting) => void -} - -function defaultInitialTheme(): ThemeSetting { - return _loadTheme() -} - -function defaultSaveTheme(setting: ThemeSetting): void { - _saveTheme(setting) -} - -export function ThemeProvider({ - children, - initialState, - onThemeSave = defaultSaveTheme, -}: Props) { - const [themeSetting, setThemeSetting] = useState( - initialState ?? defaultInitialTheme, - ) - const [previewTheme, setPreviewTheme] = useState(null) - - // Track terminal theme for 'auto' resolution. Seeds from $COLORFGBG (or - // 'dark' if unset); the OSC 11 watcher corrects it on first poll. - const [systemTheme, setSystemTheme] = useState(() => - (initialState ?? themeSetting) === 'auto' ? getSystemThemeName() : 'dark', - ) - - // The setting currently in effect (preview wins while picker is open) - const activeSetting = previewTheme ?? themeSetting - - const { internal_querier } = useStdin() - - // Watch for live terminal theme changes while 'auto' is active. - // Positive feature() pattern so the watcher import is dead-code-eliminated - // in external builds. - useEffect(() => { - if (feature('AUTO_THEME')) { - if (activeSetting !== 'auto' || !internal_querier) return - let cleanup: (() => void) | undefined - let cancelled = false - void import('../../utils/systemThemeWatcher.js').then( - ({ watchSystemTheme }) => { - if (cancelled) return - cleanup = watchSystemTheme(internal_querier, setSystemTheme) - }, - ) - return () => { - cancelled = true - cleanup?.() - } - } - }, [activeSetting, internal_querier]) - - const currentTheme: ThemeName = - activeSetting === 'auto' ? systemTheme : activeSetting - - const value = useMemo( - () => ({ - themeSetting, - setThemeSetting: (newSetting: ThemeSetting) => { - setThemeSetting(newSetting) - setPreviewTheme(null) - // Switching to 'auto' restarts the watcher (activeSetting dep), whose - // first poll fires immediately. Seed from the cache so the OSC - // round-trip doesn't flash the wrong palette. - if (newSetting === 'auto') { - setSystemTheme(getSystemThemeName()) - } - onThemeSave?.(newSetting) - }, - setPreviewTheme: (newSetting: ThemeSetting) => { - setPreviewTheme(newSetting) - if (newSetting === 'auto') { - setSystemTheme(getSystemThemeName()) - } - }, - savePreview: () => { - if (previewTheme !== null) { - setThemeSetting(previewTheme) - setPreviewTheme(null) - onThemeSave?.(previewTheme) - } - }, - cancelPreview: () => { - if (previewTheme !== null) { - setPreviewTheme(null) - } - }, - currentTheme, - }), - [themeSetting, previewTheme, currentTheme, onThemeSave], - ) - - return {children} -} - -/** - * Returns the resolved theme for rendering (never 'auto') and a setter that - * accepts any ThemeSetting (including 'auto'). - */ -export function useTheme(): [ThemeName, (setting: ThemeSetting) => void] { - const { currentTheme, setThemeSetting } = useContext(ThemeContext) - return [currentTheme, setThemeSetting] -} - -/** - * Returns the raw theme setting as stored in config. Use this in UI that - * needs to show 'auto' as a distinct choice (e.g., ThemePicker). - */ -export function useThemeSetting(): ThemeSetting { - return useContext(ThemeContext).themeSetting -} - -export function usePreviewTheme() { - const { setPreviewTheme, savePreview, cancelPreview } = - useContext(ThemeContext) - return { setPreviewTheme, savePreview, cancelPreview } -} diff --git a/packages/@ant/ink/src/theme/ThemedBox.tsx b/packages/@ant/ink/src/theme/ThemedBox.tsx deleted file mode 100644 index 46aadeab9..000000000 --- a/packages/@ant/ink/src/theme/ThemedBox.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import React, { type PropsWithChildren, type Ref } from 'react' -import Box from '../components/Box.js' -import type { DOMElement } from '../core/dom.js' -import type { ClickEvent } from '../core/events/click-event.js' -import type { FocusEvent } from '../core/events/focus-event.js' -import type { KeyboardEvent } from '../core/events/keyboard-event.js' -import type { Color, Styles } from '../core/styles.js' -import { getTheme, type Theme } from './theme-types.js' -import { useTheme } from './ThemeProvider.js' - -// Color props that accept theme keys -type ThemedColorProps = { - readonly borderColor?: keyof Theme | Color - readonly borderTopColor?: keyof Theme | Color - readonly borderBottomColor?: keyof Theme | Color - readonly borderLeftColor?: keyof Theme | Color - readonly borderRightColor?: keyof Theme | Color - readonly backgroundColor?: keyof Theme | Color -} - -// Base Styles without color props (they'll be overridden) -type BaseStylesWithoutColors = Omit< - Styles, - | 'textWrap' - | 'borderColor' - | 'borderTopColor' - | 'borderBottomColor' - | 'borderLeftColor' - | 'borderRightColor' - | 'backgroundColor' -> - -export type Props = BaseStylesWithoutColors & - ThemedColorProps & { - ref?: Ref - tabIndex?: number - autoFocus?: boolean - onClick?: (event: ClickEvent) => void - onFocus?: (event: FocusEvent) => void - onFocusCapture?: (event: FocusEvent) => void - onBlur?: (event: FocusEvent) => void - onBlurCapture?: (event: FocusEvent) => void - onKeyDown?: (event: KeyboardEvent) => void - onKeyDownCapture?: (event: KeyboardEvent) => void - onMouseEnter?: () => void - onMouseLeave?: () => void - } - -/** - * Resolves a color value that may be a theme key to a raw Color. - */ -function resolveColor( - color: keyof Theme | Color | undefined, - theme: Theme, -): Color | undefined { - if (!color) return undefined - // Check if it's a raw color (starts with rgb(, #, ansi256(, or ansi:) - if ( - color.startsWith('rgb(') || - color.startsWith('#') || - color.startsWith('ansi256(') || - color.startsWith('ansi:') - ) { - return color as Color - } - // It's a theme key - resolve it - return theme[color as keyof Theme] as Color -} - -/** - * Theme-aware Box component that resolves theme color keys to raw colors. - * This wraps the base Box component with theme resolution for border colors. - */ -function ThemedBox({ - borderColor, - borderTopColor, - borderBottomColor, - borderLeftColor, - borderRightColor, - backgroundColor, - children, - ref, - ...rest -}: PropsWithChildren): React.ReactNode { - const [themeName] = useTheme() - const theme = getTheme(themeName) - - // Resolve theme keys to raw colors - const resolvedBorderColor = resolveColor(borderColor, theme) - const resolvedBorderTopColor = resolveColor(borderTopColor, theme) - const resolvedBorderBottomColor = resolveColor(borderBottomColor, theme) - const resolvedBorderLeftColor = resolveColor(borderLeftColor, theme) - const resolvedBorderRightColor = resolveColor(borderRightColor, theme) - const resolvedBackgroundColor = resolveColor(backgroundColor, theme) - - return ( - - {children} - - ) -} - -export default ThemedBox diff --git a/packages/@ant/ink/src/theme/ThemedText.tsx b/packages/@ant/ink/src/theme/ThemedText.tsx deleted file mode 100644 index 35e884ca9..000000000 --- a/packages/@ant/ink/src/theme/ThemedText.tsx +++ /dev/null @@ -1,132 +0,0 @@ -import type { ReactNode } from 'react' -import React, { useContext } from 'react' -import Text from '../components/Text.js' -import type { Color, Styles } from '../core/styles.js' -import { getTheme, type Theme } from './theme-types.js' -import { useTheme } from './ThemeProvider.js' - -/** Colors uncolored ThemedText in the subtree. Precedence: explicit `color` > - * this > dimColor. Crosses Box boundaries (Ink's style cascade doesn't). */ -export const TextHoverColorContext = React.createContext< - keyof Theme | undefined ->(undefined) - -export type Props = { - /** - * Change text color. Accepts a theme key or raw color value. - */ - readonly color?: keyof Theme | Color - - /** - * Same as `color`, but for background. Must be a theme key. - */ - readonly backgroundColor?: keyof Theme - - /** - * Dim the color using the theme's inactive color. - * This is compatible with bold (unlike ANSI dim). - */ - readonly dimColor?: boolean - - /** - * Make the text bold. - */ - readonly bold?: boolean - - /** - * Make the text italic. - */ - readonly italic?: boolean - - /** - * Make the text underlined. - */ - readonly underline?: boolean - - /** - * Make the text crossed with a line. - */ - readonly strikethrough?: boolean - - /** - * Inverse background and foreground colors. - */ - readonly inverse?: boolean - - /** - * This property tells Ink to wrap or truncate text if its width is larger than container. - * If `wrap` is passed (by default), Ink will wrap text and split it into multiple lines. - * If `truncate-*` is passed, Ink will truncate text instead, which will result in one line of text with the rest cut off. - */ - readonly wrap?: Styles['textWrap'] - - readonly children?: ReactNode -} - -/** - * Resolves a color value that may be a theme key to a raw Color. - */ -function resolveColor( - color: keyof Theme | Color | undefined, - theme: Theme, -): Color | undefined { - if (!color) return undefined - // Check if it's a raw color (starts with rgb(, #, ansi256(, or ansi:) - if ( - color.startsWith('rgb(') || - color.startsWith('#') || - color.startsWith('ansi256(') || - color.startsWith('ansi:') - ) { - return color as Color - } - // It's a theme key - resolve it - return theme[color as keyof Theme] as Color -} - -/** - * Theme-aware Text component that resolves theme color keys to raw colors. - * This wraps the base Text component with theme resolution. - */ -export default function ThemedText({ - color, - backgroundColor, - dimColor = false, - bold = false, - italic = false, - underline = false, - strikethrough = false, - inverse = false, - wrap = 'wrap', - children, -}: Props): React.ReactNode { - const [themeName] = useTheme() - const theme = getTheme(themeName) - const hoverColor = useContext(TextHoverColorContext) - - // Resolve theme keys to raw colors - const resolvedColor = - !color && hoverColor - ? resolveColor(hoverColor, theme) - : dimColor - ? (theme.inactive as Color) - : resolveColor(color, theme) - const resolvedBackgroundColor = backgroundColor - ? (theme[backgroundColor] as Color) - : undefined - - return ( - - {children} - - ) -} diff --git a/packages/@ant/ink/src/theme/color.ts b/packages/@ant/ink/src/theme/color.ts deleted file mode 100644 index f8b567d4d..000000000 --- a/packages/@ant/ink/src/theme/color.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { type ColorType, colorize } from '../core/colorize.js' -import type { Color } from '../core/styles.js' -import { getTheme, type Theme, type ThemeName } from './theme-types.js' - -/** - * Curried theme-aware color function. Resolves theme keys to raw color - * values before delegating to the ink renderer's colorize. - */ -export function color( - c: keyof Theme | Color | undefined, - theme: ThemeName, - type: ColorType = 'foreground', -): (text: string) => string { - return text => { - if (!c) { - return text - } - // Raw color values bypass theme lookup - if ( - c.startsWith('rgb(') || - c.startsWith('#') || - c.startsWith('ansi256(') || - c.startsWith('ansi:') - ) { - return colorize(text, c, type) - } - // Theme key lookup - return colorize(text, getTheme(theme)[c as keyof Theme], type) - } -} diff --git a/packages/@ant/ink/src/theme/keybindings.ts b/packages/@ant/ink/src/theme/keybindings.ts deleted file mode 100644 index a2ce31c50..000000000 --- a/packages/@ant/ink/src/theme/keybindings.ts +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Minimal stub of the keybinding system for the standalone @anthropic/ink package. - * - * The full keybinding system (src/keybindings/) depends on KeybindingContext, - * KeybindingRegistry, and chord handling. This stub provides the same hook - * interfaces (useKeybinding / useKeybindings) but routes directly through - * useInput, matching common key sequences to action names. - * - * Only the keybindings used by theme components are mapped: - * - confirm:no → Escape - * - tabs:next → Tab / Right arrow - * - tabs:previous → Shift+Tab / Left arrow - */ - -import { useCallback } from 'react' -import useInput from '../hooks/use-input.js' -import type { Key } from '../core/events/input-event.js' - -type Options = { - context?: string - isActive?: boolean -} - -/** Maps action names to key matching logic. */ -const ACTION_MATCHERS: Record< - string, - (input: string, key: Key) => boolean -> = { - 'confirm:no': (_input, key) => key.escape === true, - 'tabs:next': (input, key) => - (key.tab && !key.shift) || (key.rightArrow && !key.shift), - 'tabs:previous': (_input, key) => - (key.tab && key.shift) || (key.leftArrow && !key.shift), -} - -/** - * Register a single keybinding action handler. - */ -export function useKeybinding( - action: string, - handler: () => void | false | Promise, - options: Options = {}, -): void { - const { isActive = true } = options - - const handleInput = useCallback( - (input: string, key: Key) => { - if (!isActive) return - const matcher = ACTION_MATCHERS[action] - if (matcher && matcher(input, key)) { - if (handler() !== false) { - // consumed - } - } - }, - [action, handler, isActive], - ) - - useInput(handleInput, { isActive }) -} - -/** - * Register multiple keybinding action handlers in one hook. - */ -export function useKeybindings( - handlers: Record void | false | Promise>, - options: Options = {}, -): void { - const { isActive = true } = options - - const handleInput = useCallback( - (input: string, key: Key) => { - if (!isActive) return - for (const [action, handler] of Object.entries(handlers)) { - const matcher = ACTION_MATCHERS[action] - if (matcher && matcher(input, key)) { - if (handler() !== false) { - break // consumed, stop checking other handlers - } - } - } - }, - [handlers, isActive], - ) - - useInput(handleInput, { isActive }) -} diff --git a/packages/@ant/ink/src/theme/modalContext.ts b/packages/@ant/ink/src/theme/modalContext.ts deleted file mode 100644 index 919abe9d6..000000000 --- a/packages/@ant/ink/src/theme/modalContext.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Minimal modal context for the standalone @anthropic/ink package. - * - * Provides useIsInsideModal() and useModalScrollRef() used by Pane and Tabs - * to adjust rendering when inside a FullscreenLayout modal slot. - */ - -import { createContext, type RefObject, useContext } from 'react' -import type { ScrollBoxHandle } from '../components/ScrollBox.js' - -type ModalCtx = { - rows: number - columns: number - scrollRef: RefObject | null -} - -export const ModalContext = createContext(null) - -export function useIsInsideModal(): boolean { - return useContext(ModalContext) !== null -} - -export function useModalScrollRef(): RefObject | null { - return useContext(ModalContext)?.scrollRef ?? null -} diff --git a/packages/@ant/ink/src/theme/systemTheme.ts b/packages/@ant/ink/src/theme/systemTheme.ts deleted file mode 100644 index 2ac3da5b6..000000000 --- a/packages/@ant/ink/src/theme/systemTheme.ts +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Terminal dark/light mode detection. - * - * Detection is based on the terminal's actual background color (queried via - * OSC 11) rather than the OS appearance setting. - * - * Vendored from src/utils/systemTheme.ts for package independence. - */ - -export type SystemTheme = 'dark' | 'light' - -let cachedSystemTheme: SystemTheme | undefined - -/** - * Detect theme from $COLORFGBG environment variable (set by some terminals). - */ -function detectFromColorFgBg(): SystemTheme | undefined { - const colorFgBg = process.env.COLORFGBG - if (!colorFgBg) return undefined - const parts = colorFgBg.split(';') - if (parts.length < 2) return undefined - const bg = parseInt(parts[parts.length - 1]!, 10) - // Standard ANSI color indices: 0-7 are dark, 8-15 are bright/light - if (isNaN(bg)) return undefined - return bg >= 8 ? 'light' : 'dark' -} - -/** - * Get the current terminal theme. Cached after first detection. - */ -export function getSystemThemeName(): SystemTheme { - if (cachedSystemTheme === undefined) { - cachedSystemTheme = detectFromColorFgBg() ?? 'dark' - } - return cachedSystemTheme -} - -export function setCachedSystemTheme(theme: SystemTheme): void { - cachedSystemTheme = theme -} diff --git a/packages/@ant/ink/src/theme/theme-types.ts b/packages/@ant/ink/src/theme/theme-types.ts deleted file mode 100644 index 7e59f4059..000000000 --- a/packages/@ant/ink/src/theme/theme-types.ts +++ /dev/null @@ -1,639 +0,0 @@ -import chalk, { Chalk } from 'chalk' -// env import replaced with process.env - -export type Theme = { - autoAccept: string - bashBorder: string - claude: string - claudeShimmer: string // Lighter version of claude color for shimmer effect - claudeBlue_FOR_SYSTEM_SPINNER: string - claudeBlueShimmer_FOR_SYSTEM_SPINNER: string - permission: string - permissionShimmer: string // Lighter version of permission color for shimmer effect - planMode: string - ide: string - promptBorder: string - promptBorderShimmer: string // Lighter version of promptBorder color for shimmer effect - text: string - inverseText: string - inactive: string - inactiveShimmer: string // Lighter version of inactive color for shimmer effect - subtle: string - suggestion: string - remember: string - background: string - // Semantic colors - success: string - error: string - warning: string - merged: string - warningShimmer: string // Lighter version of warning color for shimmer effect - // Diff colors - diffAdded: string - diffRemoved: string - diffAddedDimmed: string - diffRemovedDimmed: string - // Word-level diff highlighting - diffAddedWord: string - diffRemovedWord: string - // Agent colors - red_FOR_SUBAGENTS_ONLY: string - blue_FOR_SUBAGENTS_ONLY: string - green_FOR_SUBAGENTS_ONLY: string - yellow_FOR_SUBAGENTS_ONLY: string - purple_FOR_SUBAGENTS_ONLY: string - orange_FOR_SUBAGENTS_ONLY: string - pink_FOR_SUBAGENTS_ONLY: string - cyan_FOR_SUBAGENTS_ONLY: string - // Grove colors - professionalBlue: string - // Chrome colors - chromeYellow: string - // TUI V2 colors - clawd_body: string - clawd_background: string - userMessageBackground: string - userMessageBackgroundHover: string - /** Message-actions selection. Cool shift toward `suggestion` blue; distinct from default AND userMessageBackground. */ - messageActionsBackground: string - /** Text-selection highlight background (alt-screen mouse selection). Solid - * bg that REPLACES the cell's bg while preserving its fg — matches native - * terminal selection. Previously SGR-7 inverse (swapped fg/bg per cell), - * which fragmented badly over syntax highlighting. */ - selectionBg: string - bashMessageBackgroundColor: string - - memoryBackgroundColor: string - rate_limit_fill: string - rate_limit_empty: string - fastMode: string - fastModeShimmer: string - // Brief/assistant mode label colors - briefLabelYou: string - briefLabelClaude: string - // Rainbow colors for ultrathink keyword highlighting - rainbow_red: string - rainbow_orange: string - rainbow_yellow: string - rainbow_green: string - rainbow_blue: string - rainbow_indigo: string - rainbow_violet: string - rainbow_red_shimmer: string - rainbow_orange_shimmer: string - rainbow_yellow_shimmer: string - rainbow_green_shimmer: string - rainbow_blue_shimmer: string - rainbow_indigo_shimmer: string - rainbow_violet_shimmer: string -} - -export const THEME_NAMES = [ - 'dark', - 'light', - 'light-daltonized', - 'dark-daltonized', - 'light-ansi', - 'dark-ansi', -] as const - -/** A renderable theme. Always resolvable to a concrete color palette. */ -export type ThemeName = (typeof THEME_NAMES)[number] - -export const THEME_SETTINGS = ['auto', ...THEME_NAMES] as const - -/** - * A theme preference as stored in user config. `'auto'` follows the system - * dark/light mode and is resolved to a ThemeName at runtime. - */ -export type ThemeSetting = (typeof THEME_SETTINGS)[number] - -/** - * Light theme using explicit RGB values to avoid inconsistencies - * from users' custom terminal ANSI color definitions - */ -const lightTheme: Theme = { - autoAccept: 'rgb(135,0,255)', // Electric violet - bashBorder: 'rgb(255,0,135)', // Vibrant pink - claude: 'rgb(215,119,87)', // Claude orange - claudeShimmer: 'rgb(245,149,117)', // Lighter claude orange for shimmer effect - claudeBlue_FOR_SYSTEM_SPINNER: 'rgb(87,105,247)', // Medium blue for system spinner - claudeBlueShimmer_FOR_SYSTEM_SPINNER: 'rgb(117,135,255)', // Lighter blue for system spinner shimmer - permission: 'rgb(87,105,247)', // Medium blue - permissionShimmer: 'rgb(137,155,255)', // Lighter blue for shimmer effect - planMode: 'rgb(0,102,102)', // Muted teal - ide: 'rgb(71,130,200)', // Muted blue - promptBorder: 'rgb(153,153,153)', // Medium gray - promptBorderShimmer: 'rgb(183,183,183)', // Lighter gray for shimmer effect - text: 'rgb(0,0,0)', // Black - inverseText: 'rgb(255,255,255)', // White - inactive: 'rgb(102,102,102)', // Dark gray - inactiveShimmer: 'rgb(142,142,142)', // Lighter gray for shimmer effect - subtle: 'rgb(175,175,175)', // Light gray - suggestion: 'rgb(87,105,247)', // Medium blue - remember: 'rgb(0,0,255)', // Blue - background: 'rgb(0,153,153)', // Cyan - success: 'rgb(44,122,57)', // Green - error: 'rgb(171,43,63)', // Red - warning: 'rgb(150,108,30)', // Amber - merged: 'rgb(135,0,255)', // Electric violet (matches autoAccept) - warningShimmer: 'rgb(200,158,80)', // Lighter amber for shimmer effect - diffAdded: 'rgb(105,219,124)', // Light green - diffRemoved: 'rgb(255,168,180)', // Light red - diffAddedDimmed: 'rgb(199,225,203)', // Very light green - diffRemovedDimmed: 'rgb(253,210,216)', // Very light red - diffAddedWord: 'rgb(47,157,68)', // Medium green - diffRemovedWord: 'rgb(209,69,75)', // Medium red - // Agent colors - red_FOR_SUBAGENTS_ONLY: 'rgb(220,38,38)', // Red 600 - blue_FOR_SUBAGENTS_ONLY: 'rgb(37,99,235)', // Blue 600 - green_FOR_SUBAGENTS_ONLY: 'rgb(22,163,74)', // Green 600 - yellow_FOR_SUBAGENTS_ONLY: 'rgb(202,138,4)', // Yellow 600 - purple_FOR_SUBAGENTS_ONLY: 'rgb(147,51,234)', // Purple 600 - orange_FOR_SUBAGENTS_ONLY: 'rgb(234,88,12)', // Orange 600 - pink_FOR_SUBAGENTS_ONLY: 'rgb(219,39,119)', // Pink 600 - cyan_FOR_SUBAGENTS_ONLY: 'rgb(8,145,178)', // Cyan 600 - // Grove colors - professionalBlue: 'rgb(106,155,204)', - // Chrome colors - chromeYellow: 'rgb(251,188,4)', // Chrome yellow - // TUI V2 colors - clawd_body: 'rgb(215,119,87)', - clawd_background: 'rgb(0,0,0)', - userMessageBackground: 'rgb(240, 240, 240)', // Slightly darker grey for optimal contrast - userMessageBackgroundHover: 'rgb(252, 252, 252)', // ≥250 to quantize distinct from base at 256-color level - messageActionsBackground: 'rgb(232, 236, 244)', // cool gray — darker than userMsg 240 (visible on white), slight blue toward `suggestion` - selectionBg: 'rgb(180, 213, 255)', // classic light-mode selection blue (macOS/VS Code-ish); dark fgs stay readable - bashMessageBackgroundColor: 'rgb(250, 245, 250)', - - memoryBackgroundColor: 'rgb(230, 245, 250)', - rate_limit_fill: 'rgb(87,105,247)', // Medium blue - rate_limit_empty: 'rgb(39,47,111)', // Dark blue - fastMode: 'rgb(255,106,0)', // Electric orange - fastModeShimmer: 'rgb(255,150,50)', // Lighter orange for shimmer - // Brief/assistant mode - briefLabelYou: 'rgb(37,99,235)', // Blue - briefLabelClaude: 'rgb(215,119,87)', // Brand orange - rainbow_red: 'rgb(235,95,87)', - rainbow_orange: 'rgb(245,139,87)', - rainbow_yellow: 'rgb(250,195,95)', - rainbow_green: 'rgb(145,200,130)', - rainbow_blue: 'rgb(130,170,220)', - rainbow_indigo: 'rgb(155,130,200)', - rainbow_violet: 'rgb(200,130,180)', - rainbow_red_shimmer: 'rgb(250,155,147)', - rainbow_orange_shimmer: 'rgb(255,185,137)', - rainbow_yellow_shimmer: 'rgb(255,225,155)', - rainbow_green_shimmer: 'rgb(185,230,180)', - rainbow_blue_shimmer: 'rgb(180,205,240)', - rainbow_indigo_shimmer: 'rgb(195,180,230)', - rainbow_violet_shimmer: 'rgb(230,180,210)', -} - -/** - * Light ANSI theme using only the 16 standard ANSI colors - * for terminals without true color support - */ -const lightAnsiTheme: Theme = { - autoAccept: 'ansi:magenta', - bashBorder: 'ansi:magenta', - claude: 'ansi:redBright', - claudeShimmer: 'ansi:yellowBright', - claudeBlue_FOR_SYSTEM_SPINNER: 'ansi:blue', - claudeBlueShimmer_FOR_SYSTEM_SPINNER: 'ansi:blueBright', - permission: 'ansi:blue', - permissionShimmer: 'ansi:blueBright', - planMode: 'ansi:cyan', - ide: 'ansi:blueBright', - promptBorder: 'ansi:white', - promptBorderShimmer: 'ansi:whiteBright', - text: 'ansi:black', - inverseText: 'ansi:white', - inactive: 'ansi:blackBright', - inactiveShimmer: 'ansi:white', - subtle: 'ansi:blackBright', - suggestion: 'ansi:blue', - remember: 'ansi:blue', - background: 'ansi:cyan', - success: 'ansi:green', - error: 'ansi:red', - warning: 'ansi:yellow', - merged: 'ansi:magenta', - warningShimmer: 'ansi:yellowBright', - diffAdded: 'ansi:green', - diffRemoved: 'ansi:red', - diffAddedDimmed: 'ansi:green', - diffRemovedDimmed: 'ansi:red', - diffAddedWord: 'ansi:greenBright', - diffRemovedWord: 'ansi:redBright', - // Agent colors - red_FOR_SUBAGENTS_ONLY: 'ansi:red', - blue_FOR_SUBAGENTS_ONLY: 'ansi:blue', - green_FOR_SUBAGENTS_ONLY: 'ansi:green', - yellow_FOR_SUBAGENTS_ONLY: 'ansi:yellow', - purple_FOR_SUBAGENTS_ONLY: 'ansi:magenta', - orange_FOR_SUBAGENTS_ONLY: 'ansi:redBright', - pink_FOR_SUBAGENTS_ONLY: 'ansi:magentaBright', - cyan_FOR_SUBAGENTS_ONLY: 'ansi:cyan', - // Grove colors - professionalBlue: 'ansi:blueBright', - // Chrome colors - chromeYellow: 'ansi:yellow', // Chrome yellow - // TUI V2 colors - clawd_body: 'ansi:redBright', - clawd_background: 'ansi:black', - userMessageBackground: 'ansi:white', - userMessageBackgroundHover: 'ansi:whiteBright', - messageActionsBackground: 'ansi:white', - selectionBg: 'ansi:cyan', // lighter named bg for light-ansi; dark fgs stay readable - bashMessageBackgroundColor: 'ansi:whiteBright', - - memoryBackgroundColor: 'ansi:white', - rate_limit_fill: 'ansi:yellow', - rate_limit_empty: 'ansi:black', - fastMode: 'ansi:red', - fastModeShimmer: 'ansi:redBright', - briefLabelYou: 'ansi:blue', - briefLabelClaude: 'ansi:redBright', - rainbow_red: 'ansi:red', - rainbow_orange: 'ansi:redBright', - rainbow_yellow: 'ansi:yellow', - rainbow_green: 'ansi:green', - rainbow_blue: 'ansi:cyan', - rainbow_indigo: 'ansi:blue', - rainbow_violet: 'ansi:magenta', - rainbow_red_shimmer: 'ansi:redBright', - rainbow_orange_shimmer: 'ansi:yellow', - rainbow_yellow_shimmer: 'ansi:yellowBright', - rainbow_green_shimmer: 'ansi:greenBright', - rainbow_blue_shimmer: 'ansi:cyanBright', - rainbow_indigo_shimmer: 'ansi:blueBright', - rainbow_violet_shimmer: 'ansi:magentaBright', -} - -/** - * Dark ANSI theme using only the 16 standard ANSI colors - * for terminals without true color support - */ -const darkAnsiTheme: Theme = { - autoAccept: 'ansi:magentaBright', - bashBorder: 'ansi:magentaBright', - claude: 'ansi:redBright', - claudeShimmer: 'ansi:yellowBright', - claudeBlue_FOR_SYSTEM_SPINNER: 'ansi:blueBright', - claudeBlueShimmer_FOR_SYSTEM_SPINNER: 'ansi:blueBright', - permission: 'ansi:blueBright', - permissionShimmer: 'ansi:blueBright', - planMode: 'ansi:cyanBright', - ide: 'ansi:blue', - promptBorder: 'ansi:white', - promptBorderShimmer: 'ansi:whiteBright', - text: 'ansi:whiteBright', - inverseText: 'ansi:black', - inactive: 'ansi:white', - inactiveShimmer: 'ansi:whiteBright', - subtle: 'ansi:white', - suggestion: 'ansi:blueBright', - remember: 'ansi:blueBright', - background: 'ansi:cyanBright', - success: 'ansi:greenBright', - error: 'ansi:redBright', - warning: 'ansi:yellowBright', - merged: 'ansi:magentaBright', - warningShimmer: 'ansi:yellowBright', - diffAdded: 'ansi:green', - diffRemoved: 'ansi:red', - diffAddedDimmed: 'ansi:green', - diffRemovedDimmed: 'ansi:red', - diffAddedWord: 'ansi:greenBright', - diffRemovedWord: 'ansi:redBright', - // Agent colors - red_FOR_SUBAGENTS_ONLY: 'ansi:redBright', - blue_FOR_SUBAGENTS_ONLY: 'ansi:blueBright', - green_FOR_SUBAGENTS_ONLY: 'ansi:greenBright', - yellow_FOR_SUBAGENTS_ONLY: 'ansi:yellowBright', - purple_FOR_SUBAGENTS_ONLY: 'ansi:magentaBright', - orange_FOR_SUBAGENTS_ONLY: 'ansi:redBright', - pink_FOR_SUBAGENTS_ONLY: 'ansi:magentaBright', - cyan_FOR_SUBAGENTS_ONLY: 'ansi:cyanBright', - // Grove colors - professionalBlue: 'rgb(106,155,204)', - // Chrome colors - chromeYellow: 'ansi:yellowBright', // Chrome yellow - // TUI V2 colors - clawd_body: 'ansi:redBright', - clawd_background: 'ansi:black', - userMessageBackground: 'ansi:blackBright', - userMessageBackgroundHover: 'ansi:white', - messageActionsBackground: 'ansi:blackBright', - selectionBg: 'ansi:blue', // darker named bg for dark-ansi; bright fgs stay readable - bashMessageBackgroundColor: 'ansi:black', - - memoryBackgroundColor: 'ansi:blackBright', - rate_limit_fill: 'ansi:yellow', - rate_limit_empty: 'ansi:white', - fastMode: 'ansi:redBright', - fastModeShimmer: 'ansi:redBright', - briefLabelYou: 'ansi:blueBright', - briefLabelClaude: 'ansi:redBright', - rainbow_red: 'ansi:red', - rainbow_orange: 'ansi:redBright', - rainbow_yellow: 'ansi:yellow', - rainbow_green: 'ansi:green', - rainbow_blue: 'ansi:cyan', - rainbow_indigo: 'ansi:blue', - rainbow_violet: 'ansi:magenta', - rainbow_red_shimmer: 'ansi:redBright', - rainbow_orange_shimmer: 'ansi:yellow', - rainbow_yellow_shimmer: 'ansi:yellowBright', - rainbow_green_shimmer: 'ansi:greenBright', - rainbow_blue_shimmer: 'ansi:cyanBright', - rainbow_indigo_shimmer: 'ansi:blueBright', - rainbow_violet_shimmer: 'ansi:magentaBright', -} - -/** - * Light daltonized theme (color-blind friendly) using explicit RGB values - * to avoid inconsistencies from users' custom terminal ANSI color definitions - */ -const lightDaltonizedTheme: Theme = { - autoAccept: 'rgb(135,0,255)', // Electric violet - bashBorder: 'rgb(0,102,204)', // Blue instead of pink - claude: 'rgb(255,153,51)', // Orange adjusted for deuteranopia - claudeShimmer: 'rgb(255,183,101)', // Lighter orange for shimmer effect - claudeBlue_FOR_SYSTEM_SPINNER: 'rgb(51,102,255)', // Bright blue for system spinner - claudeBlueShimmer_FOR_SYSTEM_SPINNER: 'rgb(101,152,255)', // Lighter bright blue for system spinner shimmer - permission: 'rgb(51,102,255)', // Bright blue - permissionShimmer: 'rgb(101,152,255)', // Lighter bright blue for shimmer - planMode: 'rgb(51,102,102)', // Muted blue-gray (works for color-blind) - ide: 'rgb(71,130,200)', // Muted blue - promptBorder: 'rgb(153,153,153)', // Medium gray - promptBorderShimmer: 'rgb(183,183,183)', // Lighter gray for shimmer - text: 'rgb(0,0,0)', // Black - inverseText: 'rgb(255,255,255)', // White - inactive: 'rgb(102,102,102)', // Dark gray - inactiveShimmer: 'rgb(142,142,142)', // Lighter gray for shimmer effect - subtle: 'rgb(175,175,175)', // Light gray - suggestion: 'rgb(51,102,255)', // Bright blue - remember: 'rgb(51,102,255)', // Bright blue - background: 'rgb(0,153,153)', // Cyan (color-blind friendly) - success: 'rgb(0,102,153)', // Blue instead of green for deuteranopia - error: 'rgb(204,0,0)', // Pure red for better distinction - warning: 'rgb(255,153,0)', // Orange adjusted for deuteranopia - merged: 'rgb(135,0,255)', // Electric violet (matches autoAccept) - warningShimmer: 'rgb(255,183,50)', // Lighter orange for shimmer - diffAdded: 'rgb(153,204,255)', // Light blue instead of green - diffRemoved: 'rgb(255,204,204)', // Light red - diffAddedDimmed: 'rgb(209,231,253)', // Very light blue - diffRemovedDimmed: 'rgb(255,233,233)', // Very light red - diffAddedWord: 'rgb(51,102,204)', // Medium blue (less intense than deep blue) - diffRemovedWord: 'rgb(153,51,51)', // Softer red (less intense than deep red) - // Agent colors (daltonism-friendly) - red_FOR_SUBAGENTS_ONLY: 'rgb(204,0,0)', // Pure red - blue_FOR_SUBAGENTS_ONLY: 'rgb(0,102,204)', // Pure blue - green_FOR_SUBAGENTS_ONLY: 'rgb(0,204,0)', // Pure green - yellow_FOR_SUBAGENTS_ONLY: 'rgb(255,204,0)', // Golden yellow - purple_FOR_SUBAGENTS_ONLY: 'rgb(128,0,128)', // True purple - orange_FOR_SUBAGENTS_ONLY: 'rgb(255,128,0)', // True orange - pink_FOR_SUBAGENTS_ONLY: 'rgb(255,102,178)', // Adjusted pink - cyan_FOR_SUBAGENTS_ONLY: 'rgb(0,178,178)', // Adjusted cyan - // Grove colors - professionalBlue: 'rgb(106,155,204)', - // Chrome colors - chromeYellow: 'rgb(251,188,4)', // Chrome yellow - // TUI V2 colors - clawd_body: 'rgb(215,119,87)', - clawd_background: 'rgb(0,0,0)', - userMessageBackground: 'rgb(220, 220, 220)', // Slightly darker grey for optimal contrast - userMessageBackgroundHover: 'rgb(232, 232, 232)', // ≥230 to quantize distinct from base at 256-color level - messageActionsBackground: 'rgb(210, 216, 226)', // cool gray — darker than userMsg 220, slight blue - selectionBg: 'rgb(180, 213, 255)', // light selection blue; daltonized fgs are yellows/blues, both readable on light blue - bashMessageBackgroundColor: 'rgb(250, 245, 250)', - - memoryBackgroundColor: 'rgb(230, 245, 250)', - rate_limit_fill: 'rgb(51,102,255)', // Bright blue - rate_limit_empty: 'rgb(23,46,114)', // Dark blue - fastMode: 'rgb(255,106,0)', // Electric orange (color-blind safe) - fastModeShimmer: 'rgb(255,150,50)', // Lighter orange for shimmer - briefLabelYou: 'rgb(37,99,235)', // Blue - briefLabelClaude: 'rgb(255,153,51)', // Orange adjusted for deuteranopia (matches claude) - rainbow_red: 'rgb(235,95,87)', - rainbow_orange: 'rgb(245,139,87)', - rainbow_yellow: 'rgb(250,195,95)', - rainbow_green: 'rgb(145,200,130)', - rainbow_blue: 'rgb(130,170,220)', - rainbow_indigo: 'rgb(155,130,200)', - rainbow_violet: 'rgb(200,130,180)', - rainbow_red_shimmer: 'rgb(250,155,147)', - rainbow_orange_shimmer: 'rgb(255,185,137)', - rainbow_yellow_shimmer: 'rgb(255,225,155)', - rainbow_green_shimmer: 'rgb(185,230,180)', - rainbow_blue_shimmer: 'rgb(180,205,240)', - rainbow_indigo_shimmer: 'rgb(195,180,230)', - rainbow_violet_shimmer: 'rgb(230,180,210)', -} - -/** - * Dark theme using explicit RGB values to avoid inconsistencies - * from users' custom terminal ANSI color definitions - */ -const darkTheme: Theme = { - autoAccept: 'rgb(175,135,255)', // Electric violet - bashBorder: 'rgb(253,93,177)', // Bright pink - claude: 'rgb(215,119,87)', // Claude orange - claudeShimmer: 'rgb(235,159,127)', // Lighter claude orange for shimmer effect - claudeBlue_FOR_SYSTEM_SPINNER: 'rgb(147,165,255)', // Blue for system spinner - claudeBlueShimmer_FOR_SYSTEM_SPINNER: 'rgb(177,195,255)', // Lighter blue for system spinner shimmer - permission: 'rgb(177,185,249)', // Light blue-purple - permissionShimmer: 'rgb(207,215,255)', // Lighter blue-purple for shimmer - planMode: 'rgb(72,150,140)', // Muted sage green - ide: 'rgb(71,130,200)', // Muted blue - promptBorder: 'rgb(136,136,136)', // Medium gray - promptBorderShimmer: 'rgb(166,166,166)', // Lighter gray for shimmer - text: 'rgb(255,255,255)', // White - inverseText: 'rgb(0,0,0)', // Black - inactive: 'rgb(153,153,153)', // Light gray - inactiveShimmer: 'rgb(193,193,193)', // Lighter gray for shimmer effect - subtle: 'rgb(80,80,80)', // Dark gray - suggestion: 'rgb(177,185,249)', // Light blue-purple - remember: 'rgb(177,185,249)', // Light blue-purple - background: 'rgb(0,204,204)', // Bright cyan - success: 'rgb(78,186,101)', // Bright green - error: 'rgb(255,107,128)', // Bright red - warning: 'rgb(255,193,7)', // Bright amber - merged: 'rgb(175,135,255)', // Electric violet (matches autoAccept) - warningShimmer: 'rgb(255,223,57)', // Lighter amber for shimmer - diffAdded: 'rgb(34,92,43)', // Dark green - diffRemoved: 'rgb(122,41,54)', // Dark red - diffAddedDimmed: 'rgb(71,88,74)', // Very dark green - diffRemovedDimmed: 'rgb(105,72,77)', // Very dark red - diffAddedWord: 'rgb(56,166,96)', // Medium green - diffRemovedWord: 'rgb(179,89,107)', // Softer red (less intense than bright red) - // Agent colors - red_FOR_SUBAGENTS_ONLY: 'rgb(220,38,38)', // Red 600 - blue_FOR_SUBAGENTS_ONLY: 'rgb(37,99,235)', // Blue 600 - green_FOR_SUBAGENTS_ONLY: 'rgb(22,163,74)', // Green 600 - yellow_FOR_SUBAGENTS_ONLY: 'rgb(202,138,4)', // Yellow 600 - purple_FOR_SUBAGENTS_ONLY: 'rgb(147,51,234)', // Purple 600 - orange_FOR_SUBAGENTS_ONLY: 'rgb(234,88,12)', // Orange 600 - pink_FOR_SUBAGENTS_ONLY: 'rgb(219,39,119)', // Pink 600 - cyan_FOR_SUBAGENTS_ONLY: 'rgb(8,145,178)', // Cyan 600 - // Grove colors - professionalBlue: 'rgb(106,155,204)', - // Chrome colors - chromeYellow: 'rgb(251,188,4)', // Chrome yellow - // TUI V2 colors - clawd_body: 'rgb(215,119,87)', - clawd_background: 'rgb(0,0,0)', - userMessageBackground: 'rgb(55, 55, 55)', // Lighter grey for better visual contrast - userMessageBackgroundHover: 'rgb(70, 70, 70)', - messageActionsBackground: 'rgb(44, 50, 62)', // cool gray, slight blue - selectionBg: 'rgb(38, 79, 120)', // classic dark-mode selection blue (VS Code dark default); light fgs stay readable - bashMessageBackgroundColor: 'rgb(65, 60, 65)', - - memoryBackgroundColor: 'rgb(55, 65, 70)', - rate_limit_fill: 'rgb(177,185,249)', // Light blue-purple - rate_limit_empty: 'rgb(80,83,112)', // Medium blue-purple - fastMode: 'rgb(255,120,20)', // Electric orange for dark bg - fastModeShimmer: 'rgb(255,165,70)', // Lighter orange for shimmer - briefLabelYou: 'rgb(122,180,232)', // Light blue - briefLabelClaude: 'rgb(215,119,87)', // Brand orange - rainbow_red: 'rgb(235,95,87)', - rainbow_orange: 'rgb(245,139,87)', - rainbow_yellow: 'rgb(250,195,95)', - rainbow_green: 'rgb(145,200,130)', - rainbow_blue: 'rgb(130,170,220)', - rainbow_indigo: 'rgb(155,130,200)', - rainbow_violet: 'rgb(200,130,180)', - rainbow_red_shimmer: 'rgb(250,155,147)', - rainbow_orange_shimmer: 'rgb(255,185,137)', - rainbow_yellow_shimmer: 'rgb(255,225,155)', - rainbow_green_shimmer: 'rgb(185,230,180)', - rainbow_blue_shimmer: 'rgb(180,205,240)', - rainbow_indigo_shimmer: 'rgb(195,180,230)', - rainbow_violet_shimmer: 'rgb(230,180,210)', -} - -/** - * Dark daltonized theme (color-blind friendly) using explicit RGB values - * to avoid inconsistencies from users' custom terminal ANSI color definitions - */ -const darkDaltonizedTheme: Theme = { - autoAccept: 'rgb(175,135,255)', // Electric violet - bashBorder: 'rgb(51,153,255)', // Bright blue - claude: 'rgb(255,153,51)', // Orange adjusted for deuteranopia - claudeShimmer: 'rgb(255,183,101)', // Lighter orange for shimmer effect - claudeBlue_FOR_SYSTEM_SPINNER: 'rgb(153,204,255)', // Light blue for system spinner - claudeBlueShimmer_FOR_SYSTEM_SPINNER: 'rgb(183,224,255)', // Lighter blue for system spinner shimmer - permission: 'rgb(153,204,255)', // Light blue - permissionShimmer: 'rgb(183,224,255)', // Lighter blue for shimmer - planMode: 'rgb(102,153,153)', // Muted gray-teal (works for color-blind) - ide: 'rgb(71,130,200)', // Muted blue - promptBorder: 'rgb(136,136,136)', // Medium gray - promptBorderShimmer: 'rgb(166,166,166)', // Lighter gray for shimmer - text: 'rgb(255,255,255)', // White - inverseText: 'rgb(0,0,0)', // Black - inactive: 'rgb(153,153,153)', // Light gray - inactiveShimmer: 'rgb(193,193,193)', // Lighter gray for shimmer effect - subtle: 'rgb(80,80,80)', // Dark gray - suggestion: 'rgb(153,204,255)', // Light blue - remember: 'rgb(153,204,255)', // Light blue - background: 'rgb(0,204,204)', // Bright cyan (color-blind friendly) - success: 'rgb(51,153,255)', // Blue instead of green - error: 'rgb(255,102,102)', // Bright red - warning: 'rgb(255,204,0)', // Yellow-orange for deuteranopia - merged: 'rgb(175,135,255)', // Electric violet (matches autoAccept) - warningShimmer: 'rgb(255,234,50)', // Lighter yellow-orange for shimmer - diffAdded: 'rgb(0,68,102)', // Dark blue - diffRemoved: 'rgb(102,0,0)', // Dark red - diffAddedDimmed: 'rgb(62,81,91)', // Dimmed blue - diffRemovedDimmed: 'rgb(62,44,44)', // Dimmed red - diffAddedWord: 'rgb(0,119,179)', // Medium blue - diffRemovedWord: 'rgb(179,0,0)', // Medium red - // Agent colors (daltonism-friendly, dark mode) - red_FOR_SUBAGENTS_ONLY: 'rgb(255,102,102)', // Bright red - blue_FOR_SUBAGENTS_ONLY: 'rgb(102,178,255)', // Bright blue - green_FOR_SUBAGENTS_ONLY: 'rgb(102,255,102)', // Bright green - yellow_FOR_SUBAGENTS_ONLY: 'rgb(255,255,102)', // Bright yellow - purple_FOR_SUBAGENTS_ONLY: 'rgb(178,102,255)', // Bright purple - orange_FOR_SUBAGENTS_ONLY: 'rgb(255,178,102)', // Bright orange - pink_FOR_SUBAGENTS_ONLY: 'rgb(255,153,204)', // Bright pink - cyan_FOR_SUBAGENTS_ONLY: 'rgb(102,204,204)', // Bright cyan - // Grove colors - professionalBlue: 'rgb(106,155,204)', - // Chrome colors - chromeYellow: 'rgb(251,188,4)', // Chrome yellow - // TUI V2 colors - clawd_body: 'rgb(215,119,87)', - clawd_background: 'rgb(0,0,0)', - userMessageBackground: 'rgb(55, 55, 55)', // Lighter grey for better visual contrast - userMessageBackgroundHover: 'rgb(70, 70, 70)', - messageActionsBackground: 'rgb(44, 50, 62)', // cool gray, slight blue - selectionBg: 'rgb(38, 79, 120)', // classic dark-mode selection blue (VS Code dark default); light fgs stay readable - bashMessageBackgroundColor: 'rgb(65, 60, 65)', - - memoryBackgroundColor: 'rgb(55, 65, 70)', - rate_limit_fill: 'rgb(153,204,255)', // Light blue - rate_limit_empty: 'rgb(69,92,115)', // Dark blue - fastMode: 'rgb(255,120,20)', // Electric orange for dark bg (color-blind safe) - fastModeShimmer: 'rgb(255,165,70)', // Lighter orange for shimmer - briefLabelYou: 'rgb(122,180,232)', // Light blue - briefLabelClaude: 'rgb(255,153,51)', // Orange adjusted for deuteranopia (matches claude) - rainbow_red: 'rgb(235,95,87)', - rainbow_orange: 'rgb(245,139,87)', - rainbow_yellow: 'rgb(250,195,95)', - rainbow_green: 'rgb(145,200,130)', - rainbow_blue: 'rgb(130,170,220)', - rainbow_indigo: 'rgb(155,130,200)', - rainbow_violet: 'rgb(200,130,180)', - rainbow_red_shimmer: 'rgb(250,155,147)', - rainbow_orange_shimmer: 'rgb(255,185,137)', - rainbow_yellow_shimmer: 'rgb(255,225,155)', - rainbow_green_shimmer: 'rgb(185,230,180)', - rainbow_blue_shimmer: 'rgb(180,205,240)', - rainbow_indigo_shimmer: 'rgb(195,180,230)', - rainbow_violet_shimmer: 'rgb(230,180,210)', -} - -export function getTheme(themeName: ThemeName): Theme { - switch (themeName) { - case 'light': - return lightTheme - case 'light-ansi': - return lightAnsiTheme - case 'dark-ansi': - return darkAnsiTheme - case 'light-daltonized': - return lightDaltonizedTheme - case 'dark-daltonized': - return darkDaltonizedTheme - default: - return darkTheme - } -} - -// Create a chalk instance with 256-color level for Apple Terminal -// Apple Terminal doesn't handle 24-bit color escape sequences well -const chalkForChart = - process.env.TERM_PROGRAM === 'Apple_Terminal' - ? new Chalk({ level: 2 }) // 256 colors - : chalk - -/** - * Converts a theme color to an ANSI escape sequence for use with asciichart. - * Uses chalk to generate the escape codes, with 256-color mode for Apple Terminal. - */ -export function themeColorToAnsi(themeColor: string): string { - const rgbMatch = themeColor.match(/rgb\(\s?(\d+),\s?(\d+),\s?(\d+)\s?\)/) - if (rgbMatch) { - const r = parseInt(rgbMatch[1]!, 10) - const g = parseInt(rgbMatch[2]!, 10) - const b = parseInt(rgbMatch[3]!, 10) - // Use chalk.rgb which auto-converts to 256 colors when level is 2 - // Extract just the opening escape sequence by using a marker - const colored = chalkForChart.rgb(r, g, b)('X') - return colored.slice(0, colored.indexOf('X')) - } - // Fallback to magenta if parsing fails - return '\x1b[35m' -} diff --git a/packages/@ant/ink/src/theme/types.ts b/packages/@ant/ink/src/theme/types.ts deleted file mode 100644 index 561da3b2e..000000000 --- a/packages/@ant/ink/src/theme/types.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Theme type re-exports. - * - * ThemeName and ThemeSetting are business-level concepts stored in config; - * they live in theme-types.ts and are re-exported here for convenient - * consumption by theme-layer components. - */ -export type { Theme, ThemeName, ThemeSetting } from './theme-types.js' -export { getTheme } from './theme-types.js' -export type { ColorType } from '../core/colorize.js' -export { colorize } from '../core/colorize.js' diff --git a/packages/@ant/ink/src/types/ink-elements.d.ts b/packages/@ant/ink/src/types/ink-elements.d.ts deleted file mode 100644 index e439ae56b..000000000 --- a/packages/@ant/ink/src/types/ink-elements.d.ts +++ /dev/null @@ -1,49 +0,0 @@ -// Type declarations for custom Ink JSX elements -// Note: The detailed prop types are defined in ink-jsx.d.ts via React module augmentation. -// This file provides the global JSX namespace fallback declarations. -import type { ReactNode, Ref } from 'react'; -import type { ClickEvent } from '../core/events/click-event.js'; -import type { FocusEvent } from '../core/events/focus-event.js'; -import type { KeyboardEvent } from '../core/events/keyboard-event.js'; -import type { Styles, TextStyles } from '../core/styles.js'; -import type { DOMElement } from '../core/dom.js'; - -declare global { - namespace JSX { - interface IntrinsicElements { - 'ink-box': { - ref?: Ref; - tabIndex?: number; - autoFocus?: boolean; - onClick?: (event: ClickEvent) => void; - onFocus?: (event: FocusEvent) => void; - onFocusCapture?: (event: FocusEvent) => void; - onBlur?: (event: FocusEvent) => void; - onBlurCapture?: (event: FocusEvent) => void; - onMouseEnter?: () => void; - onMouseLeave?: () => void; - onKeyDown?: (event: KeyboardEvent) => void; - onKeyDownCapture?: (event: KeyboardEvent) => void; - style?: Styles; - stickyScroll?: boolean; - children?: ReactNode; - }; - 'ink-text': { - style?: Styles; - textStyles?: TextStyles; - children?: ReactNode; - }; - 'ink-link': { - href?: string; - children?: ReactNode; - }; - 'ink-raw-ansi': { - rawText?: string; - rawWidth?: number; - rawHeight?: number; - }; - } - } -} - -export {}; diff --git a/packages/@ant/ink/src/types/ink-jsx.d.ts b/packages/@ant/ink/src/types/ink-jsx.d.ts deleted file mode 100644 index 456be9399..000000000 --- a/packages/@ant/ink/src/types/ink-jsx.d.ts +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Ink custom JSX intrinsic elements. - * - * With "jsx": "react-jsx", TypeScript resolves JSX types from react/jsx-runtime - * whose IntrinsicElements extends React.JSX.IntrinsicElements. We augment the - * 'react' module to inject our custom elements into React.JSX.IntrinsicElements. - * - * This file must be a module (have an import/export) for `declare module` - * augmentation to work correctly. - */ -import type { ReactNode, Ref } from 'react'; -import type { ClickEvent } from '../core/events/click-event.js'; -import type { FocusEvent } from '../core/events/focus-event.js'; -import type { KeyboardEvent } from '../core/events/keyboard-event.js'; -import type { Styles, TextStyles } from '../core/styles.js'; -import type { DOMElement } from '../core/dom.js'; - -declare module 'react' { - namespace JSX { - interface IntrinsicElements { - 'ink-box': { - ref?: Ref; - tabIndex?: number; - autoFocus?: boolean; - onClick?: (event: ClickEvent) => void; - onFocus?: (event: FocusEvent) => void; - onFocusCapture?: (event: FocusEvent) => void; - onBlur?: (event: FocusEvent) => void; - onBlurCapture?: (event: FocusEvent) => void; - onMouseEnter?: () => void; - onMouseLeave?: () => void; - onKeyDown?: (event: KeyboardEvent) => void; - onKeyDownCapture?: (event: KeyboardEvent) => void; - style?: Styles; - stickyScroll?: boolean; - children?: ReactNode; - }; - 'ink-text': { - style?: Styles; - textStyles?: TextStyles; - children?: ReactNode; - }; - 'ink-link': { - href?: string; - children?: ReactNode; - }; - 'ink-raw-ansi': { - rawText?: string; - rawWidth?: number; - rawHeight?: number; - }; - } - } -} diff --git a/packages/@ant/ink/src/utils/debug.ts b/packages/@ant/ink/src/utils/debug.ts deleted file mode 100644 index 2d3ae198e..000000000 --- a/packages/@ant/ink/src/utils/debug.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Stub debug logger for package independence -export function logForDebugging(..._args: unknown[]): void {} diff --git a/src/bridge/bridgeStatusUtil.ts b/src/bridge/bridgeStatusUtil.ts index d9c285d70..90de462e8 100644 --- a/src/bridge/bridgeStatusUtil.ts +++ b/src/bridge/bridgeStatusUtil.ts @@ -2,7 +2,7 @@ import { getClaudeAiBaseUrl, getRemoteSessionUrl, } from '../constants/product.js' -import { stringWidth } from '@anthropic/ink' +import { stringWidth } from '../ink/stringWidth.js' import { formatDuration, truncateToWidth } from '../utils/format.js' import { getGraphemeSegmenter } from '../utils/intl.js' diff --git a/src/bridge/bridgeUI.ts b/src/bridge/bridgeUI.ts index 67a5132d2..5149839ea 100644 --- a/src/bridge/bridgeUI.ts +++ b/src/bridge/bridgeUI.ts @@ -5,7 +5,7 @@ import { BRIDGE_READY_INDICATOR, BRIDGE_SPINNER_FRAMES, } from '../constants/figures.js' -import { stringWidth } from '@anthropic/ink' +import { stringWidth } from '../ink/stringWidth.js' import { logForDebugging } from '../utils/debug.js' import { buildActiveFooterText, diff --git a/src/buddy/CompanionCard.tsx b/src/buddy/CompanionCard.tsx index 1f571605c..f9264acf3 100644 --- a/src/buddy/CompanionCard.tsx +++ b/src/buddy/CompanionCard.tsx @@ -3,8 +3,8 @@ * Mirrors official vc8 component: bordered box with sprite, stats, last reaction. */ import React from 'react'; -import { Box, Text } from '@anthropic/ink'; -import { useInput } from '@anthropic/ink'; +import { Box, Text } from '../ink.js'; +import { useInput } from '../ink.js'; import { renderSprite } from './sprites.js'; import { RARITY_COLORS, RARITY_STARS, STAT_NAMES, type Companion } from './types.js'; diff --git a/src/buddy/CompanionSprite.tsx b/src/buddy/CompanionSprite.tsx index 22dbeb643..d8c7ae473 100644 --- a/src/buddy/CompanionSprite.tsx +++ b/src/buddy/CompanionSprite.tsx @@ -2,7 +2,8 @@ import { feature } from 'bun:bundle' import figures from 'figures' import React, { useEffect, useRef, useState } from 'react' import { useTerminalSize } from '../hooks/useTerminalSize.js' -import { Box, Text, stringWidth } from '@anthropic/ink' +import { stringWidth } from '../ink/stringWidth.js' +import { Box, Text } from '../ink.js' import { useAppState, useSetAppState } from '../state/AppState.js' import type { AppState } from '../state/AppStateStore.js' import { getGlobalConfig } from '../utils/config.js' diff --git a/src/buddy/useBuddyNotification.tsx b/src/buddy/useBuddyNotification.tsx index 2df078e79..62d61f4cf 100644 --- a/src/buddy/useBuddyNotification.tsx +++ b/src/buddy/useBuddyNotification.tsx @@ -1,7 +1,7 @@ import { feature } from 'bun:bundle' import React, { useEffect } from 'react' import { useNotifications } from '../context/notifications.js' -import { Text } from '@anthropic/ink' +import { Text } from '../ink.js' import { getGlobalConfig } from '../utils/config.js' import { getRainbowColor } from '../utils/thinking.js' diff --git a/src/cli/handlers/mcp.tsx b/src/cli/handlers/mcp.tsx index b9c030da9..134918c75 100644 --- a/src/cli/handlers/mcp.tsx +++ b/src/cli/handlers/mcp.tsx @@ -8,7 +8,7 @@ import pMap from 'p-map' import { cwd } from 'process' import React from 'react' import { MCPServerDesktopImportDialog } from '../../components/MCPServerDesktopImportDialog.js' -import { wrappedRender as render } from '@anthropic/ink' +import { render } from '../../ink.js' import { KeybindingSetup } from '../../keybindings/KeybindingProviderSetup.js' import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, diff --git a/src/cli/handlers/util.tsx b/src/cli/handlers/util.tsx index b1a8cc0b9..c86b31737 100644 --- a/src/cli/handlers/util.tsx +++ b/src/cli/handlers/util.tsx @@ -8,8 +8,8 @@ import { cwd } from 'process' import React from 'react' import { WelcomeV2 } from '../../components/LogoV2/WelcomeV2.js' import { useManagePlugins } from '../../hooks/useManagePlugins.js' -import type { Root } from '@anthropic/ink' -import { Box, Text } from '@anthropic/ink' +import type { Root } from '../../ink.js' +import { Box, Text } from '../../ink.js' import { KeybindingSetup } from '../../keybindings/KeybindingProviderSetup.js' import { logEvent } from '../../services/analytics/index.js' import { MCPConnectionManager } from '../../services/mcp/MCPConnectionManager.js' diff --git a/src/commands/add-dir/add-dir.tsx b/src/commands/add-dir/add-dir.tsx index 91180cd73..cfb6c6687 100644 --- a/src/commands/add-dir/add-dir.tsx +++ b/src/commands/add-dir/add-dir.tsx @@ -8,7 +8,7 @@ import { import type { LocalJSXCommandContext } from '../../commands.js' import { MessageResponse } from '../../components/MessageResponse.js' import { AddWorkspaceDirectory } from '../../components/permissions/rules/AddWorkspaceDirectory.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import type { LocalJSXCommandOnDone } from '../../types/command.js' import { applyPermissionUpdate, diff --git a/src/commands/bridge/bridge.tsx b/src/commands/bridge/bridge.tsx index 78b0341f1..33a681202 100644 --- a/src/commands/bridge/bridge.tsx +++ b/src/commands/bridge/bridge.tsx @@ -13,10 +13,11 @@ import { BRIDGE_LOGIN_INSTRUCTION, REMOTE_CONTROL_DISCONNECTED_MSG, } from '../../bridge/types.js' -import { Dialog, ListItem } from '@anthropic/ink' +import { Dialog } from '../../components/design-system/Dialog.js' +import { ListItem } from '../../components/design-system/ListItem.js' import { shouldShowRemoteCallout } from '../../components/RemoteCallout.js' import { useRegisterOverlay } from '../../context/overlayContext.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { useKeybindings } from '../../keybindings/useKeybinding.js' import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, diff --git a/src/commands/btw/btw.tsx b/src/commands/btw/btw.tsx index 753b5821e..28a83946b 100644 --- a/src/commands/btw/btw.tsx +++ b/src/commands/btw/btw.tsx @@ -9,8 +9,11 @@ import { getSystemPrompt } from '../../constants/prompts.js' import { useModalOrTerminalSize } from '../../context/modalContext.js' import { getSystemContext, getUserContext } from '../../context.js' import { useTerminalSize } from '../../hooks/useTerminalSize.js' -import { type KeyboardEvent, type ScrollBoxHandle, ScrollBox } from '@anthropic/ink' -import { Box, Text } from '@anthropic/ink' +import ScrollBox, { + type ScrollBoxHandle, +} from '../../ink/components/ScrollBox.js' +import type { KeyboardEvent } from '../../ink/events/keyboard-event.js' +import { Box, Text } from '../../ink.js' import type { LocalJSXCommandOnDone } from '../../types/command.js' import type { Message } from '../../types/message.js' import { createAbortController } from '../../utils/abortController.js' diff --git a/src/commands/chrome/chrome.tsx b/src/commands/chrome/chrome.tsx index 1fe1b1470..3fd0dbca3 100644 --- a/src/commands/chrome/chrome.tsx +++ b/src/commands/chrome/chrome.tsx @@ -3,8 +3,8 @@ import { type OptionWithDescription, Select, } from '../../components/CustomSelect/select.js' -import { Dialog } from '@anthropic/ink' -import { Box, Text } from '@anthropic/ink' +import { Dialog } from '../../components/design-system/Dialog.js' +import { Box, Text } from '../../ink.js' import { useAppState } from '../../state/AppState.js' import { isClaudeAISubscriber } from '../../utils/auth.js' import { openBrowser } from '../../utils/browser.js' diff --git a/src/commands/copy/copy.tsx b/src/commands/copy/copy.tsx index e54171a63..d5196de20 100644 --- a/src/commands/copy/copy.tsx +++ b/src/commands/copy/copy.tsx @@ -6,8 +6,13 @@ import React, { useRef } from 'react' import type { CommandResultDisplay } from '../../commands.js' import type { OptionWithDescription } from '../../components/CustomSelect/select.js' import { Select } from '../../components/CustomSelect/select.js' -import { Byline, KeyboardShortcutHint, Pane } from '@anthropic/ink' -import { Box, setClipboard, Text, stringWidth, type KeyboardEvent } from '@anthropic/ink' +import { Byline } from '../../components/design-system/Byline.js' +import { KeyboardShortcutHint } from '../../components/design-system/KeyboardShortcutHint.js' +import { Pane } from '../../components/design-system/Pane.js' +import type { KeyboardEvent } from '../../ink/events/keyboard-event.js' +import { stringWidth } from '../../ink/stringWidth.js' +import { setClipboard } from '../../ink/termio/osc.js' +import { Box, Text } from '../../ink.js' import { logEvent } from '../../services/analytics/index.js' import type { LocalJSXCommandCall } from '../../types/command.js' import type { AssistantMessage, Message } from '../../types/message.js' diff --git a/src/commands/fast/fast.tsx b/src/commands/fast/fast.tsx index 2ab17db78..a959a909a 100644 --- a/src/commands/fast/fast.tsx +++ b/src/commands/fast/fast.tsx @@ -4,9 +4,9 @@ import type { CommandResultDisplay, LocalJSXCommandContext, } from '../../commands.js' -import { Dialog } from '@anthropic/ink' +import { Dialog } from '../../components/design-system/Dialog.js' import { FastIcon, getFastIconString } from '../../components/FastIcon.js' -import { Box, Link, Text } from '@anthropic/ink' +import { Box, Link, Text } from '../../ink.js' import { useKeybindings } from '../../keybindings/useKeybinding.js' import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, diff --git a/src/commands/ide/ide.tsx b/src/commands/ide/ide.tsx index d5944636d..f22c16e6a 100644 --- a/src/commands/ide/ide.tsx +++ b/src/commands/ide/ide.tsx @@ -7,14 +7,14 @@ import type { LocalJSXCommandContext, } from '../../commands.js' import { Select } from '../../components/CustomSelect/index.js' -import { Dialog } from '@anthropic/ink' +import { Dialog } from '../../components/design-system/Dialog.js' import { IdeAutoConnectDialog, IdeDisableAutoConnectDialog, shouldShowAutoConnectDialog, shouldShowDisableAutoConnectDialog, } from '../../components/IdeAutoConnectDialog.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { clearServerCache } from '../../services/mcp/client.js' import type { ScopedMcpServerConfig } from '../../services/mcp/types.js' import { useAppState, useSetAppState } from '../../state/AppState.js' diff --git a/src/commands/install-github-app/ApiKeyStep.tsx b/src/commands/install-github-app/ApiKeyStep.tsx index 942bc662a..3b88a94f3 100644 --- a/src/commands/install-github-app/ApiKeyStep.tsx +++ b/src/commands/install-github-app/ApiKeyStep.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useState } from 'react' import TextInput from '../../components/TextInput.js' import { useTerminalSize } from '../../hooks/useTerminalSize.js' -import { Box, color, Text, useTheme } from '@anthropic/ink' +import { Box, color, Text, useTheme } from '../../ink.js' import { useKeybindings } from '../../keybindings/useKeybinding.js' interface ApiKeyStepProps { diff --git a/src/commands/install-github-app/CheckExistingSecretStep.tsx b/src/commands/install-github-app/CheckExistingSecretStep.tsx index de7f4b9a7..b00b682c2 100644 --- a/src/commands/install-github-app/CheckExistingSecretStep.tsx +++ b/src/commands/install-github-app/CheckExistingSecretStep.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useState } from 'react' import TextInput from '../../components/TextInput.js' import { useTerminalSize } from '../../hooks/useTerminalSize.js' -import { Box, color, Text, useTheme } from '@anthropic/ink' +import { Box, color, Text, useTheme } from '../../ink.js' import { useKeybindings } from '../../keybindings/useKeybinding.js' interface CheckExistingSecretStepProps { diff --git a/src/commands/install-github-app/CheckGitHubStep.tsx b/src/commands/install-github-app/CheckGitHubStep.tsx index a43be6c6c..16f4d7e8a 100644 --- a/src/commands/install-github-app/CheckGitHubStep.tsx +++ b/src/commands/install-github-app/CheckGitHubStep.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { Text } from '@anthropic/ink' +import { Text } from '../../ink.js' export function CheckGitHubStep() { return Checking GitHub CLI installation… diff --git a/src/commands/install-github-app/ChooseRepoStep.tsx b/src/commands/install-github-app/ChooseRepoStep.tsx index 67e921834..b0d4c63b0 100644 --- a/src/commands/install-github-app/ChooseRepoStep.tsx +++ b/src/commands/install-github-app/ChooseRepoStep.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useState } from 'react' import TextInput from '../../components/TextInput.js' import { useTerminalSize } from '../../hooks/useTerminalSize.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { useKeybindings } from '../../keybindings/useKeybinding.js' interface ChooseRepoStepProps { diff --git a/src/commands/install-github-app/CreatingStep.tsx b/src/commands/install-github-app/CreatingStep.tsx index c021d0bcb..1861571ed 100644 --- a/src/commands/install-github-app/CreatingStep.tsx +++ b/src/commands/install-github-app/CreatingStep.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import type { Workflow } from './types.js' interface CreatingStepProps { diff --git a/src/commands/install-github-app/ErrorStep.tsx b/src/commands/install-github-app/ErrorStep.tsx index 5864a0659..a8333f395 100644 --- a/src/commands/install-github-app/ErrorStep.tsx +++ b/src/commands/install-github-app/ErrorStep.tsx @@ -1,6 +1,6 @@ import React from 'react' import { GITHUB_ACTION_SETUP_DOCS_URL } from '../../constants/github-app.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' interface ErrorStepProps { error: string | undefined diff --git a/src/commands/install-github-app/ExistingWorkflowStep.tsx b/src/commands/install-github-app/ExistingWorkflowStep.tsx index 11b0a1bb2..645edb742 100644 --- a/src/commands/install-github-app/ExistingWorkflowStep.tsx +++ b/src/commands/install-github-app/ExistingWorkflowStep.tsx @@ -1,6 +1,6 @@ import React from 'react' import { Select } from 'src/components/CustomSelect/index.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' interface ExistingWorkflowStepProps { repoName: string diff --git a/src/commands/install-github-app/InstallAppStep.tsx b/src/commands/install-github-app/InstallAppStep.tsx index e966578c1..98a699945 100644 --- a/src/commands/install-github-app/InstallAppStep.tsx +++ b/src/commands/install-github-app/InstallAppStep.tsx @@ -1,7 +1,7 @@ import figures from 'figures' import React from 'react' import { GITHUB_ACTION_SETUP_DOCS_URL } from '../../constants/github-app.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { useKeybinding } from '../../keybindings/useKeybinding.js' interface InstallAppStepProps { diff --git a/src/commands/install-github-app/OAuthFlowStep.tsx b/src/commands/install-github-app/OAuthFlowStep.tsx index b13493f18..b8fd96a49 100644 --- a/src/commands/install-github-app/OAuthFlowStep.tsx +++ b/src/commands/install-github-app/OAuthFlowStep.tsx @@ -3,11 +3,13 @@ import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent, } from 'src/services/analytics/index.js' -import { KeyboardShortcutHint } from '@anthropic/ink' +import { KeyboardShortcutHint } from '../../components/design-system/KeyboardShortcutHint.js' import { Spinner } from '../../components/Spinner.js' import TextInput from '../../components/TextInput.js' import { useTerminalSize } from '../../hooks/useTerminalSize.js' -import { type KeyboardEvent, setClipboard, Box, Link, Text } from '@anthropic/ink' +import type { KeyboardEvent } from '../../ink/events/keyboard-event.js' +import { setClipboard } from '../../ink/termio/osc.js' +import { Box, Link, Text } from '../../ink.js' import { OAuthService } from '../../services/oauth/index.js' import { saveOAuthTokensIfNeeded } from '../../utils/auth.js' import { logError } from '../../utils/log.js' diff --git a/src/commands/install-github-app/SuccessStep.tsx b/src/commands/install-github-app/SuccessStep.tsx index 2080f3dc7..a04b98ac7 100644 --- a/src/commands/install-github-app/SuccessStep.tsx +++ b/src/commands/install-github-app/SuccessStep.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' type SuccessStepProps = { secretExists: boolean diff --git a/src/commands/install-github-app/WarningsStep.tsx b/src/commands/install-github-app/WarningsStep.tsx index c3d347798..122cdeac3 100644 --- a/src/commands/install-github-app/WarningsStep.tsx +++ b/src/commands/install-github-app/WarningsStep.tsx @@ -1,7 +1,7 @@ import figures from 'figures' import React from 'react' import { GITHUB_ACTION_SETUP_DOCS_URL } from '../../constants/github-app.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { useKeybinding } from '../../keybindings/useKeybinding.js' import type { Warning } from './types.js' diff --git a/src/commands/install-github-app/install-github-app.tsx b/src/commands/install-github-app/install-github-app.tsx index c57be920c..3a78ae106 100644 --- a/src/commands/install-github-app/install-github-app.tsx +++ b/src/commands/install-github-app/install-github-app.tsx @@ -7,7 +7,8 @@ import { import { WorkflowMultiselectDialog } from '../../components/WorkflowMultiselectDialog.js' import { GITHUB_ACTION_SETUP_DOCS_URL } from '../../constants/github-app.js' import { useExitOnCtrlCDWithKeybindings } from '../../hooks/useExitOnCtrlCDWithKeybindings.js' -import { type KeyboardEvent, Box } from '@anthropic/ink' +import type { KeyboardEvent } from '../../ink/events/keyboard-event.js' +import { Box } from '../../ink.js' import type { LocalJSXCommandOnDone } from '../../types/command.js' import { getAnthropicApiKey, isAnthropicAuthEnabled } from '../../utils/auth.js' import { openBrowser } from '../../utils/browser.js' diff --git a/src/commands/install.tsx b/src/commands/install.tsx index bb72ad4a8..15eddd575 100644 --- a/src/commands/install.tsx +++ b/src/commands/install.tsx @@ -3,8 +3,8 @@ import { join } from 'node:path' import React, { useEffect, useState } from 'react' import type { CommandResultDisplay } from 'src/commands.js' import { logEvent } from 'src/services/analytics/index.js' -import { StatusIcon } from '@anthropic/ink' -import { Box, wrappedRender as render, Text } from '@anthropic/ink' +import { StatusIcon } from '../components/design-system/StatusIcon.js' +import { Box, render, Text } from '../ink.js' import { logForDebugging } from '../utils/debug.js' import { env } from '../utils/env.js' import { errorMessage } from '../utils/errors.js' diff --git a/src/commands/login/login.tsx b/src/commands/login/login.tsx index b4329fe62..912ad61f9 100644 --- a/src/commands/login/login.tsx +++ b/src/commands/login/login.tsx @@ -8,9 +8,9 @@ import { import type { LocalJSXCommandContext } from '../../commands.js' import { ConfigurableShortcutHint } from '../../components/ConfigurableShortcutHint.js' import { ConsoleOAuthFlow } from '../../components/ConsoleOAuthFlow.js' -import { Dialog } from '@anthropic/ink' +import { Dialog } from '../../components/design-system/Dialog.js' import { useMainLoopModel } from '../../hooks/useMainLoopModel.js' -import { Text } from '@anthropic/ink' +import { Text } from '../../ink.js' import { refreshGrowthBookAfterAuthChange } from '../../services/analytics/growthbook.js' import { refreshPolicyLimits } from '../../services/policyLimits/index.js' import { refreshRemoteManagedSettings } from '../../services/remoteManagedSettings/index.js' diff --git a/src/commands/logout/logout.tsx b/src/commands/logout/logout.tsx index 4223feff4..b8eb13b87 100644 --- a/src/commands/logout/logout.tsx +++ b/src/commands/logout/logout.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import { clearTrustedDeviceTokenCache } from '../../bridge/trustedDevice.js' -import { Text } from '@anthropic/ink' +import { Text } from '../../ink.js' import { refreshGrowthBookAfterAuthChange } from '../../services/analytics/growthbook.js' import { getGroveNoticeConfig, diff --git a/src/commands/memory/memory.tsx b/src/commands/memory/memory.tsx index 885ab57dd..945b34a60 100644 --- a/src/commands/memory/memory.tsx +++ b/src/commands/memory/memory.tsx @@ -1,10 +1,10 @@ import { mkdir, writeFile } from 'fs/promises' import * as React from 'react' import type { CommandResultDisplay } from '../../commands.js' -import { Dialog } from '@anthropic/ink' +import { Dialog } from '../../components/design-system/Dialog.js' import { MemoryFileSelector } from '../../components/memory/MemoryFileSelector.js' import { getRelativeMemoryPath } from '../../components/memory/MemoryUpdateNotification.js' -import { Box, Link, Text } from '@anthropic/ink' +import { Box, Link, Text } from '../../ink.js' import type { LocalJSXCommandCall } from '../../types/command.js' import { clearMemoryFileCaches, getMemoryFiles } from '../../utils/claudemd.js' import { getClaudeConfigHomeDir } from '../../utils/envUtils.js' diff --git a/src/commands/mobile/mobile.tsx b/src/commands/mobile/mobile.tsx index 6c1d8f828..0467919bb 100644 --- a/src/commands/mobile/mobile.tsx +++ b/src/commands/mobile/mobile.tsx @@ -1,8 +1,9 @@ import { toString as qrToString } from 'qrcode' import * as React from 'react' import { useCallback, useEffect, useState } from 'react' -import { Pane } from '@anthropic/ink' -import { type KeyboardEvent, Box, Text } from '@anthropic/ink' +import { Pane } from '../../components/design-system/Pane.js' +import type { KeyboardEvent } from '../../ink/events/keyboard-event.js' +import { Box, Text } from '../../ink.js' import { useKeybinding } from '../../keybindings/useKeybinding.js' import type { LocalJSXCommandOnDone } from '../../types/command.js' diff --git a/src/commands/plan/plan.tsx b/src/commands/plan/plan.tsx index d7694182c..8c3f328a7 100644 --- a/src/commands/plan/plan.tsx +++ b/src/commands/plan/plan.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import { handlePlanModeTransition } from '../../bootstrap/state.js' import type { LocalJSXCommandContext } from '../../commands.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import type { LocalJSXCommandOnDone } from '../../types/command.js' import { getExternalEditor } from '../../utils/editor.js' import { toIDEDisplayName } from '../../utils/ide.js' diff --git a/src/commands/plugin/AddMarketplace.tsx b/src/commands/plugin/AddMarketplace.tsx index 7a9138333..e0a1d4d16 100644 --- a/src/commands/plugin/AddMarketplace.tsx +++ b/src/commands/plugin/AddMarketplace.tsx @@ -5,10 +5,11 @@ import { logEvent, } from 'src/services/analytics/index.js' import { ConfigurableShortcutHint } from '../../components/ConfigurableShortcutHint.js' -import { Byline, KeyboardShortcutHint } from '@anthropic/ink' +import { Byline } from '../../components/design-system/Byline.js' +import { KeyboardShortcutHint } from '../../components/design-system/KeyboardShortcutHint.js' import { Spinner } from '../../components/Spinner.js' import TextInput from '../../components/TextInput.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { toError } from '../../utils/errors.js' import { logError } from '../../utils/log.js' import { clearAllCaches } from '../../utils/plugins/cacheUtils.js' diff --git a/src/commands/plugin/BrowseMarketplace.tsx b/src/commands/plugin/BrowseMarketplace.tsx index 8e3205f5e..e8733052e 100644 --- a/src/commands/plugin/BrowseMarketplace.tsx +++ b/src/commands/plugin/BrowseMarketplace.tsx @@ -2,7 +2,8 @@ import figures from 'figures' import * as React from 'react' import { useEffect, useState } from 'react' import { ConfigurableShortcutHint } from '../../components/ConfigurableShortcutHint.js' -import { Box, Byline, Text } from '@anthropic/ink' +import { Byline } from '../../components/design-system/Byline.js' +import { Box, Text } from '../../ink.js' import { useKeybinding, useKeybindings, diff --git a/src/commands/plugin/DiscoverPlugins.tsx b/src/commands/plugin/DiscoverPlugins.tsx index 4c3435de1..442e2686f 100644 --- a/src/commands/plugin/DiscoverPlugins.tsx +++ b/src/commands/plugin/DiscoverPlugins.tsx @@ -2,12 +2,12 @@ import figures from 'figures' import * as React from 'react' import { useCallback, useEffect, useMemo, useState } from 'react' import { ConfigurableShortcutHint } from '../../components/ConfigurableShortcutHint.js' +import { Byline } from '../../components/design-system/Byline.js' import { SearchBox } from '../../components/SearchBox.js' -import { Byline } from '@anthropic/ink' import { useSearchInput } from '../../hooks/useSearchInput.js' import { useTerminalSize } from '../../hooks/useTerminalSize.js' // eslint-disable-next-line custom-rules/prefer-use-keybindings -- useInput needed for raw search mode text input -import { Box, Text, useInput, useTerminalFocus } from '@anthropic/ink' +import { Box, Text, useInput, useTerminalFocus } from '../../ink.js' import { useKeybinding, useKeybindings, diff --git a/src/commands/plugin/ManageMarketplaces.tsx b/src/commands/plugin/ManageMarketplaces.tsx index 5ec3dbe80..868f26e32 100644 --- a/src/commands/plugin/ManageMarketplaces.tsx +++ b/src/commands/plugin/ManageMarketplaces.tsx @@ -6,9 +6,10 @@ import { logEvent, } from 'src/services/analytics/index.js' import { ConfigurableShortcutHint } from '../../components/ConfigurableShortcutHint.js' -import { Byline, KeyboardShortcutHint } from '@anthropic/ink' +import { Byline } from '../../components/design-system/Byline.js' +import { KeyboardShortcutHint } from '../../components/design-system/KeyboardShortcutHint.js' // eslint-disable-next-line custom-rules/prefer-use-keybindings -- useInput needed for marketplace-specific u/r shortcuts and y/n confirmation not in keybinding schema -import { Box, Text, useInput } from '@anthropic/ink' +import { Box, Text, useInput } from '../../ink.js' import { useKeybinding, useKeybindings, diff --git a/src/commands/plugin/ManagePlugins.tsx b/src/commands/plugin/ManagePlugins.tsx index a3524724c..e1ec554ac 100644 --- a/src/commands/plugin/ManagePlugins.tsx +++ b/src/commands/plugin/ManagePlugins.tsx @@ -5,7 +5,7 @@ import * as path from 'path' import * as React from 'react' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { ConfigurableShortcutHint } from '../../components/ConfigurableShortcutHint.js' -import { Byline } from '@anthropic/ink' +import { Byline } from '../../components/design-system/Byline.js' import { MCPRemoteServerMenu } from '../../components/mcp/MCPRemoteServerMenu.js' import { MCPStdioServerMenu } from '../../components/mcp/MCPStdioServerMenu.js' import { MCPToolDetailView } from '../../components/mcp/MCPToolDetailView.js' @@ -20,7 +20,7 @@ import { SearchBox } from '../../components/SearchBox.js' import { useSearchInput } from '../../hooks/useSearchInput.js' import { useTerminalSize } from '../../hooks/useTerminalSize.js' // eslint-disable-next-line custom-rules/prefer-use-keybindings -- useInput needed for raw search mode text input -import { Box, Text, useInput, useTerminalFocus } from '@anthropic/ink' +import { Box, Text, useInput, useTerminalFocus } from '../../ink.js' import { useKeybinding, useKeybindings, diff --git a/src/commands/plugin/PluginOptionsDialog.tsx b/src/commands/plugin/PluginOptionsDialog.tsx index cb09352d3..8ef1f6809 100644 --- a/src/commands/plugin/PluginOptionsDialog.tsx +++ b/src/commands/plugin/PluginOptionsDialog.tsx @@ -1,8 +1,9 @@ import figures from 'figures' import React, { useCallback, useState } from 'react' -import { Dialog } from '@anthropic/ink' +import { Dialog } from '../../components/design-system/Dialog.js' +import { stringWidth } from '../../ink/stringWidth.js' // eslint-disable-next-line custom-rules/prefer-use-keybindings -- raw text input for config dialog -import { Box, Text, useInput, stringWidth } from '@anthropic/ink' +import { Box, Text, useInput } from '../../ink.js' import { useKeybinding, useKeybindings, diff --git a/src/commands/plugin/PluginSettings.tsx b/src/commands/plugin/PluginSettings.tsx index 4d544ef47..e0c3d54da 100644 --- a/src/commands/plugin/PluginSettings.tsx +++ b/src/commands/plugin/PluginSettings.tsx @@ -2,10 +2,11 @@ import figures from 'figures' import * as React from 'react' import { useCallback, useEffect, useState } from 'react' import { ConfigurableShortcutHint } from '../../components/ConfigurableShortcutHint.js' -import { Byline, Pane, Tabs } from '@anthropic/ink' -import { Tab } from '../../components/design-system/Tabs.js' +import { Byline } from '../../components/design-system/Byline.js' +import { Pane } from '../../components/design-system/Pane.js' +import { Tab, Tabs } from '../../components/design-system/Tabs.js' import { useExitOnCtrlCDWithKeybindings } from '../../hooks/useExitOnCtrlCDWithKeybindings.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { useKeybinding, useKeybindings, diff --git a/src/commands/plugin/PluginTrustWarning.tsx b/src/commands/plugin/PluginTrustWarning.tsx index 3be3dd79f..2295db691 100644 --- a/src/commands/plugin/PluginTrustWarning.tsx +++ b/src/commands/plugin/PluginTrustWarning.tsx @@ -1,6 +1,6 @@ import figures from 'figures' import * as React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { getPluginTrustMessage } from '../../utils/plugins/marketplaceHelpers.js' export function PluginTrustWarning(): React.ReactNode { diff --git a/src/commands/plugin/UnifiedInstalledCell.tsx b/src/commands/plugin/UnifiedInstalledCell.tsx index 05e44821f..5ce8783de 100644 --- a/src/commands/plugin/UnifiedInstalledCell.tsx +++ b/src/commands/plugin/UnifiedInstalledCell.tsx @@ -1,6 +1,6 @@ import figures from 'figures' import * as React from 'react' -import { Box, color, Text, useTheme } from '@anthropic/ink' +import { Box, color, Text, useTheme } from '../../ink.js' import { plural } from '../../utils/stringUtils.js' import type { UnifiedInstalledItem } from './unifiedTypes.js' diff --git a/src/commands/plugin/ValidatePlugin.tsx b/src/commands/plugin/ValidatePlugin.tsx index 9eb52d4b6..4d8afd8b6 100644 --- a/src/commands/plugin/ValidatePlugin.tsx +++ b/src/commands/plugin/ValidatePlugin.tsx @@ -1,7 +1,7 @@ import figures from 'figures' import * as React from 'react' import { useEffect } from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { errorMessage } from '../../utils/errors.js' import { logError } from '../../utils/log.js' import { validateManifest } from '../../utils/plugins/validatePlugin.js' diff --git a/src/commands/plugin/pluginDetailsHelpers.tsx b/src/commands/plugin/pluginDetailsHelpers.tsx index 2a9909e1a..caec86b46 100644 --- a/src/commands/plugin/pluginDetailsHelpers.tsx +++ b/src/commands/plugin/pluginDetailsHelpers.tsx @@ -6,7 +6,8 @@ import * as React from 'react' import { ConfigurableShortcutHint } from '../../components/ConfigurableShortcutHint.js' -import { Box, Byline, Text } from '@anthropic/ink' +import { Byline } from '../../components/design-system/Byline.js' +import { Box, Text } from '../../ink.js' import type { PluginMarketplaceEntry } from '../../utils/plugins/schemas.js' /** diff --git a/src/commands/rate-limit-options/rate-limit-options.tsx b/src/commands/rate-limit-options/rate-limit-options.tsx index 50198f561..e86eb040d 100644 --- a/src/commands/rate-limit-options/rate-limit-options.tsx +++ b/src/commands/rate-limit-options/rate-limit-options.tsx @@ -7,7 +7,7 @@ import { type OptionWithDescription, Select, } from '../../components/CustomSelect/select.js' -import { Dialog } from '@anthropic/ink' +import { Dialog } from '../../components/design-system/Dialog.js' import { getFeatureValue_CACHED_MAY_BE_STALE } from '../../services/analytics/growthbook.js' import { logEvent } from '../../services/analytics/index.js' import { useClaudeAiLimits } from '../../services/claudeAiLimitsHook.js' diff --git a/src/commands/remote-setup/remote-setup.tsx b/src/commands/remote-setup/remote-setup.tsx index e511f064b..05813453d 100644 --- a/src/commands/remote-setup/remote-setup.tsx +++ b/src/commands/remote-setup/remote-setup.tsx @@ -2,7 +2,9 @@ import { execa } from 'execa' import * as React from 'react' import { useEffect, useState } from 'react' import { Select } from '../../components/CustomSelect/index.js' -import { Box, Dialog, LoadingState, Text } from '@anthropic/ink' +import { Dialog } from '../../components/design-system/Dialog.js' +import { LoadingState } from '../../components/design-system/LoadingState.js' +import { Box, Text } from '../../ink.js' import { logEvent, type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS as SafeString, diff --git a/src/commands/resume/resume.tsx b/src/commands/resume/resume.tsx index 795c05077..f66d654c6 100644 --- a/src/commands/resume/resume.tsx +++ b/src/commands/resume/resume.tsx @@ -9,8 +9,8 @@ import { MessageResponse } from '../../components/MessageResponse.js' import { Spinner } from '../../components/Spinner.js' import { useIsInsideModal } from '../../context/modalContext.js' import { useTerminalSize } from '../../hooks/useTerminalSize.js' -import { setClipboard } from '@anthropic/ink' -import { Box, Text } from '@anthropic/ink' +import { setClipboard } from '../../ink/termio/osc.js' +import { Box, Text } from '../../ink.js' import type { LocalJSXCommandCall } from '../../types/command.js' import type { LogOption } from '../../types/logs.js' import { agenticSessionSearch } from '../../utils/agenticSessionSearch.js' diff --git a/src/commands/review/UltrareviewOverageDialog.tsx b/src/commands/review/UltrareviewOverageDialog.tsx index 12ea10429..020db57f8 100644 --- a/src/commands/review/UltrareviewOverageDialog.tsx +++ b/src/commands/review/UltrareviewOverageDialog.tsx @@ -1,6 +1,7 @@ import React, { useCallback, useRef, useState } from 'react' import { Select } from '../../components/CustomSelect/select.js' -import { Box, Dialog, Text } from '@anthropic/ink' +import { Dialog } from '../../components/design-system/Dialog.js' +import { Box, Text } from '../../ink.js' type Props = { onProceed: (signal: AbortSignal) => Promise diff --git a/src/commands/sandbox-toggle/sandbox-toggle.tsx b/src/commands/sandbox-toggle/sandbox-toggle.tsx index 5f8d46097..157961ad5 100644 --- a/src/commands/sandbox-toggle/sandbox-toggle.tsx +++ b/src/commands/sandbox-toggle/sandbox-toggle.tsx @@ -2,7 +2,7 @@ import { relative } from 'path' import React from 'react' import { getCwdState } from '../../bootstrap/state.js' import { SandboxSettings } from '../../components/sandbox/SandboxSettings.js' -import { color } from '@anthropic/ink' +import { color } from '../../ink.js' import { getPlatform } from '../../utils/platform.js' import { addToExcludedCommands, diff --git a/src/commands/session/session.tsx b/src/commands/session/session.tsx index 18312e183..82135a3fa 100644 --- a/src/commands/session/session.tsx +++ b/src/commands/session/session.tsx @@ -1,7 +1,8 @@ import { toString as qrToString } from 'qrcode' import * as React from 'react' import { useEffect, useState } from 'react' -import { Box, Pane, Text } from '@anthropic/ink' +import { Pane } from '../../components/design-system/Pane.js' +import { Box, Text } from '../../ink.js' import { useKeybinding } from '../../keybindings/useKeybinding.js' import { useAppState } from '../../state/AppState.js' import type { LocalJSXCommandCall } from '../../types/command.js' diff --git a/src/commands/tag/tag.tsx b/src/commands/tag/tag.tsx index ea5813d59..c9d0c6524 100644 --- a/src/commands/tag/tag.tsx +++ b/src/commands/tag/tag.tsx @@ -4,9 +4,9 @@ import * as React from 'react' import { getSessionId } from '../../bootstrap/state.js' import type { CommandResultDisplay } from '../../commands.js' import { Select } from '../../components/CustomSelect/select.js' -import { Dialog } from '@anthropic/ink' +import { Dialog } from '../../components/design-system/Dialog.js' import { COMMON_HELP_ARGS, COMMON_INFO_ARGS } from '../../constants/xml.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { logEvent } from '../../services/analytics/index.js' import type { LocalJSXCommandOnDone } from '../../types/command.js' import { recursivelySanitizeUnicode } from '../../utils/sanitization.js' diff --git a/src/commands/terminalSetup/terminalSetup.tsx b/src/commands/terminalSetup/terminalSetup.tsx index c7776da46..52b8cdea2 100644 --- a/src/commands/terminalSetup/terminalSetup.tsx +++ b/src/commands/terminalSetup/terminalSetup.tsx @@ -5,8 +5,8 @@ import { homedir, platform } from 'os' import { dirname, join } from 'path' import type { ThemeName } from 'src/utils/theme.js' import { pathToFileURL } from 'url' -import { supportsHyperlinks } from '@anthropic/ink' -import { color } from '@anthropic/ink' +import { supportsHyperlinks } from '../../ink/supports-hyperlinks.js' +import { color } from '../../ink.js' import { maybeMarkProjectOnboardingComplete } from '../../projectOnboardingState.js' import type { ToolUseContext } from '../../Tool.js' import type { diff --git a/src/commands/theme/theme.tsx b/src/commands/theme/theme.tsx index 26d4bd1aa..03ad2fd13 100644 --- a/src/commands/theme/theme.tsx +++ b/src/commands/theme/theme.tsx @@ -1,8 +1,8 @@ import * as React from 'react' import type { CommandResultDisplay } from '../../commands.js' -import { Pane } from '@anthropic/ink' +import { Pane } from '../../components/design-system/Pane.js' import { ThemePicker } from '../../components/ThemePicker.js' -import { useTheme } from '@anthropic/ink' +import { useTheme } from '../../ink.js' import type { LocalJSXCommandCall } from '../../types/command.js' type Props = { diff --git a/src/commands/thinkback/thinkback.tsx b/src/commands/thinkback/thinkback.tsx index 1986cd4ed..feea58a66 100644 --- a/src/commands/thinkback/thinkback.tsx +++ b/src/commands/thinkback/thinkback.tsx @@ -5,9 +5,10 @@ import * as React from 'react' import { useCallback, useEffect, useState } from 'react' import type { CommandResultDisplay } from '../../commands.js' import { Select } from '../../components/CustomSelect/select.js' -import { Dialog } from '@anthropic/ink' +import { Dialog } from '../../components/design-system/Dialog.js' import { Spinner } from '../../components/Spinner.js' -import { Box, Text, instances } from '@anthropic/ink' +import instances from '../../ink/instances.js' +import { Box, Text } from '../../ink.js' import { enablePluginOp } from '../../services/plugins/pluginOperations.js' import { logForDebugging } from '../../utils/debug.js' import { isENOENT, toError } from '../../utils/errors.js' diff --git a/src/components/AgentProgressLine.tsx b/src/components/AgentProgressLine.tsx index c15a765a4..7580160e7 100644 --- a/src/components/AgentProgressLine.tsx +++ b/src/components/AgentProgressLine.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import { formatNumber } from '../utils/format.js' import type { Theme } from '../utils/theme.js' diff --git a/src/components/ApproveApiKey.tsx b/src/components/ApproveApiKey.tsx index 545e78f6f..990f0c14e 100644 --- a/src/components/ApproveApiKey.tsx +++ b/src/components/ApproveApiKey.tsx @@ -1,7 +1,8 @@ import React from 'react' -import { Text, Dialog } from '@anthropic/ink' +import { Text } from '../ink.js' import { saveGlobalConfig } from '../utils/config.js' import { Select } from './CustomSelect/index.js' +import { Dialog } from './design-system/Dialog.js' type Props = { customApiKeyTruncated: string diff --git a/src/components/AutoModeOptInDialog.tsx b/src/components/AutoModeOptInDialog.tsx index a6d7f6a12..4aeee28e6 100644 --- a/src/components/AutoModeOptInDialog.tsx +++ b/src/components/AutoModeOptInDialog.tsx @@ -1,8 +1,9 @@ import React from 'react' import { logEvent } from 'src/services/analytics/index.js' -import { Box, Dialog, Link, Text } from '@anthropic/ink' +import { Box, Link, Text } from '../ink.js' import { updateSettingsForSource } from '../utils/settings/settings.js' import { Select } from './CustomSelect/index.js' +import { Dialog } from './design-system/Dialog.js' // NOTE: This copy is legally reviewed — do not modify without Legal team approval. export const AUTO_MODE_DESCRIPTION = diff --git a/src/components/AutoUpdater.tsx b/src/components/AutoUpdater.tsx index cf41e7d84..09b523fc4 100644 --- a/src/components/AutoUpdater.tsx +++ b/src/components/AutoUpdater.tsx @@ -6,7 +6,7 @@ import { } from 'src/services/analytics/index.js' import { useInterval } from 'usehooks-ts' import { useUpdateNotification } from '../hooks/useUpdateNotification.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import { type AutoUpdaterResult, getLatestVersion, diff --git a/src/components/AwsAuthStatusBox.tsx b/src/components/AwsAuthStatusBox.tsx index fbe0216d7..ea2d1a5d3 100644 --- a/src/components/AwsAuthStatusBox.tsx +++ b/src/components/AwsAuthStatusBox.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react' -import { Box, Link, Text } from '@anthropic/ink' +import { Box, Link, Text } from '../ink.js' import { type AwsAuthStatus, AwsAuthStatusManager, diff --git a/src/components/BaseTextInput.tsx b/src/components/BaseTextInput.tsx index 054e6e2e0..07d12974b 100644 --- a/src/components/BaseTextInput.tsx +++ b/src/components/BaseTextInput.tsx @@ -1,8 +1,8 @@ import React from 'react' import { renderPlaceholder } from '../hooks/renderPlaceholder.js' import { usePasteHandler } from '../hooks/usePasteHandler.js' -import { useDeclaredCursor } from '@anthropic/ink' -import { Ansi, Box, Text, useInput } from '@anthropic/ink' +import { useDeclaredCursor } from '../ink/hooks/use-declared-cursor.js' +import { Ansi, Box, Text, useInput } from '../ink.js' import type { BaseInputState, BaseTextInputProps, diff --git a/src/components/BashModeProgress.tsx b/src/components/BashModeProgress.tsx index c36dd1b32..0b6d4b408 100644 --- a/src/components/BashModeProgress.tsx +++ b/src/components/BashModeProgress.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { Box } from '@anthropic/ink' +import { Box } from '../ink.js' import { BashTool } from '../tools/BashTool/BashTool.js' import type { ShellProgress } from '../types/tools.js' import { UserBashInputMessage } from './messages/UserBashInputMessage.js' diff --git a/src/components/BridgeDialog.tsx b/src/components/BridgeDialog.tsx index 27707875d..9a23311fb 100644 --- a/src/components/BridgeDialog.tsx +++ b/src/components/BridgeDialog.tsx @@ -15,12 +15,12 @@ import { } from '../constants/figures.js' import { useRegisterOverlay } from '../context/overlayContext.js' // eslint-disable-next-line custom-rules/prefer-use-keybindings -- raw 'd' key for disconnect, not a configurable keybinding action -import { Box, Text, useInput } from '@anthropic/ink' +import { Box, Text, useInput } from '../ink.js' import { useKeybindings } from '../keybindings/useKeybinding.js' import { useAppState, useSetAppState } from '../state/AppState.js' import { saveGlobalConfig } from '../utils/config.js' import { getBranch } from '../utils/git.js' -import { Dialog } from '@anthropic/ink' +import { Dialog } from './design-system/Dialog.js' type Props = { onDone: () => void diff --git a/src/components/BuiltinStatusLine.tsx b/src/components/BuiltinStatusLine.tsx index fdbf2969d..14b7ec594 100644 --- a/src/components/BuiltinStatusLine.tsx +++ b/src/components/BuiltinStatusLine.tsx @@ -1,7 +1,8 @@ import React, { useEffect, useState } from 'react'; import { formatCost } from '../cost-tracker.js'; -import { Box, Text, ProgressBar } from '@anthropic/ink'; +import { Box, Text } from '../ink.js'; import { formatTokens } from '../utils/format.js'; +import { ProgressBar } from './design-system/ProgressBar.js'; import { useTerminalSize } from '../hooks/useTerminalSize.js'; type RateLimitBucket = { diff --git a/src/components/BypassPermissionsModeDialog.tsx b/src/components/BypassPermissionsModeDialog.tsx index 8e8e40d88..adc708c77 100644 --- a/src/components/BypassPermissionsModeDialog.tsx +++ b/src/components/BypassPermissionsModeDialog.tsx @@ -1,10 +1,10 @@ import React, { useCallback } from 'react' import { logEvent } from 'src/services/analytics/index.js' -import { Box, Link, Newline, Text } from '@anthropic/ink' +import { Box, Link, Newline, Text } from '../ink.js' import { gracefulShutdownSync } from '../utils/gracefulShutdown.js' import { updateSettingsForSource } from '../utils/settings/settings.js' import { Select } from './CustomSelect/index.js' -import { Dialog } from '@anthropic/ink' +import { Dialog } from './design-system/Dialog.js' type Props = { onAccept(): void diff --git a/src/components/ChannelDowngradeDialog.tsx b/src/components/ChannelDowngradeDialog.tsx index 5122dda7b..54db87690 100644 --- a/src/components/ChannelDowngradeDialog.tsx +++ b/src/components/ChannelDowngradeDialog.tsx @@ -1,7 +1,7 @@ import React from 'react' -import { Text } from '@anthropic/ink' +import { Text } from '../ink.js' import { Select } from './CustomSelect/index.js' -import { Dialog } from '@anthropic/ink' +import { Dialog } from './design-system/Dialog.js' export type ChannelDowngradeChoice = 'downgrade' | 'stay' | 'cancel' diff --git a/src/components/ClaudeCodeHint/PluginHintMenu.tsx b/src/components/ClaudeCodeHint/PluginHintMenu.tsx index e461afb77..8afd7cda8 100644 --- a/src/components/ClaudeCodeHint/PluginHintMenu.tsx +++ b/src/components/ClaudeCodeHint/PluginHintMenu.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { Select } from '../CustomSelect/select.js' import { PermissionDialog } from '../permissions/PermissionDialog.js' diff --git a/src/components/ClaudeInChromeOnboarding.tsx b/src/components/ClaudeInChromeOnboarding.tsx index ad2ca096c..7c420ad43 100644 --- a/src/components/ClaudeInChromeOnboarding.tsx +++ b/src/components/ClaudeInChromeOnboarding.tsx @@ -1,9 +1,10 @@ import React from 'react' import { logEvent } from 'src/services/analytics/index.js' // eslint-disable-next-line custom-rules/prefer-use-keybindings -- enter to continue -import { Box, Dialog, Link, Newline, Text, useInput } from '@anthropic/ink' +import { Box, Link, Newline, Text, useInput } from '../ink.js' import { isChromeExtensionInstalled } from '../utils/claudeInChrome/setup.js' import { saveGlobalConfig } from '../utils/config.js' +import { Dialog } from './design-system/Dialog.js' const CHROME_EXTENSION_URL = 'https://claude.ai/chrome' const CHROME_PERMISSIONS_URL = 'https://clau.de/chrome/permissions' diff --git a/src/components/ClaudeMdExternalIncludesDialog.tsx b/src/components/ClaudeMdExternalIncludesDialog.tsx index 2614010ca..1ca6fcd12 100644 --- a/src/components/ClaudeMdExternalIncludesDialog.tsx +++ b/src/components/ClaudeMdExternalIncludesDialog.tsx @@ -1,9 +1,10 @@ import React, { useCallback } from 'react' import { logEvent } from 'src/services/analytics/index.js' -import { Box, Dialog, Link, Text } from '@anthropic/ink' +import { Box, Link, Text } from '../ink.js' import type { ExternalClaudeMdInclude } from '../utils/claudemd.js' import { saveCurrentProjectConfig } from '../utils/config.js' import { Select } from './CustomSelect/index.js' +import { Dialog } from './design-system/Dialog.js' type Props = { onDone(): void diff --git a/src/components/ClickableImageRef.tsx b/src/components/ClickableImageRef.tsx index 2aa6485b6..51144a720 100644 --- a/src/components/ClickableImageRef.tsx +++ b/src/components/ClickableImageRef.tsx @@ -1,6 +1,8 @@ import * as React from 'react' import { pathToFileURL } from 'url' -import { Link, supportsHyperlinks, Text } from '@anthropic/ink' +import Link from '../ink/components/Link.js' +import { supportsHyperlinks } from '../ink/supports-hyperlinks.js' +import { Text } from '../ink.js' import { getStoredImagePath } from '../utils/imageStore.js' import type { Theme } from '../utils/theme.js' diff --git a/src/components/CompactSummary.tsx b/src/components/CompactSummary.tsx index e343a6591..1cd1687fa 100644 --- a/src/components/CompactSummary.tsx +++ b/src/components/CompactSummary.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import { BLACK_CIRCLE } from '../constants/figures.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import type { Screen } from '../screens/REPL.js' import type { NormalizedUserMessage } from '../types/message.js' import { getUserMessageText } from '../utils/messages.js' diff --git a/src/components/ConfigurableShortcutHint.tsx b/src/components/ConfigurableShortcutHint.tsx index 36ec0acd9..82aea15fa 100644 --- a/src/components/ConfigurableShortcutHint.tsx +++ b/src/components/ConfigurableShortcutHint.tsx @@ -4,7 +4,7 @@ import type { KeybindingContextName, } from '../keybindings/types.js' import { useShortcutDisplay } from '../keybindings/useShortcutDisplay.js' -import { KeyboardShortcutHint } from '@anthropic/ink' +import { KeyboardShortcutHint } from './design-system/KeyboardShortcutHint.js' type Props = { /** The keybinding action (e.g., 'app:toggleTranscript') */ diff --git a/src/components/ConsoleOAuthFlow.tsx b/src/components/ConsoleOAuthFlow.tsx index 5264b32b3..7330be828 100644 --- a/src/components/ConsoleOAuthFlow.tsx +++ b/src/components/ConsoleOAuthFlow.tsx @@ -5,7 +5,9 @@ import { } from 'src/services/analytics/index.js' import { installOAuthTokens } from '../cli/handlers/auth.js' import { useTerminalSize } from '../hooks/useTerminalSize.js' -import { setClipboard, useTerminalNotification, Box, Link, Text, KeyboardShortcutHint } from '@anthropic/ink' +import { setClipboard } from '../ink/termio/osc.js' +import { useTerminalNotification } from '../ink/useTerminalNotification.js' +import { Box, Link, Text } from '../ink.js' import { useKeybinding } from '../keybindings/useKeybinding.js' import { getSSLErrorHint } from '../services/api/errorUtils.js' import { sendNotification } from '../services/notifier.js' @@ -14,6 +16,7 @@ import { getOauthAccountInfo, validateForceLoginOrg } from '../utils/auth.js' import { logError } from '../utils/log.js' import { getSettings_DEPRECATED, updateSettingsForSource } from '../utils/settings/settings.js' import { Select } from './CustomSelect/select.js' +import { KeyboardShortcutHint } from './design-system/KeyboardShortcutHint.js' import { Spinner } from './Spinner.js' import TextInput from './TextInput.js' import { fi } from 'zod/v4/locales' diff --git a/src/components/ContextSuggestions.tsx b/src/components/ContextSuggestions.tsx index 2017d7ab9..2eeafafe3 100644 --- a/src/components/ContextSuggestions.tsx +++ b/src/components/ContextSuggestions.tsx @@ -1,8 +1,9 @@ import figures from 'figures' import * as React from 'react' -import { Box, Text, StatusIcon } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import type { ContextSuggestion } from '../utils/contextSuggestions.js' import { formatTokens } from '../utils/format.js' +import { StatusIcon } from './design-system/StatusIcon.js' type Props = { suggestions: ContextSuggestion[] diff --git a/src/components/ContextVisualization.tsx b/src/components/ContextVisualization.tsx index 3502db5f8..e6fb57493 100644 --- a/src/components/ContextVisualization.tsx +++ b/src/components/ContextVisualization.tsx @@ -1,6 +1,6 @@ import { feature } from 'bun:bundle' import * as React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import type { ContextData } from '../utils/analyzeContext.js' import { generateContextSuggestions } from '../utils/contextSuggestions.js' import { getDisplayPath } from '../utils/file.js' diff --git a/src/components/CoordinatorAgentStatus.tsx b/src/components/CoordinatorAgentStatus.tsx index 9609a0662..2aacb7d6a 100644 --- a/src/components/CoordinatorAgentStatus.tsx +++ b/src/components/CoordinatorAgentStatus.tsx @@ -10,7 +10,8 @@ import figures from 'figures' import * as React from 'react' import { BLACK_CIRCLE, PAUSE_ICON, PLAY_ICON } from '../constants/figures.js' import { useTerminalSize } from '../hooks/useTerminalSize.js' -import { Box, Text, stringWidth, wrapText } from '@anthropic/ink' +import { stringWidth } from '../ink/stringWidth.js' +import { Box, Text, wrapText } from '../ink.js' import { type AppState, useAppState, diff --git a/src/components/CostThresholdDialog.tsx b/src/components/CostThresholdDialog.tsx index 283e76c44..584d864a3 100644 --- a/src/components/CostThresholdDialog.tsx +++ b/src/components/CostThresholdDialog.tsx @@ -1,6 +1,7 @@ import React from 'react' -import { Box, Dialog, Link, Text } from '@anthropic/ink' +import { Box, Link, Text } from '../ink.js' import { Select } from './CustomSelect/index.js' +import { Dialog } from './design-system/Dialog.js' type Props = { onDone: () => void diff --git a/src/components/CtrlOToExpand.tsx b/src/components/CtrlOToExpand.tsx index 01ac3b2fb..24b4add81 100644 --- a/src/components/CtrlOToExpand.tsx +++ b/src/components/CtrlOToExpand.tsx @@ -1,9 +1,9 @@ import chalk from 'chalk' import React, { useContext } from 'react' -import { Text } from '@anthropic/ink' +import { Text } from '../ink.js' import { getShortcutDisplay } from '../keybindings/shortcutFormat.js' import { useShortcutDisplay } from '../keybindings/useShortcutDisplay.js' -import { KeyboardShortcutHint } from '@anthropic/ink' +import { KeyboardShortcutHint } from './design-system/KeyboardShortcutHint.js' import { InVirtualListContext } from './messageActions.js' // Context to track if we're inside a sub agent diff --git a/src/components/CustomSelect/SelectMulti.tsx b/src/components/CustomSelect/SelectMulti.tsx index 198917487..bb43e9e1e 100644 --- a/src/components/CustomSelect/SelectMulti.tsx +++ b/src/components/CustomSelect/SelectMulti.tsx @@ -1,6 +1,6 @@ import figures from 'figures' import React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import type { PastedContent } from '../../utils/config.js' import type { ImageDimensions } from '../../utils/imageResizer.js' import type { OptionWithDescription } from './select.js' diff --git a/src/components/CustomSelect/select-input-option.tsx b/src/components/CustomSelect/select-input-option.tsx index ccb8a3989..0f3f9483f 100644 --- a/src/components/CustomSelect/select-input-option.tsx +++ b/src/components/CustomSelect/select-input-option.tsx @@ -1,6 +1,6 @@ import React, { type ReactNode, useEffect, useRef, useState } from 'react' // eslint-disable-next-line custom-rules/prefer-use-keybindings -- UP arrow exit not in Attachments bindings -import { Box, Text, useInput } from '@anthropic/ink' +import { Box, Text, useInput } from '../../ink.js' import { useKeybinding, useKeybindings, @@ -10,7 +10,7 @@ import { getImageFromClipboard } from '../../utils/imagePaste.js' import type { ImageDimensions } from '../../utils/imageResizer.js' import { ClickableImageRef } from '../ClickableImageRef.js' import { ConfigurableShortcutHint } from '../ConfigurableShortcutHint.js' -import { Byline } from '@anthropic/ink' +import { Byline } from '../design-system/Byline.js' import TextInput from '../TextInput.js' import type { OptionWithDescription } from './select.js' import { SelectOption } from './select-option.js' diff --git a/src/components/CustomSelect/select-option.tsx b/src/components/CustomSelect/select-option.tsx index a4c5a386d..2f84affd9 100644 --- a/src/components/CustomSelect/select-option.tsx +++ b/src/components/CustomSelect/select-option.tsx @@ -1,5 +1,5 @@ import React, { type ReactNode } from 'react' -import { ListItem } from '@anthropic/ink' +import { ListItem } from '../design-system/ListItem.js' export type SelectOptionProps = { /** diff --git a/src/components/CustomSelect/select.tsx b/src/components/CustomSelect/select.tsx index 7d368c2c3..d3a144772 100644 --- a/src/components/CustomSelect/select.tsx +++ b/src/components/CustomSelect/select.tsx @@ -1,6 +1,8 @@ import figures from 'figures' import React, { type ReactNode, useEffect, useRef, useState } from 'react' -import { Ansi, Box, Text, stringWidth, useDeclaredCursor } from '@anthropic/ink' +import { useDeclaredCursor } from '../../ink/hooks/use-declared-cursor.js' +import { stringWidth } from '../../ink/stringWidth.js' +import { Ansi, Box, Text } from '../../ink.js' import { count } from '../../utils/array.js' import type { PastedContent } from '../../utils/config.js' import type { ImageDimensions } from '../../utils/imageResizer.js' diff --git a/src/components/CustomSelect/use-multi-select-state.ts b/src/components/CustomSelect/use-multi-select-state.ts index a089a20d4..16b6c01b2 100644 --- a/src/components/CustomSelect/use-multi-select-state.ts +++ b/src/components/CustomSelect/use-multi-select-state.ts @@ -1,7 +1,9 @@ import { useCallback, useState } from 'react' import { isDeepStrictEqual } from 'util' import { useRegisterOverlay } from '../../context/overlayContext.js' -import { type InputEvent, useInput } from '@anthropic/ink' +import type { InputEvent } from '../../ink/events/input-event.js' +// eslint-disable-next-line custom-rules/prefer-use-keybindings -- raw space/arrow multiselect input +import { useInput } from '../../ink.js' import { normalizeFullWidthDigits, normalizeFullWidthSpace, diff --git a/src/components/CustomSelect/use-select-input.ts b/src/components/CustomSelect/use-select-input.ts index b289056ee..dcafeb4f9 100644 --- a/src/components/CustomSelect/use-select-input.ts +++ b/src/components/CustomSelect/use-select-input.ts @@ -1,6 +1,7 @@ import { useMemo } from 'react' import { useRegisterOverlay } from '../../context/overlayContext.js' -import { type InputEvent, useInput } from '@anthropic/ink' +import type { InputEvent } from '../../ink/events/input-event.js' +import { useInput } from '../../ink.js' import { useKeybindings } from '../../keybindings/useKeybinding.js' import { normalizeFullWidthDigits, diff --git a/src/components/DesktopHandoff.tsx b/src/components/DesktopHandoff.tsx index 8dfe65156..8e0632fd4 100644 --- a/src/components/DesktopHandoff.tsx +++ b/src/components/DesktopHandoff.tsx @@ -1,13 +1,16 @@ import React, { useEffect, useState } from 'react' import type { CommandResultDisplay } from '../commands.js' // eslint-disable-next-line custom-rules/prefer-use-keybindings -- raw input for "any key" dismiss and y/n prompt -import { Box, Text, useInput, LoadingState } from '@anthropic/ink' -import { getDesktopInstallStatus, openCurrentSessionInDesktop } from '../utils/desktopDeepLink.js' +import { Box, Text, useInput } from '../ink.js' import { openBrowser } from '../utils/browser.js' - +import { + getDesktopInstallStatus, + openCurrentSessionInDesktop, +} from '../utils/desktopDeepLink.js' import { errorMessage } from '../utils/errors.js' import { gracefulShutdown } from '../utils/gracefulShutdown.js' import { flushSessionStorage } from '../utils/sessionStorage.js' +import { LoadingState } from './design-system/LoadingState.js' const DESKTOP_DOCS_URL = 'https://clau.de/desktop' diff --git a/src/components/DesktopUpsell/DesktopUpsellStartup.tsx b/src/components/DesktopUpsell/DesktopUpsellStartup.tsx index f2ca4d46e..9f5f233a4 100644 --- a/src/components/DesktopUpsell/DesktopUpsellStartup.tsx +++ b/src/components/DesktopUpsell/DesktopUpsellStartup.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import { useEffect, useState } from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { getDynamicConfig_CACHED_MAY_BE_STALE } from '../../services/analytics/growthbook.js' import { logEvent } from '../../services/analytics/index.js' import { getGlobalConfig, saveGlobalConfig } from '../../utils/config.js' diff --git a/src/components/DevBar.tsx b/src/components/DevBar.tsx index 9695637ad..95ff6b983 100644 --- a/src/components/DevBar.tsx +++ b/src/components/DevBar.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import { useState } from 'react' import { getSlowOperations } from '../bootstrap/state.js' -import { Text, useInterval } from '@anthropic/ink' +import { Text, useInterval } from '../ink.js' // Show DevBar for dev builds or all ants function shouldShowDevBar(): boolean { diff --git a/src/components/DevChannelsDialog.tsx b/src/components/DevChannelsDialog.tsx index f055e4ae6..7dfc674cc 100644 --- a/src/components/DevChannelsDialog.tsx +++ b/src/components/DevChannelsDialog.tsx @@ -1,8 +1,9 @@ import React, { useCallback } from 'react' import type { ChannelEntry } from '../bootstrap/state.js' -import { Box, Text, Dialog } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import { gracefulShutdownSync } from '../utils/gracefulShutdown.js' import { Select } from './CustomSelect/index.js' +import { Dialog } from './design-system/Dialog.js' type Props = { channels: ChannelEntry[] diff --git a/src/components/DiagnosticsDisplay.tsx b/src/components/DiagnosticsDisplay.tsx index 5a92190b5..ad01c7756 100644 --- a/src/components/DiagnosticsDisplay.tsx +++ b/src/components/DiagnosticsDisplay.tsx @@ -1,6 +1,6 @@ import { relative } from 'path' import React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import { DiagnosticTrackingService } from '../services/diagnosticTracking.js' import type { Attachment } from '../utils/attachments.js' import { getCwd } from '../utils/cwd.js' diff --git a/src/components/EffortCallout.tsx b/src/components/EffortCallout.tsx index a9acc10a8..00feda439 100644 --- a/src/components/EffortCallout.tsx +++ b/src/components/EffortCallout.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useEffect, useRef } from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import { isMaxSubscriber, isProSubscriber, diff --git a/src/components/ExportDialog.tsx b/src/components/ExportDialog.tsx index b217d4c15..f4f1560a4 100644 --- a/src/components/ExportDialog.tsx +++ b/src/components/ExportDialog.tsx @@ -2,12 +2,16 @@ import { join } from 'path' import React, { useCallback, useState } from 'react' import type { ExitState } from '../hooks/useExitOnCtrlCDWithKeybindings.js' import { useTerminalSize } from '../hooks/useTerminalSize.js' -import { setClipboard, Box, Text, Byline, Dialog, KeyboardShortcutHint } from '@anthropic/ink' +import { setClipboard } from '../ink/termio/osc.js' +import { Box, Text } from '../ink.js' import { useKeybinding } from '../keybindings/useKeybinding.js' import { getCwd } from '../utils/cwd.js' import { writeFileSync_DEPRECATED } from '../utils/slowOperations.js' import { ConfigurableShortcutHint } from './ConfigurableShortcutHint.js' import { Select } from './CustomSelect/select.js' +import { Byline } from './design-system/Byline.js' +import { Dialog } from './design-system/Dialog.js' +import { KeyboardShortcutHint } from './design-system/KeyboardShortcutHint.js' import TextInput from './TextInput.js' type ExportDialogProps = { diff --git a/src/components/FallbackToolUseErrorMessage.tsx b/src/components/FallbackToolUseErrorMessage.tsx index a60ad65b9..d86ac2b7c 100644 --- a/src/components/FallbackToolUseErrorMessage.tsx +++ b/src/components/FallbackToolUseErrorMessage.tsx @@ -3,7 +3,7 @@ import * as React from 'react' import { stripUnderlineAnsi } from 'src/components/shell/OutputLine.js' import { extractTag } from 'src/utils/messages.js' import { removeSandboxViolationTags } from 'src/utils/sandbox/sandbox-ui-utils.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import { useShortcutDisplay } from '../keybindings/useShortcutDisplay.js' import { countCharInString } from '../utils/stringUtils.js' import { MessageResponse } from './MessageResponse.js' diff --git a/src/components/FastIcon.tsx b/src/components/FastIcon.tsx index b89283145..956c6290a 100644 --- a/src/components/FastIcon.tsx +++ b/src/components/FastIcon.tsx @@ -1,9 +1,10 @@ import chalk from 'chalk' import * as React from 'react' import { LIGHTNING_BOLT } from '../constants/figures.js' -import { Text, color } from '@anthropic/ink' +import { Text } from '../ink.js' import { getGlobalConfig } from '../utils/config.js' import { resolveThemeSetting } from '../utils/systemTheme.js' +import { color } from './design-system/color.js' type Props = { cooldown?: boolean diff --git a/src/components/Feedback.tsx b/src/components/Feedback.tsx index 6298b61b9..5d3a3678f 100644 --- a/src/components/Feedback.tsx +++ b/src/components/Feedback.tsx @@ -14,7 +14,7 @@ import { } from 'src/utils/messages.js' import type { CommandResultDisplay } from '../commands.js' import { useTerminalSize } from '../hooks/useTerminalSize.js' -import { Box, Text, useInput } from '@anthropic/ink' +import { Box, Text, useInput } from '../ink.js' import { useKeybinding } from '../keybindings/useKeybinding.js' import { queryHaiku } from '../services/api/claude.js' import { startsWithApiErrorPrefix } from '../services/api/errors.js' @@ -36,7 +36,9 @@ import { import { jsonStringify } from '../utils/slowOperations.js' import { asSystemPrompt } from '../utils/systemPromptType.js' import { ConfigurableShortcutHint } from './ConfigurableShortcutHint.js' -import { Byline, Dialog, KeyboardShortcutHint } from '@anthropic/ink' +import { Byline } from './design-system/Byline.js' +import { Dialog } from './design-system/Dialog.js' +import { KeyboardShortcutHint } from './design-system/KeyboardShortcutHint.js' import TextInput from './TextInput.js' // This value was determined experimentally by testing the URL length limit diff --git a/src/components/FeedbackSurvey/FeedbackSurvey.tsx b/src/components/FeedbackSurvey/FeedbackSurvey.tsx index a92213441..2f9c8e47d 100644 --- a/src/components/FeedbackSurvey/FeedbackSurvey.tsx +++ b/src/components/FeedbackSurvey/FeedbackSurvey.tsx @@ -3,7 +3,7 @@ import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent, } from 'src/services/analytics/index.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { FeedbackSurveyView, isValidResponseInput, diff --git a/src/components/FeedbackSurvey/FeedbackSurveyView.tsx b/src/components/FeedbackSurvey/FeedbackSurveyView.tsx index 5a8721e12..a8eadf3ba 100644 --- a/src/components/FeedbackSurvey/FeedbackSurveyView.tsx +++ b/src/components/FeedbackSurvey/FeedbackSurveyView.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { useDebouncedDigitInput } from './useDebouncedDigitInput.js' import type { FeedbackSurveyResponse } from './utils.js' diff --git a/src/components/FeedbackSurvey/TranscriptSharePrompt.tsx b/src/components/FeedbackSurvey/TranscriptSharePrompt.tsx index d3f39d25e..ec7a974f5 100644 --- a/src/components/FeedbackSurvey/TranscriptSharePrompt.tsx +++ b/src/components/FeedbackSurvey/TranscriptSharePrompt.tsx @@ -1,6 +1,6 @@ import React from 'react' import { BLACK_CIRCLE } from '../../constants/figures.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { useDebouncedDigitInput } from './useDebouncedDigitInput.js' export type TranscriptShareResponse = 'yes' | 'no' | 'dont_ask_again' diff --git a/src/components/FileEditToolDiff.tsx b/src/components/FileEditToolDiff.tsx index 75efda0f5..a5146d3c8 100644 --- a/src/components/FileEditToolDiff.tsx +++ b/src/components/FileEditToolDiff.tsx @@ -2,7 +2,7 @@ import type { StructuredPatchHunk } from 'diff' import * as React from 'react' import { Suspense, use, useState } from 'react' import { useTerminalSize } from '../hooks/useTerminalSize.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import type { FileEdit } from '../tools/FileEditTool/types.js' import { findActualString, diff --git a/src/components/FileEditToolUpdatedMessage.tsx b/src/components/FileEditToolUpdatedMessage.tsx index 5fbc68005..0248583af 100644 --- a/src/components/FileEditToolUpdatedMessage.tsx +++ b/src/components/FileEditToolUpdatedMessage.tsx @@ -1,7 +1,7 @@ import type { StructuredPatchHunk } from 'diff' import * as React from 'react' import { useTerminalSize } from '../hooks/useTerminalSize.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import { count } from '../utils/array.js' import { MessageResponse } from './MessageResponse.js' import { StructuredDiffList } from './StructuredDiffList.js' diff --git a/src/components/FileEditToolUseRejectedMessage.tsx b/src/components/FileEditToolUseRejectedMessage.tsx index 2d53117d2..6171b0f65 100644 --- a/src/components/FileEditToolUseRejectedMessage.tsx +++ b/src/components/FileEditToolUseRejectedMessage.tsx @@ -3,7 +3,7 @@ import { relative } from 'path' import * as React from 'react' import { useTerminalSize } from 'src/hooks/useTerminalSize.js' import { getCwd } from 'src/utils/cwd.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import { HighlightedCode } from './HighlightedCode.js' import { MessageResponse } from './MessageResponse.js' import { StructuredDiffList } from './StructuredDiffList.js' diff --git a/src/components/FilePathLink.tsx b/src/components/FilePathLink.tsx index 9ab7f3cd1..05a6167a2 100644 --- a/src/components/FilePathLink.tsx +++ b/src/components/FilePathLink.tsx @@ -1,6 +1,6 @@ import React from 'react' import { pathToFileURL } from 'url' -import { Link } from '@anthropic/ink' +import Link from '../ink/components/Link.js' type Props = { /** The absolute file path */ diff --git a/src/components/FullscreenLayout.tsx b/src/components/FullscreenLayout.tsx index 608c7b1c9..8502e46de 100644 --- a/src/components/FullscreenLayout.tsx +++ b/src/components/FullscreenLayout.tsx @@ -19,7 +19,9 @@ import { usePromptOverlayDialog, } from '../context/promptOverlayContext.js' import { useTerminalSize } from '../hooks/useTerminalSize.js' -import { Box, ScrollBox, type ScrollBoxHandle, Text, instances } from '@anthropic/ink' +import ScrollBox, { type ScrollBoxHandle } from '../ink/components/ScrollBox.js' +import instances from '../ink/instances.js' +import { Box, Text } from '../ink.js' import type { Message } from '../types/message.js' import { openBrowser, openPath } from '../utils/browser.js' import { isFullscreenEnvEnabled } from '../utils/fullscreen.js' diff --git a/src/components/GlobalSearchDialog.tsx b/src/components/GlobalSearchDialog.tsx index ee1d2fdf5..0df3231ce 100644 --- a/src/components/GlobalSearchDialog.tsx +++ b/src/components/GlobalSearchDialog.tsx @@ -3,7 +3,7 @@ import * as React from 'react' import { useEffect, useRef, useState } from 'react' import { useRegisterOverlay } from '../context/overlayContext.js' import { useTerminalSize } from '../hooks/useTerminalSize.js' -import { Text } from '@anthropic/ink' +import { Text } from '../ink.js' import { logEvent } from '../services/analytics/index.js' import { getCwd } from '../utils/cwd.js' import { openFileInExternalEditor } from '../utils/editor.js' @@ -12,7 +12,8 @@ import { highlightMatch } from '../utils/highlightMatch.js' import { relativePath } from '../utils/permissions/filesystem.js' import { readFileInRange } from '../utils/readFileInRange.js' import { ripGrepStream } from '../utils/ripgrep.js' -import { FuzzyPicker, LoadingState } from '@anthropic/ink' +import { FuzzyPicker } from './design-system/FuzzyPicker.js' +import { LoadingState } from './design-system/LoadingState.js' type Props = { onDone: () => void diff --git a/src/components/HelpV2/Commands.tsx b/src/components/HelpV2/Commands.tsx index dd5eda75e..9e34e5539 100644 --- a/src/components/HelpV2/Commands.tsx +++ b/src/components/HelpV2/Commands.tsx @@ -1,7 +1,9 @@ import * as React from 'react' import { useMemo } from 'react' import { type Command, formatDescriptionWithSource } from '../../commands.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' +import { truncate } from '../../utils/format.js' +import { Select } from '../CustomSelect/select.js' import { useTabHeaderFocus } from '../design-system/Tabs.js' type Props = { diff --git a/src/components/HelpV2/General.tsx b/src/components/HelpV2/General.tsx index 4117d9568..69b5c6509 100644 --- a/src/components/HelpV2/General.tsx +++ b/src/components/HelpV2/General.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { PromptInputHelpMenu } from '../PromptInput/PromptInputHelpMenu.js' export function General(): React.ReactNode { diff --git a/src/components/HelpV2/HelpV2.tsx b/src/components/HelpV2/HelpV2.tsx index aa077f649..9e2b0ce27 100644 --- a/src/components/HelpV2/HelpV2.tsx +++ b/src/components/HelpV2/HelpV2.tsx @@ -9,10 +9,10 @@ import { } from '../../commands.js' import { useIsInsideModal } from '../../context/modalContext.js' import { useTerminalSize } from '../../hooks/useTerminalSize.js' -import { Box, Link, Text } from '@anthropic/ink' +import { Box, Link, Text } from '../../ink.js' import { useKeybinding } from '../../keybindings/useKeybinding.js' -import { Pane, Tabs } from '@anthropic/ink' -import { Tab } from '../design-system/Tabs.js' +import { Pane } from '../design-system/Pane.js' +import { Tab, Tabs } from '../design-system/Tabs.js' import { Commands } from './Commands.js' import { General } from './General.js' diff --git a/src/components/HighlightedCode.tsx b/src/components/HighlightedCode.tsx index 3cfe9a736..47f7271bc 100644 --- a/src/components/HighlightedCode.tsx +++ b/src/components/HighlightedCode.tsx @@ -1,7 +1,15 @@ import * as React from 'react' import { memo, useEffect, useMemo, useRef, useState } from 'react' import { useSettings } from '../hooks/useSettings.js' -import { Ansi, Box, type DOMElement, measureElement, NoSelect, Text, useTheme } from '@anthropic/ink' +import { + Ansi, + Box, + type DOMElement, + measureElement, + NoSelect, + Text, + useTheme, +} from '../ink.js' import { isFullscreenEnvEnabled } from '../utils/fullscreen.js' import sliceAnsi from '../utils/sliceAnsi.js' import { countCharInString } from '../utils/stringUtils.js' diff --git a/src/components/HighlightedCode/Fallback.tsx b/src/components/HighlightedCode/Fallback.tsx index e81d44f3d..3d1f70112 100644 --- a/src/components/HighlightedCode/Fallback.tsx +++ b/src/components/HighlightedCode/Fallback.tsx @@ -1,6 +1,6 @@ import { extname } from 'path' import React, { Suspense, use, useMemo } from 'react' -import { Ansi, Text } from '@anthropic/ink' +import { Ansi, Text } from '../../ink.js' import { getCliHighlightPromise } from '../../utils/cliHighlight.js' import { logForDebugging } from '../../utils/debug.js' import { convertLeadingTabsToSpaces } from '../../utils/file.js' diff --git a/src/components/HistorySearchDialog.tsx b/src/components/HistorySearchDialog.tsx index 54bf98372..dd2e02da5 100644 --- a/src/components/HistorySearchDialog.tsx +++ b/src/components/HistorySearchDialog.tsx @@ -6,11 +6,13 @@ import { type TimestampedHistoryEntry, } from '../history.js' import { useTerminalSize } from '../hooks/useTerminalSize.js' -import { Box, Text, stringWidth, wrapAnsi } from '@anthropic/ink' +import { stringWidth } from '../ink/stringWidth.js' +import { wrapAnsi } from '../ink/wrapAnsi.js' +import { Box, Text } from '../ink.js' import { logEvent } from '../services/analytics/index.js' import type { HistoryEntry } from '../utils/config.js' import { formatRelativeTimeAgo, truncateToWidth } from '../utils/format.js' -import { FuzzyPicker } from '@anthropic/ink' +import { FuzzyPicker } from './design-system/FuzzyPicker.js' type Props = { initialQuery?: string diff --git a/src/components/IdeAutoConnectDialog.tsx b/src/components/IdeAutoConnectDialog.tsx index f262cc465..2377cfb3a 100644 --- a/src/components/IdeAutoConnectDialog.tsx +++ b/src/components/IdeAutoConnectDialog.tsx @@ -1,8 +1,9 @@ import React, { useCallback } from 'react' -import { Text, Dialog } from '@anthropic/ink' +import { Text } from '../ink.js' import { getGlobalConfig, saveGlobalConfig } from '../utils/config.js' import { isSupportedTerminal } from '../utils/ide.js' import { Select } from './CustomSelect/index.js' +import { Dialog } from './design-system/Dialog.js' type IdeAutoConnectDialogProps = { onComplete: () => void diff --git a/src/components/IdeOnboardingDialog.tsx b/src/components/IdeOnboardingDialog.tsx index aae0b1742..86f03018e 100644 --- a/src/components/IdeOnboardingDialog.tsx +++ b/src/components/IdeOnboardingDialog.tsx @@ -1,6 +1,6 @@ import React from 'react' import { envDynamic } from 'src/utils/envDynamic.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import { useKeybindings } from '../keybindings/useKeybinding.js' import { getGlobalConfig, saveGlobalConfig } from '../utils/config.js' import { env } from '../utils/env.js' @@ -10,7 +10,7 @@ import { isJetBrainsIde, toIDEDisplayName, } from '../utils/ide.js' -import { Dialog } from '@anthropic/ink' +import { Dialog } from './design-system/Dialog.js' interface Props { onDone: () => void diff --git a/src/components/IdeStatusIndicator.tsx b/src/components/IdeStatusIndicator.tsx index 13096b120..13c1846c0 100644 --- a/src/components/IdeStatusIndicator.tsx +++ b/src/components/IdeStatusIndicator.tsx @@ -2,7 +2,7 @@ import { basename } from 'path' import * as React from 'react' import { useIdeConnectionStatus } from '../hooks/useIdeConnectionStatus.js' import type { IDESelection } from '../hooks/useIdeSelection.js' -import { Text } from '@anthropic/ink' +import { Text } from '../ink.js' import type { MCPServerConnection } from '../services/mcp/types.js' type IdeStatusIndicatorProps = { diff --git a/src/components/IdleReturnDialog.tsx b/src/components/IdleReturnDialog.tsx index 9ffc8893a..d651cfe38 100644 --- a/src/components/IdleReturnDialog.tsx +++ b/src/components/IdleReturnDialog.tsx @@ -1,8 +1,8 @@ import React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import { formatTokens } from '../utils/format.js' import { Select } from './CustomSelect/index.js' -import { Dialog } from '@anthropic/ink' +import { Dialog } from './design-system/Dialog.js' type IdleReturnAction = 'continue' | 'clear' | 'dismiss' | 'never' diff --git a/src/components/InterruptedByUser.tsx b/src/components/InterruptedByUser.tsx index 13c641f0b..0a77c7153 100644 --- a/src/components/InterruptedByUser.tsx +++ b/src/components/InterruptedByUser.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { Text } from '@anthropic/ink' +import { Text } from '../ink.js' export function InterruptedByUser(): React.ReactNode { return ( diff --git a/src/components/InvalidConfigDialog.tsx b/src/components/InvalidConfigDialog.tsx index 7bbc04b14..8fa3bba97 100644 --- a/src/components/InvalidConfigDialog.tsx +++ b/src/components/InvalidConfigDialog.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { Box, Dialog, wrappedRender as render, Text } from '@anthropic/ink' +import { Box, render, Text } from '../ink.js' import { KeybindingSetup } from '../keybindings/KeybindingProviderSetup.js' import { AppStateProvider } from '../state/AppState.js' import type { ConfigParseError } from '../utils/errors.js' @@ -10,6 +10,7 @@ import { } from '../utils/slowOperations.js' import type { ThemeName } from '../utils/theme.js' import { Select } from './CustomSelect/index.js' +import { Dialog } from './design-system/Dialog.js' interface InvalidConfigHandlerProps { error: ConfigParseError diff --git a/src/components/InvalidSettingsDialog.tsx b/src/components/InvalidSettingsDialog.tsx index f3b0db018..c1fddf96a 100644 --- a/src/components/InvalidSettingsDialog.tsx +++ b/src/components/InvalidSettingsDialog.tsx @@ -1,7 +1,8 @@ import React from 'react' -import { Text, Dialog } from '@anthropic/ink' +import { Text } from '../ink.js' import type { ValidationError } from '../utils/settings/validation.js' import { Select } from './CustomSelect/index.js' +import { Dialog } from './design-system/Dialog.js' import { ValidationErrorsList } from './ValidationErrorsList.js' type Props = { diff --git a/src/components/KeybindingWarnings.tsx b/src/components/KeybindingWarnings.tsx index dc1b5a74a..8f6957c3e 100644 --- a/src/components/KeybindingWarnings.tsx +++ b/src/components/KeybindingWarnings.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import { getCachedKeybindingWarnings, getKeybindingsPath, diff --git a/src/components/LanguagePicker.tsx b/src/components/LanguagePicker.tsx index ae357ff8e..53be69d48 100644 --- a/src/components/LanguagePicker.tsx +++ b/src/components/LanguagePicker.tsx @@ -1,6 +1,6 @@ import figures from 'figures' import React, { useState } from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import { useKeybinding } from '../keybindings/useKeybinding.js' import TextInput from './TextInput.js' diff --git a/src/components/LogSelector.tsx b/src/components/LogSelector.tsx index 806a0082d..d1fb9f607 100644 --- a/src/components/LogSelector.tsx +++ b/src/components/LogSelector.tsx @@ -6,7 +6,9 @@ import { getOriginalCwd, getSessionId } from '../bootstrap/state.js' import { useExitOnCtrlCDWithKeybindings } from '../hooks/useExitOnCtrlCDWithKeybindings.js' import { useSearchInput } from '../hooks/useSearchInput.js' import { useTerminalSize } from '../hooks/useTerminalSize.js' -import { applyColor, Box, Text, useInput, useTerminalFocus, useTheme, type Color, Byline, Divider, KeyboardShortcutHint } from '@anthropic/ink' +import { applyColor } from '../ink/colorize.js' +import type { Color } from '../ink/styles.js' +import { Box, Text, useInput, useTerminalFocus, useTheme } from '../ink.js' import { useKeybinding } from '../keybindings/useKeybinding.js' import { logEvent } from '../services/analytics/index.js' import type { LogOption, SerializedMessage } from '../types/logs.js' @@ -23,6 +25,9 @@ import { import { getTheme } from '../utils/theme.js' import { ConfigurableShortcutHint } from './ConfigurableShortcutHint.js' import { Select } from './CustomSelect/select.js' +import { Byline } from './design-system/Byline.js' +import { Divider } from './design-system/Divider.js' +import { KeyboardShortcutHint } from './design-system/KeyboardShortcutHint.js' import { SearchBox } from './SearchBox.js' import { SessionPreview } from './SessionPreview.js' import { Spinner } from './Spinner.js' diff --git a/src/components/LogoV2/AnimatedAsterisk.tsx b/src/components/LogoV2/AnimatedAsterisk.tsx index 9e72baaa2..1c5adcf06 100644 --- a/src/components/LogoV2/AnimatedAsterisk.tsx +++ b/src/components/LogoV2/AnimatedAsterisk.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import { useEffect, useRef, useState } from 'react' import { TEARDROP_ASTERISK } from '../../constants/figures.js' -import { Box, Text, useAnimationFrame } from '@anthropic/ink' +import { Box, Text, useAnimationFrame } from '../../ink.js' import { getInitialSettings } from '../../utils/settings/settings.js' import { hueToRgb, toRGBColor } from '../Spinner/utils.js' diff --git a/src/components/LogoV2/AnimatedClawd.tsx b/src/components/LogoV2/AnimatedClawd.tsx index 5ad68babb..ed3060066 100644 --- a/src/components/LogoV2/AnimatedClawd.tsx +++ b/src/components/LogoV2/AnimatedClawd.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import { useEffect, useRef, useState } from 'react' -import { Box } from '@anthropic/ink' +import { Box } from '../../ink.js' import { getInitialSettings } from '../../utils/settings/settings.js' import { Clawd, type ClawdPose } from './Clawd.js' diff --git a/src/components/LogoV2/ChannelsNotice.tsx b/src/components/LogoV2/ChannelsNotice.tsx index c58f400c1..66f41b303 100644 --- a/src/components/LogoV2/ChannelsNotice.tsx +++ b/src/components/LogoV2/ChannelsNotice.tsx @@ -11,7 +11,7 @@ import { getAllowedChannels, getHasDevChannels, } from '../../bootstrap/state.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { isChannelsEnabled } from '../../services/mcp/channelAllowlist.js' import { getEffectiveChannelAllowlist } from '../../services/mcp/channelNotification.js' import { getMcpConfigsByScope } from '../../services/mcp/config.js' diff --git a/src/components/LogoV2/Clawd.tsx b/src/components/LogoV2/Clawd.tsx index 6969466bc..8ddc1bf8e 100644 --- a/src/components/LogoV2/Clawd.tsx +++ b/src/components/LogoV2/Clawd.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { env } from '../../utils/env.js' export type ClawdPose = diff --git a/src/components/LogoV2/CondensedLogo.tsx b/src/components/LogoV2/CondensedLogo.tsx index eb048ec2d..be587bf83 100644 --- a/src/components/LogoV2/CondensedLogo.tsx +++ b/src/components/LogoV2/CondensedLogo.tsx @@ -2,7 +2,8 @@ import * as React from 'react' import { type ReactNode, useEffect } from 'react' import { useMainLoopModel } from '../../hooks/useMainLoopModel.js' import { useTerminalSize } from '../../hooks/useTerminalSize.js' -import { Box, Text, stringWidth } from '@anthropic/ink' +import { stringWidth } from '../../ink/stringWidth.js' +import { Box, Text } from '../../ink.js' import { useAppState } from '../../state/AppState.js' import { getEffortSuffix } from '../../utils/effort.js' import { truncate } from '../../utils/format.js' diff --git a/src/components/LogoV2/EmergencyTip.tsx b/src/components/LogoV2/EmergencyTip.tsx index 33280bcf7..c0a8235ba 100644 --- a/src/components/LogoV2/EmergencyTip.tsx +++ b/src/components/LogoV2/EmergencyTip.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import { useEffect, useMemo } from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from 'src/ink.js' import { getDynamicConfig_CACHED_MAY_BE_STALE } from 'src/services/analytics/growthbook.js' import { getGlobalConfig, saveGlobalConfig } from 'src/utils/config.js' diff --git a/src/components/LogoV2/Feed.tsx b/src/components/LogoV2/Feed.tsx index be849bfd7..15a7d84d6 100644 --- a/src/components/LogoV2/Feed.tsx +++ b/src/components/LogoV2/Feed.tsx @@ -1,5 +1,6 @@ import * as React from 'react' -import { Box, Text, stringWidth } from '@anthropic/ink' +import { stringWidth } from '../../ink/stringWidth.js' +import { Box, Text } from '../../ink.js' import { truncate } from '../../utils/format.js' export type FeedLine = { diff --git a/src/components/LogoV2/FeedColumn.tsx b/src/components/LogoV2/FeedColumn.tsx index 4c6ae84f5..0b08ec84a 100644 --- a/src/components/LogoV2/FeedColumn.tsx +++ b/src/components/LogoV2/FeedColumn.tsx @@ -1,6 +1,6 @@ import * as React from 'react' -import { Box } from '@anthropic/ink' -import { Divider } from '@anthropic/ink' +import { Box } from '../../ink.js' +import { Divider } from '../design-system/Divider.js' import type { FeedConfig } from './Feed.js' import { calculateFeedWidth, Feed } from './Feed.js' diff --git a/src/components/LogoV2/GuestPassesUpsell.tsx b/src/components/LogoV2/GuestPassesUpsell.tsx index c1bae01b4..12796e43b 100644 --- a/src/components/LogoV2/GuestPassesUpsell.tsx +++ b/src/components/LogoV2/GuestPassesUpsell.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import { useState } from 'react' -import { Text } from '@anthropic/ink' +import { Text } from '../../ink.js' import { logEvent } from '../../services/analytics/index.js' import { checkCachedPassesEligibility, diff --git a/src/components/LogoV2/LogoV2.tsx b/src/components/LogoV2/LogoV2.tsx index e457f1084..d65c24fe3 100644 --- a/src/components/LogoV2/LogoV2.tsx +++ b/src/components/LogoV2/LogoV2.tsx @@ -1,7 +1,8 @@ // biome-ignore-all assist/source/organizeImports: ANT-ONLY import markers must not be reordered import * as React from 'react' -import { Box, Text, color, stringWidth } from '@anthropic/ink' +import { Box, Text, color } from '../../ink.js' import { useTerminalSize } from '../../hooks/useTerminalSize.js' +import { stringWidth } from '../../ink/stringWidth.js' import { getLayoutMode, calculateLayoutDimensions, diff --git a/src/components/LogoV2/Opus1mMergeNotice.tsx b/src/components/LogoV2/Opus1mMergeNotice.tsx index 9bbf84752..63c42ab66 100644 --- a/src/components/LogoV2/Opus1mMergeNotice.tsx +++ b/src/components/LogoV2/Opus1mMergeNotice.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import { useEffect, useState } from 'react' import { UP_ARROW } from '../../constants/figures.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { getGlobalConfig, saveGlobalConfig } from '../../utils/config.js' import { isOpus1mMergeEnabled } from '../../utils/model/model.js' import { AnimatedAsterisk } from './AnimatedAsterisk.js' diff --git a/src/components/LogoV2/OverageCreditUpsell.tsx b/src/components/LogoV2/OverageCreditUpsell.tsx index c08140645..ce006e0d0 100644 --- a/src/components/LogoV2/OverageCreditUpsell.tsx +++ b/src/components/LogoV2/OverageCreditUpsell.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import { useState } from 'react' -import { Text } from '@anthropic/ink' +import { Text } from '../../ink.js' import { logEvent } from '../../services/analytics/index.js' import { formatGrantAmount, diff --git a/src/components/LogoV2/VoiceModeNotice.tsx b/src/components/LogoV2/VoiceModeNotice.tsx index b8e74e3c6..531460533 100644 --- a/src/components/LogoV2/VoiceModeNotice.tsx +++ b/src/components/LogoV2/VoiceModeNotice.tsx @@ -1,7 +1,7 @@ import { feature } from 'bun:bundle' import * as React from 'react' import { useEffect, useState } from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { getGlobalConfig, saveGlobalConfig } from '../../utils/config.js' import { getInitialSettings } from '../../utils/settings/settings.js' import { isVoiceModeEnabled } from '../../voice/voiceModeEnabled.js' diff --git a/src/components/LogoV2/WelcomeV2.tsx b/src/components/LogoV2/WelcomeV2.tsx index ccbbcbf44..354e1182b 100644 --- a/src/components/LogoV2/WelcomeV2.tsx +++ b/src/components/LogoV2/WelcomeV2.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { Box, Text, useTheme } from '@anthropic/ink' +import { Box, Text, useTheme } from 'src/ink.js' import { env } from '../../utils/env.js' const WELCOME_V2_WIDTH = 58 diff --git a/src/components/LogoV2/feedConfigs.tsx b/src/components/LogoV2/feedConfigs.tsx index 8f6652f3c..50ec4575c 100644 --- a/src/components/LogoV2/feedConfigs.tsx +++ b/src/components/LogoV2/feedConfigs.tsx @@ -1,7 +1,7 @@ import figures from 'figures' import { homedir } from 'os' import * as React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import type { Step } from '../../projectOnboardingState.js' import { formatCreditAmount, diff --git a/src/components/LspRecommendation/LspRecommendationMenu.tsx b/src/components/LspRecommendation/LspRecommendationMenu.tsx index 3ea815069..7dc41ac39 100644 --- a/src/components/LspRecommendation/LspRecommendationMenu.tsx +++ b/src/components/LspRecommendation/LspRecommendationMenu.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { Select } from '../CustomSelect/select.js' import { PermissionDialog } from '../permissions/PermissionDialog.js' diff --git a/src/components/MCPServerApprovalDialog.tsx b/src/components/MCPServerApprovalDialog.tsx index f80d42008..5d5e00898 100644 --- a/src/components/MCPServerApprovalDialog.tsx +++ b/src/components/MCPServerApprovalDialog.tsx @@ -8,7 +8,7 @@ import { updateSettingsForSource, } from '../utils/settings/settings.js' import { Select } from './CustomSelect/index.js' -import { Dialog } from '@anthropic/ink' +import { Dialog } from './design-system/Dialog.js' import { MCPServerDialogCopy } from './MCPServerDialogCopy.js' type Props = { diff --git a/src/components/MCPServerDesktopImportDialog.tsx b/src/components/MCPServerDesktopImportDialog.tsx index aaea61894..50b9ef6d6 100644 --- a/src/components/MCPServerDesktopImportDialog.tsx +++ b/src/components/MCPServerDesktopImportDialog.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useEffect, useState } from 'react' import { gracefulShutdown } from 'src/utils/gracefulShutdown.js' import { writeToStdout } from 'src/utils/process.js' -import { Box, color, Text, useTheme, Byline, Dialog, KeyboardShortcutHint } from '@anthropic/ink' +import { Box, color, Text, useTheme } from '../ink.js' import { addMcpConfig, getAllMcpConfigs } from '../services/mcp/config.js' import type { ConfigScope, @@ -11,6 +11,9 @@ import type { import { plural } from '../utils/stringUtils.js' import { ConfigurableShortcutHint } from './ConfigurableShortcutHint.js' import { SelectMulti } from './CustomSelect/SelectMulti.js' +import { Byline } from './design-system/Byline.js' +import { Dialog } from './design-system/Dialog.js' +import { KeyboardShortcutHint } from './design-system/KeyboardShortcutHint.js' type Props = { servers: Record diff --git a/src/components/MCPServerDialogCopy.tsx b/src/components/MCPServerDialogCopy.tsx index f6a87674f..93dce3655 100644 --- a/src/components/MCPServerDialogCopy.tsx +++ b/src/components/MCPServerDialogCopy.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { Link, Text } from '@anthropic/ink' +import { Link, Text } from '../ink.js' export function MCPServerDialogCopy(): React.ReactNode { return ( diff --git a/src/components/MCPServerMultiselectDialog.tsx b/src/components/MCPServerMultiselectDialog.tsx index fa41da3a7..e14c46d46 100644 --- a/src/components/MCPServerMultiselectDialog.tsx +++ b/src/components/MCPServerMultiselectDialog.tsx @@ -1,14 +1,16 @@ import partition from 'lodash-es/partition.js' import React, { useCallback } from 'react' import { logEvent } from 'src/services/analytics/index.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import { getSettings_DEPRECATED, updateSettingsForSource, } from '../utils/settings/settings.js' import { ConfigurableShortcutHint } from './ConfigurableShortcutHint.js' import { SelectMulti } from './CustomSelect/SelectMulti.js' -import { Byline, Dialog, KeyboardShortcutHint } from '@anthropic/ink' +import { Byline } from './design-system/Byline.js' +import { Dialog } from './design-system/Dialog.js' +import { KeyboardShortcutHint } from './design-system/KeyboardShortcutHint.js' import { MCPServerDialogCopy } from './MCPServerDialogCopy.js' type Props = { diff --git a/src/components/ManagedSettingsSecurityDialog/ManagedSettingsSecurityDialog.tsx b/src/components/ManagedSettingsSecurityDialog/ManagedSettingsSecurityDialog.tsx index 2d7de2ec7..392979770 100644 --- a/src/components/ManagedSettingsSecurityDialog/ManagedSettingsSecurityDialog.tsx +++ b/src/components/ManagedSettingsSecurityDialog/ManagedSettingsSecurityDialog.tsx @@ -1,6 +1,6 @@ import React from 'react' import { useExitOnCtrlCDWithKeybindings } from '../../hooks/useExitOnCtrlCDWithKeybindings.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { useKeybinding } from '../../keybindings/useKeybinding.js' import type { SettingsJson } from '../../utils/settings/types.js' import { Select } from '../CustomSelect/index.js' diff --git a/src/components/Markdown.tsx b/src/components/Markdown.tsx index 063404baf..4616f46c2 100644 --- a/src/components/Markdown.tsx +++ b/src/components/Markdown.tsx @@ -1,7 +1,7 @@ import { marked, type Token, type Tokens } from 'marked' import React, { Suspense, use, useMemo, useRef } from 'react' import { useSettings } from '../hooks/useSettings.js' -import { Ansi, Box, useTheme } from '@anthropic/ink' +import { Ansi, Box, useTheme } from '../ink.js' import { type CliHighlight, getCliHighlightPromise, diff --git a/src/components/MarkdownTable.tsx b/src/components/MarkdownTable.tsx index 0b3473d43..c8997d9a1 100644 --- a/src/components/MarkdownTable.tsx +++ b/src/components/MarkdownTable.tsx @@ -2,7 +2,9 @@ import type { Token, Tokens } from 'marked' import React from 'react' import stripAnsi from 'strip-ansi' import { useTerminalSize } from '../hooks/useTerminalSize.js' -import { Ansi, stringWidth, useTheme, wrapAnsi } from '@anthropic/ink' +import { stringWidth } from '../ink/stringWidth.js' +import { wrapAnsi } from '../ink/wrapAnsi.js' +import { Ansi, useTheme } from '../ink.js' import type { CliHighlight } from '../utils/cliHighlight.js' import { formatToken, padAligned } from '../utils/markdown.js' diff --git a/src/components/MemoryUsageIndicator.tsx b/src/components/MemoryUsageIndicator.tsx index 4004133c2..37c91e778 100644 --- a/src/components/MemoryUsageIndicator.tsx +++ b/src/components/MemoryUsageIndicator.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import { useMemoryUsage } from '../hooks/useMemoryUsage.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import { formatFileSize } from '../utils/format.js' export function MemoryUsageIndicator(): React.ReactNode { diff --git a/src/components/Message.tsx b/src/components/Message.tsx index 7f5055465..79a152682 100644 --- a/src/components/Message.tsx +++ b/src/components/Message.tsx @@ -10,7 +10,7 @@ import type { import * as React from 'react' import type { Command } from '../commands.js' import { useTerminalSize } from '../hooks/useTerminalSize.js' -import { Box } from '@anthropic/ink' +import { Box } from '../ink.js' import type { Tools } from '../Tool.js' import { type ConnectorTextBlock, diff --git a/src/components/MessageModel.tsx b/src/components/MessageModel.tsx index 4afa5bc33..a99f861e6 100644 --- a/src/components/MessageModel.tsx +++ b/src/components/MessageModel.tsx @@ -1,5 +1,6 @@ import React from 'react' -import { Box, Text, stringWidth } from '@anthropic/ink' +import { stringWidth } from '../ink/stringWidth.js' +import { Box, Text } from '../ink.js' import type { NormalizedMessage } from '../types/message.js' type Props = { diff --git a/src/components/MessageResponse.tsx b/src/components/MessageResponse.tsx index a8dd8613c..f71d40ce8 100644 --- a/src/components/MessageResponse.tsx +++ b/src/components/MessageResponse.tsx @@ -1,6 +1,7 @@ import * as React from 'react' import { useContext } from 'react' -import { Box, NoSelect, Text, Ratchet } from '@anthropic/ink' +import { Box, NoSelect, Text } from '../ink.js' +import { Ratchet } from './design-system/Ratchet.js' type Props = { children: React.ReactNode diff --git a/src/components/MessageRow.tsx b/src/components/MessageRow.tsx index e02a2708f..e42fb9d96 100644 --- a/src/components/MessageRow.tsx +++ b/src/components/MessageRow.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import type { Command } from '../commands.js' -import { Box } from '@anthropic/ink' +import { Box } from '../ink.js' import type { Screen } from '../screens/REPL.js' import type { Tools } from '../Tool.js' import type { RenderableMessage } from '../types/message.js' diff --git a/src/components/MessageSelector.tsx b/src/components/MessageSelector.tsx index ab4e12292..b372a4b5d 100644 --- a/src/components/MessageSelector.tsx +++ b/src/components/MessageSelector.tsx @@ -19,7 +19,7 @@ import { } from 'src/utils/fileHistory.js' import { logError } from 'src/utils/log.js' import { useExitOnCtrlCDWithKeybindings } from '../hooks/useExitOnCtrlCDWithKeybindings.js' -import { Box, Text, Divider } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import { useKeybinding, useKeybindings } from '../keybindings/useKeybinding.js' import type { Message, @@ -58,6 +58,8 @@ import { import { count } from '../utils/array.js' import { formatRelativeTimeAgo, truncate } from '../utils/format.js' import type { Theme } from '../utils/theme.js' +import { Divider } from './design-system/Divider.js' + type RestoreOption = | 'both' | 'conversation' diff --git a/src/components/MessageTimestamp.tsx b/src/components/MessageTimestamp.tsx index 3b0e07322..8eac935e5 100644 --- a/src/components/MessageTimestamp.tsx +++ b/src/components/MessageTimestamp.tsx @@ -1,5 +1,6 @@ import React from 'react' -import { Box, Text, stringWidth } from '@anthropic/ink' +import { stringWidth } from '../ink/stringWidth.js' +import { Box, Text } from '../ink.js' import type { NormalizedMessage } from '../types/message.js' type Props = { diff --git a/src/components/Messages.tsx b/src/components/Messages.tsx index bd165b535..c946a9fd7 100644 --- a/src/components/Messages.tsx +++ b/src/components/Messages.tsx @@ -9,9 +9,9 @@ import { getIsRemoteMode } from '../bootstrap/state.js' import type { Command } from '../commands.js' import { BLACK_CIRCLE } from '../constants/figures.js' import { useTerminalSize } from '../hooks/useTerminalSize.js' -import type { ScrollBoxHandle } from '@anthropic/ink' -import { useTerminalNotification } from '@anthropic/ink' -import { Box, Text } from '@anthropic/ink' +import type { ScrollBoxHandle } from '../ink/components/ScrollBox.js' +import { useTerminalNotification } from '../ink/useTerminalNotification.js' +import { Box, Text } from '../ink.js' import { useShortcutDisplay } from '../keybindings/useShortcutDisplay.js' import type { Screen } from '../screens/REPL.js' import type { Tools } from '../Tool.js' @@ -49,7 +49,7 @@ import { } from '../utils/messages.js' import { plural } from '../utils/stringUtils.js' import { renderableSearchText } from '../utils/transcriptSearch.js' -import { Divider } from '@anthropic/ink' +import { Divider } from './design-system/Divider.js' import type { UnseenDivider } from './FullscreenLayout.js' import { LogoV2 } from './LogoV2/LogoV2.js' import { StreamingMarkdown } from './Markdown.js' @@ -291,13 +291,13 @@ type Props = { /** Paint an existing DOM subtree to fresh Screen, scan. Element comes * from the main tree (all real providers). Message-relative positions. */ scanElement?: ( - el: import('@anthropic/ink').DOMElement, - ) => import('@anthropic/ink').MatchPosition[] + el: import('../ink/dom.js').DOMElement, + ) => import('../ink/render-to-screen.js').MatchPosition[] /** Position-based CURRENT highlight. positions stable (msg-relative), * rowOffset tracks scroll. null clears. */ setPositions?: ( state: { - positions: import('@anthropic/ink').MatchPosition[] + positions: import('../ink/render-to-screen.js').MatchPosition[] rowOffset: number currentIdx: number } | null, diff --git a/src/components/ModelPicker.tsx b/src/components/ModelPicker.tsx index 4977dbbcb..6658ad62d 100644 --- a/src/components/ModelPicker.tsx +++ b/src/components/ModelPicker.tsx @@ -12,7 +12,7 @@ import { isFastModeCooldown, isFastModeEnabled, } from 'src/utils/fastMode.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import { useKeybindings } from '../keybindings/useKeybinding.js' import { useAppState, useSetAppState } from '../state/AppState.js' import { @@ -37,7 +37,9 @@ import { } from '../utils/settings/settings.js' import { ConfigurableShortcutHint } from './ConfigurableShortcutHint.js' import { Select } from './CustomSelect/index.js' -import { Byline, KeyboardShortcutHint, Pane } from '@anthropic/ink' +import { Byline } from './design-system/Byline.js' +import { KeyboardShortcutHint } from './design-system/KeyboardShortcutHint.js' +import { Pane } from './design-system/Pane.js' import { effortLevelToSymbol } from './EffortIndicator.js' export type Props = { diff --git a/src/components/NativeAutoUpdater.tsx b/src/components/NativeAutoUpdater.tsx index 174d07951..fcb448ade 100644 --- a/src/components/NativeAutoUpdater.tsx +++ b/src/components/NativeAutoUpdater.tsx @@ -5,7 +5,7 @@ import { logForDebugging } from 'src/utils/debug.js' import { logError } from 'src/utils/log.js' import { useInterval } from 'usehooks-ts' import { useUpdateNotification } from '../hooks/useUpdateNotification.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import type { AutoUpdaterResult } from '../utils/autoUpdater.js' import { getMaxVersion, getMaxVersionMessage } from '../utils/autoUpdater.js' import { isAutoUpdaterDisabled } from '../utils/config.js' diff --git a/src/components/NotebookEditToolUseRejectedMessage.tsx b/src/components/NotebookEditToolUseRejectedMessage.tsx index fc4e4f317..4eb3cf887 100644 --- a/src/components/NotebookEditToolUseRejectedMessage.tsx +++ b/src/components/NotebookEditToolUseRejectedMessage.tsx @@ -1,7 +1,7 @@ import { relative } from 'path' import * as React from 'react' import { getCwd } from 'src/utils/cwd.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import { HighlightedCode } from './HighlightedCode.js' import { MessageResponse } from './MessageResponse.js' diff --git a/src/components/OffscreenFreeze.tsx b/src/components/OffscreenFreeze.tsx index 8a94b5bd9..51595bb6c 100644 --- a/src/components/OffscreenFreeze.tsx +++ b/src/components/OffscreenFreeze.tsx @@ -1,5 +1,6 @@ import React, { useContext, useRef } from 'react' -import { useTerminalViewport, Box } from '@anthropic/ink' +import { useTerminalViewport } from '../ink/hooks/use-terminal-viewport.js' +import { Box } from '../ink.js' import { InVirtualListContext } from './messageActions.js' type Props = { diff --git a/src/components/Onboarding.tsx b/src/components/Onboarding.tsx index 51dc04306..80083ff91 100644 --- a/src/components/Onboarding.tsx +++ b/src/components/Onboarding.tsx @@ -8,7 +8,7 @@ import { shouldOfferTerminalSetup, } from '../commands/terminalSetup/terminalSetup.js' import { useExitOnCtrlCDWithKeybindings } from '../hooks/useExitOnCtrlCDWithKeybindings.js' -import { Box, Link, Newline, Text, useTheme } from '@anthropic/ink' +import { Box, Link, Newline, Text, useTheme } from '../ink.js' import { useKeybindings } from '../keybindings/useKeybinding.js' import { isAnthropicAuthEnabled } from '../utils/auth.js' import { normalizeApiKeyForConfig } from '../utils/authPortable.js' diff --git a/src/components/OutputStylePicker.tsx b/src/components/OutputStylePicker.tsx index 5717a26e6..4ff039a82 100644 --- a/src/components/OutputStylePicker.tsx +++ b/src/components/OutputStylePicker.tsx @@ -5,11 +5,12 @@ import { OUTPUT_STYLE_CONFIG, type OutputStyleConfig, } from '../constants/outputStyles.js' -import { Box, Text, Dialog } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import type { OutputStyle } from '../utils/config.js' import { getCwd } from '../utils/cwd.js' import type { OptionWithDescription } from './CustomSelect/select.js' import { Select } from './CustomSelect/select.js' +import { Dialog } from './design-system/Dialog.js' const DEFAULT_OUTPUT_STYLE_LABEL = 'Default' const DEFAULT_OUTPUT_STYLE_DESCRIPTION = diff --git a/src/components/PackageManagerAutoUpdater.tsx b/src/components/PackageManagerAutoUpdater.tsx index ffbb9e632..e97d32b12 100644 --- a/src/components/PackageManagerAutoUpdater.tsx +++ b/src/components/PackageManagerAutoUpdater.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import { useState } from 'react' import { useInterval } from 'usehooks-ts' -import { Text } from '@anthropic/ink' +import { Text } from '../ink.js' import { type AutoUpdaterResult, getLatestVersionFromGcs, diff --git a/src/components/Passes/Passes.tsx b/src/components/Passes/Passes.tsx index 94b6b2d52..69388618a 100644 --- a/src/components/Passes/Passes.tsx +++ b/src/components/Passes/Passes.tsx @@ -3,9 +3,9 @@ import { useCallback, useEffect, useState } from 'react' import type { CommandResultDisplay } from '../../commands.js' import { TEARDROP_ASTERISK } from '../../constants/figures.js' import { useExitOnCtrlCDWithKeybindings } from '../../hooks/useExitOnCtrlCDWithKeybindings.js' -import { setClipboard } from '@anthropic/ink' +import { setClipboard } from '../../ink/termio/osc.js' // eslint-disable-next-line custom-rules/prefer-use-keybindings -- enter to copy link -import { Box, Link, Text, useInput } from '@anthropic/ink' +import { Box, Link, Text, useInput } from '../../ink.js' import { useKeybinding } from '../../keybindings/useKeybinding.js' import { logEvent } from '../../services/analytics/index.js' import { @@ -19,7 +19,7 @@ import type { } from '../../services/oauth/types.js' import { count } from '../../utils/array.js' import { logError } from '../../utils/log.js' -import { Pane } from '@anthropic/ink' +import { Pane } from '../design-system/Pane.js' type PassStatus = { passNumber: number diff --git a/src/components/PrBadge.tsx b/src/components/PrBadge.tsx index e2f7a2a7b..bb0aef9e7 100644 --- a/src/components/PrBadge.tsx +++ b/src/components/PrBadge.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { Link, Text } from '@anthropic/ink' +import { Link, Text } from '../ink.js' import type { PrReviewState } from '../utils/ghPrStatus.js' type Props = { diff --git a/src/components/PressEnterToContinue.tsx b/src/components/PressEnterToContinue.tsx index 49b398b12..662c7af85 100644 --- a/src/components/PressEnterToContinue.tsx +++ b/src/components/PressEnterToContinue.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { Text } from '@anthropic/ink' +import { Text } from '../ink.js' export function PressEnterToContinue(): React.ReactNode { return ( diff --git a/src/components/PromptInput/HistorySearchInput.tsx b/src/components/PromptInput/HistorySearchInput.tsx index b8eaf8714..22830119d 100644 --- a/src/components/PromptInput/HistorySearchInput.tsx +++ b/src/components/PromptInput/HistorySearchInput.tsx @@ -1,5 +1,6 @@ import * as React from 'react' -import { Box, Text, stringWidth } from '@anthropic/ink' +import { stringWidth } from '../../ink/stringWidth.js' +import { Box, Text } from '../../ink.js' import TextInput from '../TextInput.js' type Props = { diff --git a/src/components/PromptInput/IssueFlagBanner.tsx b/src/components/PromptInput/IssueFlagBanner.tsx index 39cb491b9..723678eaf 100644 --- a/src/components/PromptInput/IssueFlagBanner.tsx +++ b/src/components/PromptInput/IssueFlagBanner.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import { FLAG_ICON } from '../../constants/figures.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' /** * ANT-ONLY: Banner shown in the transcript that prompts users to report diff --git a/src/components/PromptInput/Notifications.tsx b/src/components/PromptInput/Notifications.tsx index fb905efda..d89d596b3 100644 --- a/src/components/PromptInput/Notifications.tsx +++ b/src/components/PromptInput/Notifications.tsx @@ -13,7 +13,7 @@ import { useIdeConnectionStatus } from '../../hooks/useIdeConnectionStatus.js' import type { IDESelection } from '../../hooks/useIdeSelection.js' import { useMainLoopModel } from '../../hooks/useMainLoopModel.js' import { useVoiceEnabled } from '../../hooks/useVoiceEnabled.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { useClaudeAiLimits } from '../../services/claudeAiLimitsHook.js' import { calculateTokenWarningState } from '../../services/compact/autoCompact.js' import type { MCPServerConnection } from '../../services/mcp/types.js' diff --git a/src/components/PromptInput/PromptInput.tsx b/src/components/PromptInput/PromptInput.tsx index e62da28de..a678d41f6 100644 --- a/src/components/PromptInput/PromptInput.tsx +++ b/src/components/PromptInput/PromptInput.tsx @@ -63,7 +63,9 @@ import { useMainLoopModel } from '../../hooks/useMainLoopModel.js' import { usePromptSuggestion } from '../../hooks/usePromptSuggestion.js' import { useTerminalSize } from '../../hooks/useTerminalSize.js' import { useTypeahead } from '../../hooks/useTypeahead.js' -import { Box, type BorderTextOptions, type ClickEvent, type Key, stringWidth, Text, useInput } from '@anthropic/ink' +import type { BorderTextOptions } from '../../ink/render-border.js' +import { stringWidth } from '../../ink/stringWidth.js' +import { Box, type ClickEvent, type Key, Text, useInput } from '../../ink.js' import { useOptionalKeybindingContext } from '../../keybindings/KeybindingContext.js' import { getShortcutDisplay } from '../../keybindings/shortcutFormat.js' import { diff --git a/src/components/PromptInput/PromptInputFooter.tsx b/src/components/PromptInput/PromptInputFooter.tsx index 46b3981f5..652bdf3f0 100644 --- a/src/components/PromptInput/PromptInputFooter.tsx +++ b/src/components/PromptInput/PromptInputFooter.tsx @@ -8,7 +8,7 @@ import type { VerificationStatus } from '../../hooks/useApiKeyVerification.js' import type { IDESelection } from '../../hooks/useIdeSelection.js' import { useSettings } from '../../hooks/useSettings.js' import { useTerminalSize } from '../../hooks/useTerminalSize.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import type { MCPServerConnection } from '../../services/mcp/types.js' import { useAppState } from '../../state/AppState.js' import type { ToolPermissionContext } from '../../Tool.js' diff --git a/src/components/PromptInput/PromptInputFooterLeftSide.tsx b/src/components/PromptInput/PromptInputFooterLeftSide.tsx index 8130c8ef1..fc1be8124 100644 --- a/src/components/PromptInput/PromptInputFooterLeftSide.tsx +++ b/src/components/PromptInput/PromptInputFooterLeftSide.tsx @@ -6,7 +6,7 @@ const coordinatorModule = feature('COORDINATOR_MODE') ? (require('../../coordinator/coordinatorMode.js') as typeof import('../../coordinator/coordinatorMode.js')) : undefined /* eslint-enable @typescript-eslint/no-require-imports */ -import { Box, Text, Link } from '@anthropic/ink' +import { Box, Text, Link } from '../../ink.js' import * as React from 'react' import figures from 'figures' import { @@ -39,7 +39,8 @@ import { useAppState, useAppStateStore } from 'src/state/AppState.js' import { getIsRemoteMode } from '../../bootstrap/state.js' import HistorySearchInput from './HistorySearchInput.js' import { usePrStatus } from '../../hooks/usePrStatus.js' -import { Byline, KeyboardShortcutHint } from '@anthropic/ink' +import { KeyboardShortcutHint } from '../design-system/KeyboardShortcutHint.js' +import { Byline } from '../design-system/Byline.js' import { useTerminalSize } from '../../hooks/useTerminalSize.js' import { useTasksV2 } from '../../hooks/useTasksV2.js' import { formatDuration } from '../../utils/format.js' @@ -47,7 +48,8 @@ import { VoiceWarmupHint } from './VoiceIndicator.js' import { useVoiceEnabled } from '../../hooks/useVoiceEnabled.js' import { useVoiceState } from '../../context/voice.js' import { isFullscreenEnvEnabled } from '../../utils/fullscreen.js' -import { isXtermJs, useHasSelection, useSelection } from '@anthropic/ink' +import { isXtermJs } from '../../ink/terminal.js' +import { useHasSelection, useSelection } from '../../ink/hooks/use-selection.js' import { getGlobalConfig, saveGlobalConfig } from '../../utils/config.js' import { getPlatform } from '../../utils/platform.js' import { PrBadge } from '../PrBadge.js' diff --git a/src/components/PromptInput/PromptInputFooterSuggestions.tsx b/src/components/PromptInput/PromptInputFooterSuggestions.tsx index 689123581..7a5d70260 100644 --- a/src/components/PromptInput/PromptInputFooterSuggestions.tsx +++ b/src/components/PromptInput/PromptInputFooterSuggestions.tsx @@ -1,7 +1,8 @@ import * as React from 'react' import { memo, type ReactNode } from 'react' import { useTerminalSize } from '../../hooks/useTerminalSize.js' -import { Box, Text, stringWidth } from '@anthropic/ink' +import { stringWidth } from '../../ink/stringWidth.js' +import { Box, Text } from '../../ink.js' import { truncatePathMiddle, truncateToWidth } from '../../utils/format.js' import type { Theme } from '../../utils/theme.js' diff --git a/src/components/PromptInput/PromptInputHelpMenu.tsx b/src/components/PromptInput/PromptInputHelpMenu.tsx index 88aa5dfc2..5f15327d2 100644 --- a/src/components/PromptInput/PromptInputHelpMenu.tsx +++ b/src/components/PromptInput/PromptInputHelpMenu.tsx @@ -1,6 +1,6 @@ import { feature } from 'bun:bundle' import * as React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from 'src/ink.js' import { getPlatform } from 'src/utils/platform.js' import { isKeybindingCustomizationEnabled } from '../../keybindings/loadUserBindings.js' import { useShortcutDisplay } from '../../keybindings/useShortcutDisplay.js' diff --git a/src/components/PromptInput/PromptInputModeIndicator.tsx b/src/components/PromptInput/PromptInputModeIndicator.tsx index e493dba2d..4aa66bf7b 100644 --- a/src/components/PromptInput/PromptInputModeIndicator.tsx +++ b/src/components/PromptInput/PromptInputModeIndicator.tsx @@ -1,6 +1,6 @@ import figures from 'figures' import * as React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from 'src/ink.js' import { AGENT_COLOR_TO_THEME_COLOR, AGENT_COLORS, diff --git a/src/components/PromptInput/PromptInputQueuedCommands.tsx b/src/components/PromptInput/PromptInputQueuedCommands.tsx index e96ba3532..c4637b803 100644 --- a/src/components/PromptInput/PromptInputQueuedCommands.tsx +++ b/src/components/PromptInput/PromptInputQueuedCommands.tsx @@ -1,7 +1,7 @@ import { feature } from 'bun:bundle' import * as React from 'react' import { useMemo } from 'react' -import { Box } from '@anthropic/ink' +import { Box } from 'src/ink.js' import { useAppState } from 'src/state/AppState.js' import { STATUS_TAG, diff --git a/src/components/PromptInput/PromptInputStashNotice.tsx b/src/components/PromptInput/PromptInputStashNotice.tsx index c33f43190..8a44e8607 100644 --- a/src/components/PromptInput/PromptInputStashNotice.tsx +++ b/src/components/PromptInput/PromptInputStashNotice.tsx @@ -1,6 +1,6 @@ import figures from 'figures' import * as React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from 'src/ink.js' type Props = { hasStash: boolean diff --git a/src/components/PromptInput/SandboxPromptFooterHint.tsx b/src/components/PromptInput/SandboxPromptFooterHint.tsx index e470509d4..1324a9832 100644 --- a/src/components/PromptInput/SandboxPromptFooterHint.tsx +++ b/src/components/PromptInput/SandboxPromptFooterHint.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import { type ReactNode, useEffect, useRef, useState } from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { useShortcutDisplay } from '../../keybindings/useShortcutDisplay.js' import { SandboxManager } from '../../utils/sandbox/sandbox-adapter.js' diff --git a/src/components/PromptInput/ShimmeredInput.tsx b/src/components/PromptInput/ShimmeredInput.tsx index a14afbc9b..11da7ad76 100644 --- a/src/components/PromptInput/ShimmeredInput.tsx +++ b/src/components/PromptInput/ShimmeredInput.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { Ansi, Box, Text, useAnimationFrame } from '@anthropic/ink' +import { Ansi, Box, Text, useAnimationFrame } from '../../ink.js' import { segmentTextByHighlights, type TextHighlight, diff --git a/src/components/PromptInput/VoiceIndicator.tsx b/src/components/PromptInput/VoiceIndicator.tsx index c6fe0b07f..6dc73baf2 100644 --- a/src/components/PromptInput/VoiceIndicator.tsx +++ b/src/components/PromptInput/VoiceIndicator.tsx @@ -1,7 +1,7 @@ import { feature } from 'bun:bundle' import * as React from 'react' import { useSettings } from '../../hooks/useSettings.js' -import { Box, Text, useAnimationFrame } from '@anthropic/ink' +import { Box, Text, useAnimationFrame } from '../../ink.js' import { interpolateColor, toRGBColor } from '../Spinner/utils.js' type Props = { diff --git a/src/components/PromptInput/utils.ts b/src/components/PromptInput/utils.ts index 8b59fdf74..eb5cc8128 100644 --- a/src/components/PromptInput/utils.ts +++ b/src/components/PromptInput/utils.ts @@ -2,7 +2,7 @@ import { hasUsedBackslashReturn, isShiftEnterKeyBindingInstalled, } from '../../commands/terminalSetup/terminalSetup.js' -import type { Key } from '@anthropic/ink' +import type { Key } from '../../ink.js' import { getGlobalConfig } from '../../utils/config.js' import { env } from '../../utils/env.js' /** diff --git a/src/components/QuickOpenDialog.tsx b/src/components/QuickOpenDialog.tsx index 6945fb682..37b7bb7e1 100644 --- a/src/components/QuickOpenDialog.tsx +++ b/src/components/QuickOpenDialog.tsx @@ -4,14 +4,15 @@ import { useEffect, useRef, useState } from 'react' import { useRegisterOverlay } from '../context/overlayContext.js' import { generateFileSuggestions } from '../hooks/fileSuggestions.js' import { useTerminalSize } from '../hooks/useTerminalSize.js' -import { Text } from '@anthropic/ink' +import { Text } from '../ink.js' import { logEvent } from '../services/analytics/index.js' import { getCwd } from '../utils/cwd.js' import { openFileInExternalEditor } from '../utils/editor.js' import { truncatePathMiddle, truncateToWidth } from '../utils/format.js' import { highlightMatch } from '../utils/highlightMatch.js' import { readFileInRange } from '../utils/readFileInRange.js' -import { FuzzyPicker, LoadingState } from '@anthropic/ink' +import { FuzzyPicker } from './design-system/FuzzyPicker.js' +import { LoadingState } from './design-system/LoadingState.js' type Props = { onDone: () => void diff --git a/src/components/RemoteCallout.tsx b/src/components/RemoteCallout.tsx index 7d9dff991..d6b0af589 100644 --- a/src/components/RemoteCallout.tsx +++ b/src/components/RemoteCallout.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useEffect, useRef } from 'react' import { isBridgeEnabled } from '../bridge/bridgeEnabled.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import { getClaudeAIOAuthTokens } from '../utils/auth.js' import { getGlobalConfig, saveGlobalConfig } from '../utils/config.js' import type { OptionWithDescription } from './CustomSelect/select.js' diff --git a/src/components/RemoteEnvironmentDialog.tsx b/src/components/RemoteEnvironmentDialog.tsx index 436b59583..b0f47a60c 100644 --- a/src/components/RemoteEnvironmentDialog.tsx +++ b/src/components/RemoteEnvironmentDialog.tsx @@ -2,7 +2,7 @@ import chalk from 'chalk' import figures from 'figures' import * as React from 'react' import { useEffect, useState } from 'react' -import { Text } from '@anthropic/ink' +import { Text } from '../ink.js' import { useKeybinding } from '../keybindings/useKeybinding.js' import { toError } from '../utils/errors.js' import { logError } from '../utils/log.js' @@ -15,7 +15,10 @@ import { getEnvironmentSelectionInfo } from '../utils/teleport/environmentSelect import type { EnvironmentResource } from '../utils/teleport/environments.js' import { ConfigurableShortcutHint } from './ConfigurableShortcutHint.js' import { Select } from './CustomSelect/select.js' -import { Byline, Dialog, KeyboardShortcutHint, LoadingState } from '@anthropic/ink' +import { Byline } from './design-system/Byline.js' +import { Dialog } from './design-system/Dialog.js' +import { KeyboardShortcutHint } from './design-system/KeyboardShortcutHint.js' +import { LoadingState } from './design-system/LoadingState.js' const DIALOG_TITLE = 'Select Remote Environment' const SETUP_HINT = `Configure environments at: https://claude.ai/code` diff --git a/src/components/ResumeTask.tsx b/src/components/ResumeTask.tsx index c81ed0291..8b657ab0d 100644 --- a/src/components/ResumeTask.tsx +++ b/src/components/ResumeTask.tsx @@ -5,7 +5,7 @@ import { fetchCodeSessionsFromSessionsAPI, } from 'src/utils/teleport/api.js' // eslint-disable-next-line custom-rules/prefer-use-keybindings -- raw j/k/arrow list navigation -import { Box, Text, useInput } from '@anthropic/ink' +import { Box, Text, useInput } from '../ink.js' import { useKeybinding } from '../keybindings/useKeybinding.js' import { useShortcutDisplay } from '../keybindings/useShortcutDisplay.js' import { logForDebugging } from '../utils/debug.js' @@ -13,7 +13,8 @@ import { detectCurrentRepository } from '../utils/detectRepository.js' import { formatRelativeTime } from '../utils/format.js' import { ConfigurableShortcutHint } from './ConfigurableShortcutHint.js' import { Select } from './CustomSelect/index.js' -import { Byline, KeyboardShortcutHint } from '@anthropic/ink' +import { Byline } from './design-system/Byline.js' +import { KeyboardShortcutHint } from './design-system/KeyboardShortcutHint.js' import { Spinner } from './Spinner.js' import { TeleportError } from './TeleportError.js' diff --git a/src/components/SandboxViolationExpandedView.tsx b/src/components/SandboxViolationExpandedView.tsx index 93a448c87..4b8bbbd7a 100644 --- a/src/components/SandboxViolationExpandedView.tsx +++ b/src/components/SandboxViolationExpandedView.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import { type ReactNode, useEffect, useState } from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import type { SandboxViolationEvent } from '../utils/sandbox/sandbox-adapter.js' import { SandboxManager } from '../utils/sandbox/sandbox-adapter.js' diff --git a/src/components/ScrollKeybindingHandler.tsx b/src/components/ScrollKeybindingHandler.tsx index cdaac3256..e51787f9f 100644 --- a/src/components/ScrollKeybindingHandler.tsx +++ b/src/components/ScrollKeybindingHandler.tsx @@ -4,8 +4,13 @@ import { useCopyOnSelect, useSelectionBgColor, } from '../hooks/useCopyOnSelect.js' -import type { ScrollBoxHandle, FocusMove, SelectionState } from '@anthropic/ink' -import { useSelection, type Key, useInput, isXtermJs, getClipboardPath } from '@anthropic/ink' +import type { ScrollBoxHandle } from '../ink/components/ScrollBox.js' +import { useSelection } from '../ink/hooks/use-selection.js' +import type { FocusMove, SelectionState } from '../ink/selection.js' +import { isXtermJs } from '../ink/terminal.js' +import { getClipboardPath } from '../ink/termio/osc.js' +// eslint-disable-next-line custom-rules/prefer-use-keybindings -- Esc needs conditional propagation based on selection state +import { type Key, useInput } from '../ink.js' import { useKeybindings } from '../keybindings/useKeybinding.js' import { logForDebugging } from '../utils/debug.js' diff --git a/src/components/SearchBox.tsx b/src/components/SearchBox.tsx index 9fd4a7fb4..d35d67edd 100644 --- a/src/components/SearchBox.tsx +++ b/src/components/SearchBox.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../ink.js' type Props = { query: string diff --git a/src/components/SessionBackgroundHint.tsx b/src/components/SessionBackgroundHint.tsx index aa336afb0..a7f5e8f59 100644 --- a/src/components/SessionBackgroundHint.tsx +++ b/src/components/SessionBackgroundHint.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import { useCallback, useState } from 'react' import { useDoublePress } from '../hooks/useDoublePress.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import { useKeybinding } from '../keybindings/useKeybinding.js' import { useShortcutDisplay } from '../keybindings/useShortcutDisplay.js' import { @@ -16,7 +16,7 @@ import { import { getGlobalConfig, saveGlobalConfig } from '../utils/config.js' import { env } from '../utils/env.js' import { isEnvTruthy } from '../utils/envUtils.js' -import { KeyboardShortcutHint } from '@anthropic/ink' +import { KeyboardShortcutHint } from './design-system/KeyboardShortcutHint.js' type Props = { onBackgroundSession: () => void diff --git a/src/components/SessionPreview.tsx b/src/components/SessionPreview.tsx index 2e0f74b2d..2d0e10a97 100644 --- a/src/components/SessionPreview.tsx +++ b/src/components/SessionPreview.tsx @@ -1,6 +1,6 @@ import type { UUID } from 'crypto' import React, { useCallback } from 'react' -import { Box, Text, Byline, KeyboardShortcutHint, LoadingState } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import { useKeybinding } from '../keybindings/useKeybinding.js' import { getAllBaseTools } from '../tools.js' import type { LogOption } from '../types/logs.js' @@ -11,6 +11,9 @@ import { loadFullLog, } from '../utils/sessionStorage.js' import { ConfigurableShortcutHint } from './ConfigurableShortcutHint.js' +import { Byline } from './design-system/Byline.js' +import { KeyboardShortcutHint } from './design-system/KeyboardShortcutHint.js' +import { LoadingState } from './design-system/LoadingState.js' import { Messages } from './Messages.js' type Props = { diff --git a/src/components/Settings/Config.tsx b/src/components/Settings/Config.tsx index 2a4bc73c0..09f832f0c 100644 --- a/src/components/Settings/Config.tsx +++ b/src/components/Settings/Config.tsx @@ -1,6 +1,13 @@ // biome-ignore-all assist/source/organizeImports: ANT-ONLY import markers must not be reordered import { feature } from 'bun:bundle' -import { type KeyboardEvent, Box, Text, useTheme, useThemeSetting, useTerminalFocus } from '@anthropic/ink' +import { + Box, + Text, + useTheme, + useThemeSetting, + useTerminalFocus, +} from '../../ink.js' +import type { KeyboardEvent } from '../../ink/events/keyboard-event.js' import * as React from 'react' import { useState, useCallback } from 'react' import { @@ -60,7 +67,7 @@ import { ChannelDowngradeDialog, type ChannelDowngradeChoice, } from '../ChannelDowngradeDialog.js' -import { Dialog } from '@anthropic/ink' +import { Dialog } from '../design-system/Dialog.js' import { Select } from '../CustomSelect/index.js' import { OutputStylePicker } from '../OutputStylePicker.js' import { LanguagePicker } from '../LanguagePicker.js' @@ -69,9 +76,10 @@ import { getMemoryFiles, hasExternalClaudeMdIncludes, } from 'src/utils/claudemd.js' -import { Byline, KeyboardShortcutHint } from '@anthropic/ink' -import { useTabHeaderFocus } from '../design-system/Tabs.js' +import { KeyboardShortcutHint } from '../design-system/KeyboardShortcutHint.js' import { ConfigurableShortcutHint } from '../ConfigurableShortcutHint.js' +import { Byline } from '../design-system/Byline.js' +import { useTabHeaderFocus } from '../design-system/Tabs.js' import { useIsInsideModal } from '../../context/modalContext.js' import { SearchBox } from '../SearchBox.js' import { diff --git a/src/components/Settings/Settings.tsx b/src/components/Settings/Settings.tsx index b6a5516dd..d54f6e758 100644 --- a/src/components/Settings/Settings.tsx +++ b/src/components/Settings/Settings.tsx @@ -8,8 +8,8 @@ import { useIsInsideModal, useModalOrTerminalSize, } from '../../context/modalContext.js' -import { Pane, Tabs } from '@anthropic/ink' -import { Tab } from '../design-system/Tabs.js' +import { Pane } from '../design-system/Pane.js' +import { Tabs, Tab } from '../design-system/Tabs.js' import { Status, buildDiagnostics } from './Status.js' import { Config } from './Config.js' import { Usage } from './Usage.js' diff --git a/src/components/Settings/Status.tsx b/src/components/Settings/Status.tsx index acadfcc06..1cd4aac14 100644 --- a/src/components/Settings/Status.tsx +++ b/src/components/Settings/Status.tsx @@ -4,7 +4,7 @@ import { Suspense, use } from 'react' import { getSessionId } from '../../bootstrap/state.js' import type { LocalJSXCommandContext } from '../../commands.js' import { useIsInsideModal } from '../../context/modalContext.js' -import { Box, Text, useTheme } from '@anthropic/ink' +import { Box, Text, useTheme } from '../../ink.js' import { type AppState, useAppState } from '../../state/AppState.js' import { getCwd } from '../../utils/cwd.js' import { getCurrentSessionTitle } from '../../utils/sessionStorage.js' diff --git a/src/components/Settings/Usage.tsx b/src/components/Settings/Usage.tsx index 403e050eb..d52d19577 100644 --- a/src/components/Settings/Usage.tsx +++ b/src/components/Settings/Usage.tsx @@ -4,7 +4,7 @@ import { extraUsage as extraUsageCommand } from 'src/commands/extra-usage/index. import { formatCost } from 'src/cost-tracker.js' import { getSubscriptionType } from 'src/utils/auth.js' import { useTerminalSize } from '../../hooks/useTerminalSize.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { useKeybinding } from '../../keybindings/useKeybinding.js' import { type ExtraUsage, @@ -16,7 +16,8 @@ import { formatResetText } from '../../utils/format.js' import { logError } from '../../utils/log.js' import { jsonStringify } from '../../utils/slowOperations.js' import { ConfigurableShortcutHint } from '../ConfigurableShortcutHint.js' -import { Byline, ProgressBar } from '@anthropic/ink' +import { Byline } from '../design-system/Byline.js' +import { ProgressBar } from '../design-system/ProgressBar.js' import { isEligibleForOverageCreditGrant, OverageCreditUpsell, diff --git a/src/components/ShowInIDEPrompt.tsx b/src/components/ShowInIDEPrompt.tsx index e522cc6ae..e5ff331a3 100644 --- a/src/components/ShowInIDEPrompt.tsx +++ b/src/components/ShowInIDEPrompt.tsx @@ -1,9 +1,10 @@ import { basename, relative } from 'path' import React from 'react' -import { Box, Text, Pane } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import { getCwd } from '../utils/cwd.js' import { isSupportedVSCodeTerminal } from '../utils/ide.js' import { Select } from './CustomSelect/index.js' +import { Pane } from './design-system/Pane.js' import type { PermissionOption, PermissionOptionWithLabel, diff --git a/src/components/SkillImprovementSurvey.tsx b/src/components/SkillImprovementSurvey.tsx index ad7b09147..f42e27f23 100644 --- a/src/components/SkillImprovementSurvey.tsx +++ b/src/components/SkillImprovementSurvey.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useRef } from 'react' import { BLACK_CIRCLE, BULLET_OPERATOR } from '../constants/figures.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import type { SkillUpdate } from '../utils/hooks/skillImprovement.js' import { normalizeFullWidthDigits } from '../utils/stringUtils.js' import { isValidResponseInput } from './FeedbackSurvey/FeedbackSurveyView.js' diff --git a/src/components/Spinner.tsx b/src/components/Spinner.tsx index 089bf4d8e..dc8692215 100644 --- a/src/components/Spinner.tsx +++ b/src/components/Spinner.tsx @@ -1,5 +1,5 @@ // biome-ignore-all assist/source/organizeImports: ANT-ONLY import markers must not be reordered -import { Box, Text, stringWidth } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import * as React from 'react' import { useEffect, useMemo, useRef, useState } from 'react' import { @@ -27,6 +27,7 @@ import { useTasksV2 } from '../hooks/useTasksV2.js' import type { Task } from '../utils/tasks.js' import { useAppState } from '../state/AppState.js' import { useTerminalSize } from '../hooks/useTerminalSize.js' +import { stringWidth } from '../ink/stringWidth.js' import { getDefaultCharacters, type SpinnerMode } from './Spinner/index.js' import { SpinnerAnimationRow } from './Spinner/SpinnerAnimationRow.js' import { useSettings } from '../hooks/useSettings.js' @@ -44,7 +45,7 @@ import { } from '../bootstrap/state.js' import { TeammateSpinnerTree } from './Spinner/TeammateSpinnerTree.js' -import { useAnimationFrame } from '@anthropic/ink' +import { useAnimationFrame } from '../ink.js' import { getGlobalConfig } from '../utils/config.js' export type { SpinnerMode } from './Spinner/index.js' diff --git a/src/components/Spinner/FlashingChar.tsx b/src/components/Spinner/FlashingChar.tsx index bfb190999..7f67a47ad 100644 --- a/src/components/Spinner/FlashingChar.tsx +++ b/src/components/Spinner/FlashingChar.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { Text, useTheme } from '@anthropic/ink' +import { Text, useTheme } from '../../ink.js' import { getTheme, type Theme } from '../../utils/theme.js' import { interpolateColor, parseRGB, toRGBColor } from './utils.js' diff --git a/src/components/Spinner/GlimmerMessage.tsx b/src/components/Spinner/GlimmerMessage.tsx index 9f8ee9265..3e488f9a1 100644 --- a/src/components/Spinner/GlimmerMessage.tsx +++ b/src/components/Spinner/GlimmerMessage.tsx @@ -1,5 +1,6 @@ import * as React from 'react' -import { Text, stringWidth, useTheme } from '@anthropic/ink' +import { stringWidth } from '../../ink/stringWidth.js' +import { Text, useTheme } from '../../ink.js' import { getGraphemeSegmenter } from '../../utils/intl.js' import { getTheme, type Theme } from '../../utils/theme.js' import type { SpinnerMode } from './types.js' diff --git a/src/components/Spinner/ShimmerChar.tsx b/src/components/Spinner/ShimmerChar.tsx index 0daa44c67..038ffb33d 100644 --- a/src/components/Spinner/ShimmerChar.tsx +++ b/src/components/Spinner/ShimmerChar.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { Text } from '@anthropic/ink' +import { Text } from '../../ink.js' import type { Theme } from '../../utils/theme.js' type Props = { diff --git a/src/components/Spinner/SpinnerAnimationRow.tsx b/src/components/Spinner/SpinnerAnimationRow.tsx index 57b8358f2..93b2fc64a 100644 --- a/src/components/Spinner/SpinnerAnimationRow.tsx +++ b/src/components/Spinner/SpinnerAnimationRow.tsx @@ -1,13 +1,13 @@ import figures from 'figures' import * as React from 'react' import { useMemo, useRef } from 'react' -import { Box, Text, useAnimationFrame, stringWidth, Byline } from '@anthropic/ink' -import { toInkColor } from '../../utils/ink.js' +import { stringWidth } from '../../ink/stringWidth.js' +import { Box, Text, useAnimationFrame } from '../../ink.js' import type { InProcessTeammateTaskState } from '../../tasks/InProcessTeammateTask/types.js' import { formatDuration, formatNumber } from '../../utils/format.js' - +import { toInkColor } from '../../utils/ink.js' import type { Theme } from '../../utils/theme.js' - +import { Byline } from '../design-system/Byline.js' import { GlimmerMessage } from './GlimmerMessage.js' import { SpinnerGlyph } from './SpinnerGlyph.js' import type { SpinnerMode } from './types.js' @@ -236,6 +236,7 @@ export function SpinnerAnimationRow({ totalTokens > 0 && availableSpace > usedAfterTimer + tokensWidth + const thinkingOnly = showThinking && thinkingStatus === 'thinking' && diff --git a/src/components/Spinner/SpinnerGlyph.tsx b/src/components/Spinner/SpinnerGlyph.tsx index d7db456db..242d05971 100644 --- a/src/components/Spinner/SpinnerGlyph.tsx +++ b/src/components/Spinner/SpinnerGlyph.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { Box, Text, useTheme } from '@anthropic/ink' +import { Box, Text, useTheme } from '../../ink.js' import { getTheme, type Theme } from '../../utils/theme.js' import { getDefaultCharacters, diff --git a/src/components/Spinner/TeammateSpinnerLine.tsx b/src/components/Spinner/TeammateSpinnerLine.tsx index 736623ce4..ee6807f76 100644 --- a/src/components/Spinner/TeammateSpinnerLine.tsx +++ b/src/components/Spinner/TeammateSpinnerLine.tsx @@ -6,8 +6,8 @@ import { getSpinnerVerbs } from '../../constants/spinnerVerbs.js' import { TURN_COMPLETION_VERBS } from '../../constants/turnCompletionVerbs.js' import { useElapsedTime } from '../../hooks/useElapsedTime.js' import { useTerminalSize } from '../../hooks/useTerminalSize.js' -import { Box, Text, stringWidth } from '@anthropic/ink' -import { toInkColor } from '../../utils/ink.js' +import { stringWidth } from '../../ink/stringWidth.js' +import { Box, Text } from '../../ink.js' import type { InProcessTeammateTaskState } from '../../tasks/InProcessTeammateTask/types.js' import { summarizeRecentActivities } from '../../utils/collapseReadSearch.js' import { @@ -15,7 +15,7 @@ import { formatNumber, truncateToWidth, } from '../../utils/format.js' - +import { toInkColor } from '../../utils/ink.js' import { TEAMMATE_SELECT_HINT } from './teammateSelectHint.js' type Props = { diff --git a/src/components/Spinner/TeammateSpinnerTree.tsx b/src/components/Spinner/TeammateSpinnerTree.tsx index 8c297ed57..331126f71 100644 --- a/src/components/Spinner/TeammateSpinnerTree.tsx +++ b/src/components/Spinner/TeammateSpinnerTree.tsx @@ -1,6 +1,6 @@ import figures from 'figures' import * as React from 'react' -import { Box, Text, type TextProps } from '@anthropic/ink' +import { Box, Text, type TextProps } from '../../ink.js' import { useAppState } from '../../state/AppState.js' import { getRunningTeammatesSorted } from '../../tasks/InProcessTeammateTask/InProcessTeammateTask.js' import { formatNumber } from '../../utils/format.js' diff --git a/src/components/Spinner/useShimmerAnimation.ts b/src/components/Spinner/useShimmerAnimation.ts index d71d11f71..d1d4ea947 100644 --- a/src/components/Spinner/useShimmerAnimation.ts +++ b/src/components/Spinner/useShimmerAnimation.ts @@ -1,5 +1,6 @@ import { useMemo } from 'react' -import { type DOMElement, useAnimationFrame, stringWidth } from '@anthropic/ink' +import { stringWidth } from '../../ink/stringWidth.js' +import { type DOMElement, useAnimationFrame } from '../../ink.js' import type { SpinnerMode } from './types.js' export function useShimmerAnimation( diff --git a/src/components/Spinner/utils.ts b/src/components/Spinner/utils.ts index 0b90a7a59..7c0c54d00 100644 --- a/src/components/Spinner/utils.ts +++ b/src/components/Spinner/utils.ts @@ -1,4 +1,4 @@ -import type { RGBColor as RGBColorString } from '@anthropic/ink' +import type { RGBColor as RGBColorString } from '../../ink/styles.js' import type { RGBColor as RGBColorType } from './types.js' export function getDefaultCharacters(): string[] { diff --git a/src/components/Stats.tsx b/src/components/Stats.tsx index 6d311259a..b2dc29168 100644 --- a/src/components/Stats.tsx +++ b/src/components/Stats.tsx @@ -13,9 +13,11 @@ import React, { import stripAnsi from 'strip-ansi' import type { CommandResultDisplay } from '../commands.js' import { useTerminalSize } from '../hooks/useTerminalSize.js' +import { applyColor } from '../ink/colorize.js' +import { stringWidth as getStringWidth } from '../ink/stringWidth.js' +import type { Color } from '../ink/styles.js' // eslint-disable-next-line custom-rules/prefer-use-keybindings -- raw j/k/arrow stats navigation -import { Ansi, applyColor, Box, Text, useInput, stringWidth as getStringWidth, type Color, Pane, Tabs } from '@anthropic/ink' -import { Tab, useTabHeaderFocus } from './design-system/Tabs.js' +import { Ansi, Box, Text, useInput } from '../ink.js' import { useKeybinding } from '../keybindings/useKeybinding.js' import { getGlobalConfig } from '../utils/config.js' import { formatDuration, formatNumber } from '../utils/format.js' @@ -30,6 +32,8 @@ import { } from '../utils/stats.js' import { resolveThemeSetting } from '../utils/systemTheme.js' import { getTheme, themeColorToAnsi } from '../utils/theme.js' +import { Pane } from './design-system/Pane.js' +import { Tab, Tabs, useTabHeaderFocus } from './design-system/Tabs.js' import { Spinner } from './Spinner.js' function formatPeakDay(dateStr: string): string { diff --git a/src/components/StatusLine.tsx b/src/components/StatusLine.tsx index b2f2c7c91..ad90655f4 100644 --- a/src/components/StatusLine.tsx +++ b/src/components/StatusLine.tsx @@ -25,7 +25,7 @@ import { } from '../cost-tracker.js' import { useMainLoopModel } from '../hooks/useMainLoopModel.js' import { type ReadonlySettings, useSettings } from '../hooks/useSettings.js' -import { Ansi, Box, Text } from '@anthropic/ink' +import { Ansi, Box, Text } from '../ink.js' import { getRawUtilization } from '../services/claudeAiLimits.js' import type { Message } from '../types/message.js' import type { StatusLineCommandInput } from '../types/statusLine.js' diff --git a/src/components/StatusNotices.tsx b/src/components/StatusNotices.tsx index ae25c5d9f..a62df498e 100644 --- a/src/components/StatusNotices.tsx +++ b/src/components/StatusNotices.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import { use } from 'react' -import { Box } from '@anthropic/ink' +import { Box } from '../ink.js' import type { AgentDefinitionsResult } from '../tools/AgentTool/loadAgentsDir.js' import { getMemoryFiles } from '../utils/claudemd.js' import { getGlobalConfig } from '../utils/config.js' diff --git a/src/components/StructuredDiff.tsx b/src/components/StructuredDiff.tsx index 890977701..237dfea2b 100644 --- a/src/components/StructuredDiff.tsx +++ b/src/components/StructuredDiff.tsx @@ -2,7 +2,7 @@ import type { StructuredPatchHunk } from 'diff' import * as React from 'react' import { memo } from 'react' import { useSettings } from '../hooks/useSettings.js' -import { Box, NoSelect, RawAnsi, useTheme } from '@anthropic/ink' +import { Box, NoSelect, RawAnsi, useTheme } from '../ink.js' import { isFullscreenEnvEnabled } from '../utils/fullscreen.js' import sliceAnsi from '../utils/sliceAnsi.js' import { expectColorDiff } from './StructuredDiff/colorDiff.js' diff --git a/src/components/StructuredDiff/Fallback.tsx b/src/components/StructuredDiff/Fallback.tsx index 0331a0ed5..335391e0a 100644 --- a/src/components/StructuredDiff/Fallback.tsx +++ b/src/components/StructuredDiff/Fallback.tsx @@ -2,7 +2,8 @@ import { diffWordsWithSpace, type StructuredPatchHunk } from 'diff' import * as React from 'react' import { useMemo } from 'react' import type { ThemeName } from 'src/utils/theme.js' -import { Box, NoSelect, Text, stringWidth, useTheme, wrapText } from '@anthropic/ink' +import { stringWidth } from '../../ink/stringWidth.js' +import { Box, NoSelect, Text, useTheme, wrapText } from '../../ink.js' /* * StructuredDiffFallback Component: Word-Level Diff Highlighting Example diff --git a/src/components/StructuredDiffList.tsx b/src/components/StructuredDiffList.tsx index cfbedaecb..af0eeeb02 100644 --- a/src/components/StructuredDiffList.tsx +++ b/src/components/StructuredDiffList.tsx @@ -1,6 +1,6 @@ import type { StructuredPatchHunk } from 'diff' import * as React from 'react' -import { Box, NoSelect, Text } from '@anthropic/ink' +import { Box, NoSelect, Text } from '../ink.js' import { intersperse } from '../utils/array.js' import { StructuredDiff } from './StructuredDiff.js' diff --git a/src/components/TagTabs.tsx b/src/components/TagTabs.tsx index f974bdad4..786a1f81d 100644 --- a/src/components/TagTabs.tsx +++ b/src/components/TagTabs.tsx @@ -1,5 +1,6 @@ import React from 'react' -import { Box, Text, stringWidth } from '@anthropic/ink' +import { stringWidth } from '../ink/stringWidth.js' +import { Box, Text } from '../ink.js' import { truncateToWidth } from '../utils/format.js' // Constants for width calculations - derived from actual rendered strings diff --git a/src/components/TaskListV2.tsx b/src/components/TaskListV2.tsx index 6ab77ccbb..d9d4dfa04 100644 --- a/src/components/TaskListV2.tsx +++ b/src/components/TaskListV2.tsx @@ -1,7 +1,8 @@ import figures from 'figures' import * as React from 'react' import { useTerminalSize } from '../hooks/useTerminalSize.js' -import { Box, Text, stringWidth } from '@anthropic/ink' +import { stringWidth } from '../ink/stringWidth.js' +import { Box, Text } from '../ink.js' import { useAppState } from '../state/AppState.js' import { isInProcessTeammateTask } from '../tasks/InProcessTeammateTask/types.js' import { diff --git a/src/components/TeammateViewHeader.tsx b/src/components/TeammateViewHeader.tsx index 1613e7aca..01e64571d 100644 --- a/src/components/TeammateViewHeader.tsx +++ b/src/components/TeammateViewHeader.tsx @@ -1,9 +1,9 @@ import * as React from 'react' -import { Box, Text, KeyboardShortcutHint } from '@anthropic/ink' -import { toInkColor } from '../utils/ink.js' +import { Box, Text } from '../ink.js' import { useAppState } from '../state/AppState.js' import { getViewedTeammateTask } from '../state/selectors.js' - +import { toInkColor } from '../utils/ink.js' +import { KeyboardShortcutHint } from './design-system/KeyboardShortcutHint.js' import { OffscreenFreeze } from './OffscreenFreeze.js' /** diff --git a/src/components/TeleportError.tsx b/src/components/TeleportError.tsx index 260f488d3..343ef7427 100644 --- a/src/components/TeleportError.tsx +++ b/src/components/TeleportError.tsx @@ -4,10 +4,10 @@ import { checkNeedsClaudeAiLogin, } from 'src/utils/background/remote/preconditions.js' import { gracefulShutdownSync } from 'src/utils/gracefulShutdown.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import { ConsoleOAuthFlow } from './ConsoleOAuthFlow.js' import { Select } from './CustomSelect/index.js' -import { Dialog } from '@anthropic/ink' +import { Dialog } from './design-system/Dialog.js' import { TeleportStash } from './TeleportStash.js' export type TeleportLocalErrorType = 'needsLogin' | 'needsGitStash' diff --git a/src/components/TeleportProgress.tsx b/src/components/TeleportProgress.tsx index 6ecb4c7cf..f8ef62110 100644 --- a/src/components/TeleportProgress.tsx +++ b/src/components/TeleportProgress.tsx @@ -1,8 +1,8 @@ import figures from 'figures' import * as React from 'react' import { useState } from 'react' -import type { Root } from '@anthropic/ink' -import { Box, Text, useAnimationFrame } from '@anthropic/ink' +import type { Root } from '../ink.js' +import { Box, Text, useAnimationFrame } from '../ink.js' import { AppStateProvider } from '../state/AppState.js' import { checkOutTeleportedSessionBranch, diff --git a/src/components/TeleportRepoMismatchDialog.tsx b/src/components/TeleportRepoMismatchDialog.tsx index 83e55bd90..126a9c432 100644 --- a/src/components/TeleportRepoMismatchDialog.tsx +++ b/src/components/TeleportRepoMismatchDialog.tsx @@ -1,12 +1,12 @@ import React, { useCallback, useState } from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import { getDisplayPath } from '../utils/file.js' import { removePathFromRepo, validateRepoAtPath, } from '../utils/githubRepoPathMapping.js' import { Select } from './CustomSelect/index.js' -import { Dialog } from '@anthropic/ink' +import { Dialog } from './design-system/Dialog.js' import { Spinner } from './Spinner.js' type Props = { diff --git a/src/components/TeleportResumeWrapper.tsx b/src/components/TeleportResumeWrapper.tsx index 9a2a6261e..60e6c7806 100644 --- a/src/components/TeleportResumeWrapper.tsx +++ b/src/components/TeleportResumeWrapper.tsx @@ -9,7 +9,7 @@ import { type TeleportSource, useTeleportResume, } from '../hooks/useTeleportResume.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import { useKeybinding } from '../keybindings/useKeybinding.js' import { ResumeTask } from './ResumeTask.js' import { Spinner } from './Spinner.js' diff --git a/src/components/TeleportStash.tsx b/src/components/TeleportStash.tsx index bb60e45a4..8baa30580 100644 --- a/src/components/TeleportStash.tsx +++ b/src/components/TeleportStash.tsx @@ -1,10 +1,11 @@ import figures from 'figures' import React, { useEffect, useState } from 'react' -import { Box, Text, Dialog } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import { logForDebugging } from '../utils/debug.js' import type { GitFileStatus } from '../utils/git.js' import { getFileStatus, stashToCleanState } from '../utils/git.js' import { Select } from './CustomSelect/index.js' +import { Dialog } from './design-system/Dialog.js' import { Spinner } from './Spinner.js' type TeleportStashProps = { diff --git a/src/components/TextInput.tsx b/src/components/TextInput.tsx index 0e1308336..486c73ef2 100644 --- a/src/components/TextInput.tsx +++ b/src/components/TextInput.tsx @@ -5,7 +5,13 @@ import { useVoiceState } from '../context/voice.js' import { useClipboardImageHint } from '../hooks/useClipboardImageHint.js' import { useSettings } from '../hooks/useSettings.js' import { useTextInput } from '../hooks/useTextInput.js' -import { Box, color, useAnimationFrame, useTerminalFocus, useTheme } from '@anthropic/ink' +import { + Box, + color, + useAnimationFrame, + useTerminalFocus, + useTheme, +} from '../ink.js' import type { BaseTextInputProps } from '../types/textInputTypes.js' import { isEnvTruthy } from '../utils/envUtils.js' import type { TextHighlight } from '../utils/textHighlighting.js' diff --git a/src/components/ThemePicker.tsx b/src/components/ThemePicker.tsx index 2c99fc455..b14bcfd2c 100644 --- a/src/components/ThemePicker.tsx +++ b/src/components/ThemePicker.tsx @@ -2,7 +2,13 @@ import { feature } from 'bun:bundle' import * as React from 'react' import { useExitOnCtrlCDWithKeybindings } from '../hooks/useExitOnCtrlCDWithKeybindings.js' import { useTerminalSize } from '../hooks/useTerminalSize.js' -import { Box, Text, usePreviewTheme, useTheme, useThemeSetting } from '@anthropic/ink' +import { + Box, + Text, + usePreviewTheme, + useTheme, + useThemeSetting, +} from '../ink.js' import { useRegisterKeybindingContext } from '../keybindings/KeybindingContext.js' import { useKeybinding } from '../keybindings/useKeybinding.js' import { useShortcutDisplay } from '../keybindings/useShortcutDisplay.js' @@ -11,7 +17,8 @@ import { gracefulShutdown } from '../utils/gracefulShutdown.js' import { updateSettingsForSource } from '../utils/settings/settings.js' import type { ThemeSetting } from '../utils/theme.js' import { Select } from './CustomSelect/index.js' -import { Byline, KeyboardShortcutHint } from '@anthropic/ink' +import { Byline } from './design-system/Byline.js' +import { KeyboardShortcutHint } from './design-system/KeyboardShortcutHint.js' import { getColorModuleUnavailableReason, getSyntaxTheme, diff --git a/src/components/ThinkingToggle.tsx b/src/components/ThinkingToggle.tsx index da2a78687..f17636cde 100644 --- a/src/components/ThinkingToggle.tsx +++ b/src/components/ThinkingToggle.tsx @@ -1,11 +1,13 @@ import * as React from 'react' import { useState } from 'react' import { useExitOnCtrlCDWithKeybindings } from 'src/hooks/useExitOnCtrlCDWithKeybindings.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import { useKeybinding } from '../keybindings/useKeybinding.js' import { ConfigurableShortcutHint } from './ConfigurableShortcutHint.js' import { Select } from './CustomSelect/index.js' -import { Byline, KeyboardShortcutHint, Pane } from '@anthropic/ink' +import { Byline } from './design-system/Byline.js' +import { KeyboardShortcutHint } from './design-system/KeyboardShortcutHint.js' +import { Pane } from './design-system/Pane.js' export type Props = { currentValue: boolean diff --git a/src/components/TokenWarning.tsx b/src/components/TokenWarning.tsx index fe134c7ec..3ccb7235f 100644 --- a/src/components/TokenWarning.tsx +++ b/src/components/TokenWarning.tsx @@ -1,7 +1,7 @@ import { feature } from 'bun:bundle' import * as React from 'react' import { useSyncExternalStore } from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import { getFeatureValue_CACHED_MAY_BE_STALE } from '../services/analytics/growthbook.js' import { calculateTokenWarningState, diff --git a/src/components/ToolUseLoader.tsx b/src/components/ToolUseLoader.tsx index de8fdb3c2..52652be99 100644 --- a/src/components/ToolUseLoader.tsx +++ b/src/components/ToolUseLoader.tsx @@ -1,8 +1,7 @@ import React from 'react' import { BLACK_CIRCLE } from '../constants/figures.js' - -import { Box, Text } from '@anthropic/ink' import { useBlink } from '../hooks/useBlink.js' +import { Box, Text } from '../ink.js' type Props = { isError: boolean diff --git a/src/components/TrustDialog/TrustDialog.tsx b/src/components/TrustDialog/TrustDialog.tsx index 88a0c897f..a57408392 100644 --- a/src/components/TrustDialog/TrustDialog.tsx +++ b/src/components/TrustDialog/TrustDialog.tsx @@ -4,7 +4,7 @@ import { logEvent } from 'src/services/analytics/index.js' import { setSessionTrustAccepted } from '../../bootstrap/state.js' import type { Command } from '../../commands.js' import { useExitOnCtrlCDWithKeybindings } from '../../hooks/useExitOnCtrlCDWithKeybindings.js' -import { Box, Link, Text } from '@anthropic/ink' +import { Box, Link, Text } from '../../ink.js' import { useKeybinding } from '../../keybindings/useKeybinding.js' import { getMcpConfigsByScope } from '../../services/mcp/config.js' import { BASH_TOOL_NAME } from '../../tools/BashTool/toolName.js' diff --git a/src/components/ValidationErrorsList.tsx b/src/components/ValidationErrorsList.tsx index 6620ab2fd..a7eea5400 100644 --- a/src/components/ValidationErrorsList.tsx +++ b/src/components/ValidationErrorsList.tsx @@ -1,6 +1,6 @@ import setWith from 'lodash-es/setWith.js' import * as React from 'react' -import { Box, Text, useTheme } from '@anthropic/ink' +import { Box, Text, useTheme } from '../ink.js' import type { ValidationError } from '../utils/settings/validation.js' import { type TreeNode, treeify } from '../utils/treeify.js' diff --git a/src/components/VimTextInput.tsx b/src/components/VimTextInput.tsx index 42dfa252e..bc8e8211f 100644 --- a/src/components/VimTextInput.tsx +++ b/src/components/VimTextInput.tsx @@ -2,7 +2,7 @@ import chalk from 'chalk' import React from 'react' import { useClipboardImageHint } from '../hooks/useClipboardImageHint.js' import { useVimInput } from '../hooks/useVimInput.js' -import { Box, color, useTerminalFocus, useTheme } from '@anthropic/ink' +import { Box, color, useTerminalFocus, useTheme } from '../ink.js' import type { VimTextInputProps } from '../types/textInputTypes.js' import type { TextHighlight } from '../utils/textHighlighting.js' import { BaseTextInput } from './BaseTextInput.js' diff --git a/src/components/VirtualMessageList.tsx b/src/components/VirtualMessageList.tsx index 5797297f6..bbe4a196c 100644 --- a/src/components/VirtualMessageList.tsx +++ b/src/components/VirtualMessageList.tsx @@ -10,7 +10,11 @@ import { useSyncExternalStore, } from 'react' import { useVirtualScroll } from '../hooks/useVirtualScroll.js' -import { Box, type DOMElement, type ScrollBoxHandle, type MatchPosition } from '@anthropic/ink' +import type { ScrollBoxHandle } from '../ink/components/ScrollBox.js' +import type { DOMElement } from '../ink/dom.js' +import type { MatchPosition } from '../ink/render-to-screen.js' +import { Box } from '../ink.js' +import type { RenderableMessage } from '../types/message.js' import { TextHoverColorContext } from './design-system/ThemedText.js' import { ScrollChromeContext } from './FullscreenLayout.js' diff --git a/src/components/WorkflowMultiselectDialog.tsx b/src/components/WorkflowMultiselectDialog.tsx index d5e1d5460..e06737514 100644 --- a/src/components/WorkflowMultiselectDialog.tsx +++ b/src/components/WorkflowMultiselectDialog.tsx @@ -1,9 +1,12 @@ import React, { useCallback, useState } from 'react' import type { Workflow } from '../commands/install-github-app/types.js' import type { ExitState } from '../hooks/useExitOnCtrlCDWithKeybindings.js' -import { Box, Link, Text, Byline, Dialog, KeyboardShortcutHint } from '@anthropic/ink' +import { Box, Link, Text } from '../ink.js' import { ConfigurableShortcutHint } from './ConfigurableShortcutHint.js' import { SelectMulti } from './CustomSelect/SelectMulti.js' +import { Byline } from './design-system/Byline.js' +import { Dialog } from './design-system/Dialog.js' +import { KeyboardShortcutHint } from './design-system/KeyboardShortcutHint.js' type WorkflowOption = { value: Workflow diff --git a/src/components/WorktreeExitDialog.tsx b/src/components/WorktreeExitDialog.tsx index 83ea8d554..ba5ab0f83 100644 --- a/src/components/WorktreeExitDialog.tsx +++ b/src/components/WorktreeExitDialog.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react' import type { CommandResultDisplay } from 'src/commands.js' import { logEvent } from 'src/services/analytics/index.js' import { logForDebugging } from 'src/utils/debug.js' -import { Box, Text, Dialog } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import { execFileNoThrow } from '../utils/execFileNoThrow.js' import { getPlansDirectory } from '../utils/plans.js' import { setCwd } from '../utils/Shell.js' @@ -13,6 +13,7 @@ import { killTmuxSession, } from '../utils/worktree.js' import { Select } from './CustomSelect/select.js' +import { Dialog } from './design-system/Dialog.js' import { Spinner } from './Spinner.js' // Inline require breaks the cycle this file would otherwise close: diff --git a/src/components/agents/AgentDetail.tsx b/src/components/agents/AgentDetail.tsx index 88f368dc6..4c817b134 100644 --- a/src/components/agents/AgentDetail.tsx +++ b/src/components/agents/AgentDetail.tsx @@ -1,6 +1,7 @@ import figures from 'figures' import * as React from 'react' -import { type KeyboardEvent, Box, Text } from '@anthropic/ink' +import type { KeyboardEvent } from '../../ink/events/keyboard-event.js' +import { Box, Text } from '../../ink.js' import { useKeybinding } from '../../keybindings/useKeybinding.js' import type { Tools } from '../../Tool.js' import { getAgentColor } from '../../tools/AgentTool/agentColorManager.js' diff --git a/src/components/agents/AgentEditor.tsx b/src/components/agents/AgentEditor.tsx index 7cbd52b96..e5c7b1847 100644 --- a/src/components/agents/AgentEditor.tsx +++ b/src/components/agents/AgentEditor.tsx @@ -3,7 +3,8 @@ import figures from 'figures' import * as React from 'react' import { useCallback, useMemo, useState } from 'react' import { useSetAppState } from 'src/state/AppState.js' -import { type KeyboardEvent, Box, Text } from '@anthropic/ink' +import type { KeyboardEvent } from '../../ink/events/keyboard-event.js' +import { Box, Text } from '../../ink.js' import { useKeybinding } from '../../keybindings/useKeybinding.js' import type { Tools } from '../../Tool.js' import { diff --git a/src/components/agents/AgentNavigationFooter.tsx b/src/components/agents/AgentNavigationFooter.tsx index e52149f6a..9c4fa9f76 100644 --- a/src/components/agents/AgentNavigationFooter.tsx +++ b/src/components/agents/AgentNavigationFooter.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import { useExitOnCtrlCDWithKeybindings } from '../../hooks/useExitOnCtrlCDWithKeybindings.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' type Props = { instructions?: string diff --git a/src/components/agents/AgentsList.tsx b/src/components/agents/AgentsList.tsx index 4be51e11b..6eadf1ef7 100644 --- a/src/components/agents/AgentsList.tsx +++ b/src/components/agents/AgentsList.tsx @@ -1,7 +1,8 @@ import figures from 'figures' import * as React from 'react' import type { SettingSource } from 'src/utils/settings/constants.js' -import { type KeyboardEvent, Box, Text } from '@anthropic/ink' +import type { KeyboardEvent } from '../../ink/events/keyboard-event.js' +import { Box, Text } from '../../ink.js' import type { ResolvedAgent } from '../../tools/AgentTool/agentDisplay.js' import { AGENT_SOURCE_GROUPS, @@ -11,7 +12,8 @@ import { } from '../../tools/AgentTool/agentDisplay.js' import type { AgentDefinition } from '../../tools/AgentTool/loadAgentsDir.js' import { count } from '../../utils/array.js' -import { Dialog, Divider } from '@anthropic/ink' +import { Dialog } from '../design-system/Dialog.js' +import { Divider } from '../design-system/Divider.js' import { getAgentSourceDisplayName } from './utils.js' type Props = { diff --git a/src/components/agents/AgentsMenu.tsx b/src/components/agents/AgentsMenu.tsx index 00c3a9ce3..91de932b4 100644 --- a/src/components/agents/AgentsMenu.tsx +++ b/src/components/agents/AgentsMenu.tsx @@ -5,7 +5,7 @@ import type { SettingSource } from 'src/utils/settings/constants.js' import type { CommandResultDisplay } from '../../commands.js' import { useExitOnCtrlCDWithKeybindings } from '../../hooks/useExitOnCtrlCDWithKeybindings.js' import { useMergedTools } from '../../hooks/useMergedTools.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { useAppState, useSetAppState } from '../../state/AppState.js' import type { Tools } from '../../Tool.js' import { @@ -19,7 +19,7 @@ import { import { toError } from '../../utils/errors.js' import { logError } from '../../utils/log.js' import { Select } from '../CustomSelect/select.js' -import { Dialog } from '@anthropic/ink' +import { Dialog } from '../design-system/Dialog.js' import { AgentDetail } from './AgentDetail.js' import { AgentEditor } from './AgentEditor.js' import { AgentNavigationFooter } from './AgentNavigationFooter.js' diff --git a/src/components/agents/ColorPicker.tsx b/src/components/agents/ColorPicker.tsx index 7db5ea288..8549424cd 100644 --- a/src/components/agents/ColorPicker.tsx +++ b/src/components/agents/ColorPicker.tsx @@ -1,7 +1,7 @@ import figures from 'figures' import React, { useState } from 'react' -import type { KeyboardEvent } from '@anthropic/ink' -import { Box, Text } from '@anthropic/ink' +import type { KeyboardEvent } from '../../ink/events/keyboard-event.js' +import { Box, Text } from '../../ink.js' import { AGENT_COLOR_TO_THEME_COLOR, AGENT_COLORS, diff --git a/src/components/agents/ModelSelector.tsx b/src/components/agents/ModelSelector.tsx index c3707d3fd..4f1b2e8af 100644 --- a/src/components/agents/ModelSelector.tsx +++ b/src/components/agents/ModelSelector.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { getAgentModelOptions } from '../../utils/model/agent.js' import { Select } from '../CustomSelect/select.js' diff --git a/src/components/agents/ToolSelector.tsx b/src/components/agents/ToolSelector.tsx index 96b9f4b68..9bc20b7d8 100644 --- a/src/components/agents/ToolSelector.tsx +++ b/src/components/agents/ToolSelector.tsx @@ -21,11 +21,12 @@ import { TodoWriteTool } from 'src/tools/TodoWriteTool/TodoWriteTool.js' import { TungstenTool } from 'src/tools/TungstenTool/TungstenTool.js' import { WebFetchTool } from 'src/tools/WebFetchTool/WebFetchTool.js' import { WebSearchTool } from 'src/tools/WebSearchTool/WebSearchTool.js' -import { type KeyboardEvent, Box, Text } from '@anthropic/ink' +import type { KeyboardEvent } from '../../ink/events/keyboard-event.js' +import { Box, Text } from '../../ink.js' import { useKeybinding } from '../../keybindings/useKeybinding.js' import { count } from '../../utils/array.js' import { plural } from '../../utils/stringUtils.js' -import { Divider } from '@anthropic/ink' +import { Divider } from '../design-system/Divider.js' type Props = { tools: Tools diff --git a/src/components/agents/new-agent-creation/wizard-steps/ColorStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/ColorStep.tsx index 7763daac6..adc35e27c 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/ColorStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/ColorStep.tsx @@ -1,8 +1,10 @@ import React, { type ReactNode } from 'react' -import { Box, Byline, KeyboardShortcutHint } from '@anthropic/ink' +import { Box } from '../../../../ink.js' import { useKeybinding } from '../../../../keybindings/useKeybinding.js' import type { AgentColorName } from '../../../../tools/AgentTool/agentColorManager.js' import { ConfigurableShortcutHint } from '../../../ConfigurableShortcutHint.js' +import { Byline } from '../../../design-system/Byline.js' +import { KeyboardShortcutHint } from '../../../design-system/KeyboardShortcutHint.js' import { useWizard } from '../../../wizard/index.js' import { WizardDialogLayout } from '../../../wizard/WizardDialogLayout.js' import { ColorPicker } from '../../ColorPicker.js' diff --git a/src/components/agents/new-agent-creation/wizard-steps/ConfirmStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/ConfirmStep.tsx index ef02cbefe..bfa035eb5 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/ConfirmStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/ConfirmStep.tsx @@ -1,5 +1,6 @@ import React, { type ReactNode } from 'react' -import { type KeyboardEvent, Box, Byline, KeyboardShortcutHint, Text } from '@anthropic/ink' +import type { KeyboardEvent } from '../../../../ink/events/keyboard-event.js' +import { Box, Text } from '../../../../ink.js' import { useKeybinding } from '../../../../keybindings/useKeybinding.js' import { isAutoMemoryEnabled } from '../../../../memdir/paths.js' import type { Tools } from '../../../../Tool.js' @@ -8,6 +9,8 @@ import type { AgentDefinition } from '../../../../tools/AgentTool/loadAgentsDir. import { truncateToWidth } from '../../../../utils/format.js' import { getAgentModelDisplay } from '../../../../utils/model/agent.js' import { ConfigurableShortcutHint } from '../../../ConfigurableShortcutHint.js' +import { Byline } from '../../../design-system/Byline.js' +import { KeyboardShortcutHint } from '../../../design-system/KeyboardShortcutHint.js' import { useWizard } from '../../../wizard/index.js' import { WizardDialogLayout } from '../../../wizard/WizardDialogLayout.js' import { getNewRelativeAgentFilePath } from '../../agentFileUtils.js' diff --git a/src/components/agents/new-agent-creation/wizard-steps/DescriptionStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/DescriptionStep.tsx index 50a0d0590..1138cc3d3 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/DescriptionStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/DescriptionStep.tsx @@ -1,8 +1,10 @@ import React, { type ReactNode, useCallback, useState } from 'react' -import { Box, Byline, KeyboardShortcutHint, Text } from '@anthropic/ink' +import { Box, Text } from '../../../../ink.js' import { useKeybinding } from '../../../../keybindings/useKeybinding.js' import { editPromptInEditor } from '../../../../utils/promptEditor.js' import { ConfigurableShortcutHint } from '../../../ConfigurableShortcutHint.js' +import { Byline } from '../../../design-system/Byline.js' +import { KeyboardShortcutHint } from '../../../design-system/KeyboardShortcutHint.js' import TextInput from '../../../TextInput.js' import { useWizard } from '../../../wizard/index.js' import { WizardDialogLayout } from '../../../wizard/WizardDialogLayout.js' diff --git a/src/components/agents/new-agent-creation/wizard-steps/GenerateStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/GenerateStep.tsx index 1e0b88512..1cb7ae69d 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/GenerateStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/GenerateStep.tsx @@ -1,11 +1,12 @@ import { APIUserAbortError } from '@anthropic-ai/sdk' import React, { type ReactNode, useCallback, useRef, useState } from 'react' import { useMainLoopModel } from '../../../../hooks/useMainLoopModel.js' -import { Box, Byline, Text } from '@anthropic/ink' +import { Box, Text } from '../../../../ink.js' import { useKeybinding } from '../../../../keybindings/useKeybinding.js' import { createAbortController } from '../../../../utils/abortController.js' import { editPromptInEditor } from '../../../../utils/promptEditor.js' import { ConfigurableShortcutHint } from '../../../ConfigurableShortcutHint.js' +import { Byline } from '../../../design-system/Byline.js' import { Spinner } from '../../../Spinner.js' import TextInput from '../../../TextInput.js' import { useWizard } from '../../../wizard/index.js' diff --git a/src/components/agents/new-agent-creation/wizard-steps/LocationStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/LocationStep.tsx index d846cb39b..a7fd0a2bc 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/LocationStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/LocationStep.tsx @@ -1,8 +1,10 @@ import React, { type ReactNode } from 'react' -import { Box, Byline, KeyboardShortcutHint } from '@anthropic/ink' +import { Box } from '../../../../ink.js' import type { SettingSource } from '../../../../utils/settings/constants.js' import { ConfigurableShortcutHint } from '../../../ConfigurableShortcutHint.js' import { Select } from '../../../CustomSelect/select.js' +import { Byline } from '../../../design-system/Byline.js' +import { KeyboardShortcutHint } from '../../../design-system/KeyboardShortcutHint.js' import { useWizard } from '../../../wizard/index.js' import { WizardDialogLayout } from '../../../wizard/WizardDialogLayout.js' import type { AgentWizardData } from '../types.js' diff --git a/src/components/agents/new-agent-creation/wizard-steps/MemoryStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/MemoryStep.tsx index d9fb92ed5..3c987cf77 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/MemoryStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/MemoryStep.tsx @@ -1,5 +1,5 @@ import React, { type ReactNode } from 'react' -import { Box, Byline, KeyboardShortcutHint } from '@anthropic/ink' +import { Box } from '../../../../ink.js' import { useKeybinding } from '../../../../keybindings/useKeybinding.js' import { isAutoMemoryEnabled } from '../../../../memdir/paths.js' import { @@ -8,6 +8,8 @@ import { } from '../../../../tools/AgentTool/agentMemory.js' import { ConfigurableShortcutHint } from '../../../ConfigurableShortcutHint.js' import { Select } from '../../../CustomSelect/select.js' +import { Byline } from '../../../design-system/Byline.js' +import { KeyboardShortcutHint } from '../../../design-system/KeyboardShortcutHint.js' import { useWizard } from '../../../wizard/index.js' import { WizardDialogLayout } from '../../../wizard/WizardDialogLayout.js' import type { AgentWizardData } from '../types.js' diff --git a/src/components/agents/new-agent-creation/wizard-steps/MethodStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/MethodStep.tsx index 552c20d1c..8f8252e12 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/MethodStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/MethodStep.tsx @@ -1,7 +1,9 @@ import React, { type ReactNode } from 'react' -import { Box, Byline, KeyboardShortcutHint } from '@anthropic/ink' +import { Box } from '../../../../ink.js' import { ConfigurableShortcutHint } from '../../../ConfigurableShortcutHint.js' import { Select } from '../../../CustomSelect/select.js' +import { Byline } from '../../../design-system/Byline.js' +import { KeyboardShortcutHint } from '../../../design-system/KeyboardShortcutHint.js' import { useWizard } from '../../../wizard/index.js' import { WizardDialogLayout } from '../../../wizard/WizardDialogLayout.js' import type { AgentWizardData } from '../types.js' diff --git a/src/components/agents/new-agent-creation/wizard-steps/ModelStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/ModelStep.tsx index a92ecde4a..586cc6cc8 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/ModelStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/ModelStep.tsx @@ -1,6 +1,7 @@ import React, { type ReactNode } from 'react' import { ConfigurableShortcutHint } from '../../../ConfigurableShortcutHint.js' -import { Byline, KeyboardShortcutHint } from '@anthropic/ink' +import { Byline } from '../../../design-system/Byline.js' +import { KeyboardShortcutHint } from '../../../design-system/KeyboardShortcutHint.js' import { useWizard } from '../../../wizard/index.js' import { WizardDialogLayout } from '../../../wizard/WizardDialogLayout.js' import { ModelSelector } from '../../ModelSelector.js' diff --git a/src/components/agents/new-agent-creation/wizard-steps/PromptStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/PromptStep.tsx index c1e1f5260..4d6747520 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/PromptStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/PromptStep.tsx @@ -1,8 +1,10 @@ import React, { type ReactNode, useCallback, useState } from 'react' -import { Box, Byline, KeyboardShortcutHint, Text } from '@anthropic/ink' +import { Box, Text } from '../../../../ink.js' import { useKeybinding } from '../../../../keybindings/useKeybinding.js' import { editPromptInEditor } from '../../../../utils/promptEditor.js' import { ConfigurableShortcutHint } from '../../../ConfigurableShortcutHint.js' +import { Byline } from '../../../design-system/Byline.js' +import { KeyboardShortcutHint } from '../../../design-system/KeyboardShortcutHint.js' import TextInput from '../../../TextInput.js' import { useWizard } from '../../../wizard/index.js' import { WizardDialogLayout } from '../../../wizard/WizardDialogLayout.js' diff --git a/src/components/agents/new-agent-creation/wizard-steps/ToolsStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/ToolsStep.tsx index 19a8360c4..501509ff5 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/ToolsStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/ToolsStep.tsx @@ -1,7 +1,8 @@ import React, { type ReactNode } from 'react' import type { Tools } from '../../../../Tool.js' -import { Byline, KeyboardShortcutHint } from '@anthropic/ink' import { ConfigurableShortcutHint } from '../../../ConfigurableShortcutHint.js' +import { Byline } from '../../../design-system/Byline.js' +import { KeyboardShortcutHint } from '../../../design-system/KeyboardShortcutHint.js' import { useWizard } from '../../../wizard/index.js' import { WizardDialogLayout } from '../../../wizard/WizardDialogLayout.js' import { ToolSelector } from '../../ToolSelector.js' diff --git a/src/components/agents/new-agent-creation/wizard-steps/TypeStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/TypeStep.tsx index 6c57440c8..6ff025492 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/TypeStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/TypeStep.tsx @@ -1,8 +1,10 @@ import React, { type ReactNode, useState } from 'react' -import { Box, Byline, KeyboardShortcutHint, Text } from '@anthropic/ink' +import { Box, Text } from '../../../../ink.js' import { useKeybinding } from '../../../../keybindings/useKeybinding.js' import type { AgentDefinition } from '../../../../tools/AgentTool/loadAgentsDir.js' import { ConfigurableShortcutHint } from '../../../ConfigurableShortcutHint.js' +import { Byline } from '../../../design-system/Byline.js' +import { KeyboardShortcutHint } from '../../../design-system/KeyboardShortcutHint.js' import TextInput from '../../../TextInput.js' import { useWizard } from '../../../wizard/index.js' import { WizardDialogLayout } from '../../../wizard/WizardDialogLayout.js' diff --git a/src/components/design-system/Byline.tsx b/src/components/design-system/Byline.tsx index d2b73b77a..b0ddc97f3 100644 --- a/src/components/design-system/Byline.tsx +++ b/src/components/design-system/Byline.tsx @@ -1,5 +1,5 @@ import React, { Children, isValidElement } from 'react' -import { Text } from '@anthropic/ink' +import { Text } from '../../ink.js' type Props = { /** The items to join with a middot separator */ diff --git a/src/components/design-system/Dialog.tsx b/src/components/design-system/Dialog.tsx index dfd65449e..4472bd0d0 100644 --- a/src/components/design-system/Dialog.tsx +++ b/src/components/design-system/Dialog.tsx @@ -3,7 +3,7 @@ import { type ExitState, useExitOnCtrlCDWithKeybindings, } from '../../hooks/useExitOnCtrlCDWithKeybindings.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { useKeybinding } from '../../keybindings/useKeybinding.js' import type { Theme } from '../../utils/theme.js' import { ConfigurableShortcutHint } from '../ConfigurableShortcutHint.js' diff --git a/src/components/design-system/Divider.tsx b/src/components/design-system/Divider.tsx index 2606a5d5b..a88982be5 100644 --- a/src/components/design-system/Divider.tsx +++ b/src/components/design-system/Divider.tsx @@ -1,6 +1,7 @@ import React from 'react' import { useTerminalSize } from '../../hooks/useTerminalSize.js' -import { Ansi, Text, stringWidth } from '@anthropic/ink' +import { stringWidth } from '../../ink/stringWidth.js' +import { Ansi, Text } from '../../ink.js' import type { Theme } from '../../utils/theme.js' type DividerProps = { diff --git a/src/components/design-system/FuzzyPicker.tsx b/src/components/design-system/FuzzyPicker.tsx index 8218f4074..fc1b9fe9e 100644 --- a/src/components/design-system/FuzzyPicker.tsx +++ b/src/components/design-system/FuzzyPicker.tsx @@ -2,7 +2,9 @@ import * as React from 'react' import { useEffect, useState } from 'react' import { useSearchInput } from '../../hooks/useSearchInput.js' import { useTerminalSize } from '../../hooks/useTerminalSize.js' -import { type KeyboardEvent, Box, clamp, Text, useTerminalFocus } from '@anthropic/ink' +import type { KeyboardEvent } from '../../ink/events/keyboard-event.js' +import { clamp } from '../../ink/layout/geometry.js' +import { Box, Text, useTerminalFocus } from '../../ink.js' import { SearchBox } from '../SearchBox.js' import { Byline } from './Byline.js' import { KeyboardShortcutHint } from './KeyboardShortcutHint.js' diff --git a/src/components/design-system/KeyboardShortcutHint.tsx b/src/components/design-system/KeyboardShortcutHint.tsx index 45489dfac..7d3c136d1 100644 --- a/src/components/design-system/KeyboardShortcutHint.tsx +++ b/src/components/design-system/KeyboardShortcutHint.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { Text } from '@anthropic/ink' +import Text from '../../ink/components/Text.js' type Props = { /** The key or chord to display (e.g., "ctrl+o", "Enter", "↑/↓") */ diff --git a/src/components/design-system/ListItem.tsx b/src/components/design-system/ListItem.tsx index bfdbb2747..2d142be03 100644 --- a/src/components/design-system/ListItem.tsx +++ b/src/components/design-system/ListItem.tsx @@ -1,7 +1,8 @@ import figures from 'figures' import type { ReactNode } from 'react' import React from 'react' -import { useDeclaredCursor, Box, Text } from '@anthropic/ink' +import { useDeclaredCursor } from '../../ink/hooks/use-declared-cursor.js' +import { Box, Text } from '../../ink.js' type ListItemProps = { /** diff --git a/src/components/design-system/LoadingState.tsx b/src/components/design-system/LoadingState.tsx index ff6de70ec..046f726fa 100644 --- a/src/components/design-system/LoadingState.tsx +++ b/src/components/design-system/LoadingState.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { Spinner } from '../Spinner.js' type LoadingStateProps = { diff --git a/src/components/design-system/Pane.tsx b/src/components/design-system/Pane.tsx index e6641d18b..9c10907d3 100644 --- a/src/components/design-system/Pane.tsx +++ b/src/components/design-system/Pane.tsx @@ -1,6 +1,6 @@ import React from 'react' import { useIsInsideModal } from '../../context/modalContext.js' -import { Box } from '@anthropic/ink' +import { Box } from '../../ink.js' import type { Theme } from '../../utils/theme.js' import { Divider } from './Divider.js' diff --git a/src/components/design-system/ProgressBar.tsx b/src/components/design-system/ProgressBar.tsx index b69b47bcf..590fcd265 100644 --- a/src/components/design-system/ProgressBar.tsx +++ b/src/components/design-system/ProgressBar.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { Text } from '@anthropic/ink' +import { Text } from '../../ink.js' import type { Theme } from '../../utils/theme.js' type Props = { diff --git a/src/components/design-system/Ratchet.tsx b/src/components/design-system/Ratchet.tsx index d47caa017..91580ff05 100644 --- a/src/components/design-system/Ratchet.tsx +++ b/src/components/design-system/Ratchet.tsx @@ -1,6 +1,7 @@ import React, { useCallback, useLayoutEffect, useRef, useState } from 'react' import { useTerminalSize } from '../../hooks/useTerminalSize.js' -import { useTerminalViewport, Box, type DOMElement, measureElement } from '@anthropic/ink' +import { useTerminalViewport } from '../../ink/hooks/use-terminal-viewport.js' +import { Box, type DOMElement, measureElement } from '../../ink.js' type Props = { children: React.ReactNode diff --git a/src/components/design-system/StatusIcon.tsx b/src/components/design-system/StatusIcon.tsx index d37d4d23b..832c83a9e 100644 --- a/src/components/design-system/StatusIcon.tsx +++ b/src/components/design-system/StatusIcon.tsx @@ -1,6 +1,6 @@ import figures from 'figures' import React from 'react' -import { Text } from '@anthropic/ink' +import { Text } from '../../ink.js' type Status = 'success' | 'error' | 'warning' | 'info' | 'pending' | 'loading' diff --git a/src/components/design-system/Tabs.tsx b/src/components/design-system/Tabs.tsx index 886b84673..40bae7baa 100644 --- a/src/components/design-system/Tabs.tsx +++ b/src/components/design-system/Tabs.tsx @@ -10,7 +10,10 @@ import { useModalScrollRef, } from '../../context/modalContext.js' import { useTerminalSize } from '../../hooks/useTerminalSize.js' -import { Box, ScrollBox, Text, stringWidth, type KeyboardEvent } from '@anthropic/ink' +import ScrollBox from '../../ink/components/ScrollBox.js' +import type { KeyboardEvent } from '../../ink/events/keyboard-event.js' +import { stringWidth } from '../../ink/stringWidth.js' +import { Box, Text } from '../../ink.js' import { useKeybindings } from '../../keybindings/useKeybinding.js' import type { Theme } from '../../utils/theme.js' diff --git a/src/components/design-system/ThemeProvider.tsx b/src/components/design-system/ThemeProvider.tsx index 55eb90447..ef60d23a1 100644 --- a/src/components/design-system/ThemeProvider.tsx +++ b/src/components/design-system/ThemeProvider.tsx @@ -6,7 +6,7 @@ import React, { useMemo, useState, } from 'react' -import { useStdin } from '@anthropic/ink' +import useStdin from '../../ink/hooks/use-stdin.js' import { getGlobalConfig, saveGlobalConfig } from '../../utils/config.js' import { getSystemThemeName, diff --git a/src/components/design-system/ThemedBox.tsx b/src/components/design-system/ThemedBox.tsx index 008a2ab9e..10fbe9137 100644 --- a/src/components/design-system/ThemedBox.tsx +++ b/src/components/design-system/ThemedBox.tsx @@ -1,5 +1,10 @@ import React, { type PropsWithChildren, type Ref } from 'react' -import { type ClickEvent, DOMElement, type FocusEvent, type KeyboardEvent, Color, type Styles, Box as BaseBox } from '@anthropic/ink' +import Box from '../../ink/components/Box.js' +import type { DOMElement } from '../../ink/dom.js' +import type { ClickEvent } from '../../ink/events/click-event.js' +import type { FocusEvent } from '../../ink/events/focus-event.js' +import type { KeyboardEvent } from '../../ink/events/keyboard-event.js' +import type { Color, Styles } from '../../ink/styles.js' import { getTheme, type Theme } from '../../utils/theme.js' import { useTheme } from './ThemeProvider.js' diff --git a/src/components/design-system/ThemedText.tsx b/src/components/design-system/ThemedText.tsx index c89b1749a..3c32b8bc3 100644 --- a/src/components/design-system/ThemedText.tsx +++ b/src/components/design-system/ThemedText.tsx @@ -1,7 +1,7 @@ import type { ReactNode } from 'react' import React, { useContext } from 'react' -import { Text } from '@anthropic/ink' -import type { Color, Styles } from '@anthropic/ink' +import Text from '../../ink/components/Text.js' +import type { Color, Styles } from '../../ink/styles.js' import { getTheme, type Theme } from '../../utils/theme.js' import { useTheme } from './ThemeProvider.js' diff --git a/src/components/design-system/color.ts b/src/components/design-system/color.ts index 56c8612de..d21e51279 100644 --- a/src/components/design-system/color.ts +++ b/src/components/design-system/color.ts @@ -1,4 +1,5 @@ -import { type ColorType, colorize, type Color } from '@anthropic/ink' +import { type ColorType, colorize } from '../../ink/colorize.js' +import type { Color } from '../../ink/styles.js' import { getTheme, type Theme, type ThemeName } from '../../utils/theme.js' /** diff --git a/src/components/diff/DiffDetailView.tsx b/src/components/diff/DiffDetailView.tsx index 43fb442fc..af79d9365 100644 --- a/src/components/diff/DiffDetailView.tsx +++ b/src/components/diff/DiffDetailView.tsx @@ -2,10 +2,10 @@ import type { StructuredPatchHunk } from 'diff' import { resolve } from 'path' import React, { useMemo } from 'react' import { useTerminalSize } from '../../hooks/useTerminalSize.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { getCwd } from '../../utils/cwd.js' import { readFileSafe } from '../../utils/file.js' -import { Divider } from '@anthropic/ink' +import { Divider } from '../design-system/Divider.js' import { StructuredDiff } from '../StructuredDiff.js' type Props = { diff --git a/src/components/diff/DiffDialog.tsx b/src/components/diff/DiffDialog.tsx index 3f6757b66..8847a25fc 100644 --- a/src/components/diff/DiffDialog.tsx +++ b/src/components/diff/DiffDialog.tsx @@ -4,12 +4,13 @@ import type { CommandResultDisplay } from '../../commands.js' import { useRegisterOverlay } from '../../context/overlayContext.js' import { type DiffData, useDiffData } from '../../hooks/useDiffData.js' import { type TurnDiff, useTurnDiffs } from '../../hooks/useTurnDiffs.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { useKeybindings } from '../../keybindings/useKeybinding.js' import { useShortcutDisplay } from '../../keybindings/useShortcutDisplay.js' import type { Message } from '../../types/message.js' import { plural } from '../../utils/stringUtils.js' -import { Byline, Dialog } from '@anthropic/ink' +import { Byline } from '../design-system/Byline.js' +import { Dialog } from '../design-system/Dialog.js' import { DiffDetailView } from './DiffDetailView.js' import { DiffFileList } from './DiffFileList.js' diff --git a/src/components/diff/DiffFileList.tsx b/src/components/diff/DiffFileList.tsx index ef498925b..76c3040aa 100644 --- a/src/components/diff/DiffFileList.tsx +++ b/src/components/diff/DiffFileList.tsx @@ -2,7 +2,7 @@ import figures from 'figures' import React, { useMemo } from 'react' import type { DiffFile } from '../../hooks/useDiffData.js' import { useTerminalSize } from '../../hooks/useTerminalSize.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { truncateStartToWidth } from '../../utils/format.js' import { plural } from '../../utils/stringUtils.js' diff --git a/src/components/grove/Grove.tsx b/src/components/grove/Grove.tsx index 938971b99..0998fcf05 100644 --- a/src/components/grove/Grove.tsx +++ b/src/components/grove/Grove.tsx @@ -3,7 +3,7 @@ import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent, } from 'src/services/analytics/index.js' -import { Box, Link, Text, useInput } from '@anthropic/ink' +import { Box, Link, Text, useInput } from '../../ink.js' import { type AccountSettings, calculateShouldShowGrove, @@ -14,7 +14,9 @@ import { updateGroveSettings, } from '../../services/api/grove.js' import { Select } from '../CustomSelect/index.js' -import { Byline, Dialog, KeyboardShortcutHint } from '@anthropic/ink' +import { Byline } from '../design-system/Byline.js' +import { Dialog } from '../design-system/Dialog.js' +import { KeyboardShortcutHint } from '../design-system/KeyboardShortcutHint.js' export type GroveDecision = | 'accept_opt_in' diff --git a/src/components/hooks/HooksConfigMenu.tsx b/src/components/hooks/HooksConfigMenu.tsx index f59ced979..425af2798 100644 --- a/src/components/hooks/HooksConfigMenu.tsx +++ b/src/components/hooks/HooksConfigMenu.tsx @@ -16,7 +16,7 @@ import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js' import { useAppState, useAppStateStore } from 'src/state/AppState.js' import type { CommandResultDisplay } from '../../commands.js' import { useSettingsChange } from '../../hooks/useSettingsChange.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { useKeybinding } from '../../keybindings/useKeybinding.js' import { getHookEventMetadata, @@ -31,7 +31,7 @@ import { getSettingsForSource, } from '../../utils/settings/settings.js' import { plural } from '../../utils/stringUtils.js' -import { Dialog } from '@anthropic/ink' +import { Dialog } from '../design-system/Dialog.js' import { SelectEventMode } from './SelectEventMode.js' import { SelectHookMode } from './SelectHookMode.js' import { SelectMatcherMode } from './SelectMatcherMode.js' diff --git a/src/components/hooks/PromptDialog.tsx b/src/components/hooks/PromptDialog.tsx index 2bca1ceb4..f5566ec46 100644 --- a/src/components/hooks/PromptDialog.tsx +++ b/src/components/hooks/PromptDialog.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { useKeybinding } from '../../keybindings/useKeybinding.js' import type { PromptRequest } from '../../types/hooks.js' import { Select } from '../CustomSelect/select.js' diff --git a/src/components/hooks/SelectEventMode.tsx b/src/components/hooks/SelectEventMode.tsx index c232f745a..a18d01952 100644 --- a/src/components/hooks/SelectEventMode.tsx +++ b/src/components/hooks/SelectEventMode.tsx @@ -11,10 +11,10 @@ import figures from 'figures' import * as React from 'react' import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js' import type { HookEventMetadata } from 'src/utils/hooks/hooksConfigManager.js' -import { Box, Link, Text } from '@anthropic/ink' +import { Box, Link, Text } from '../../ink.js' import { plural } from '../../utils/stringUtils.js' import { Select } from '../CustomSelect/select.js' -import { Dialog } from '@anthropic/ink' +import { Dialog } from '../design-system/Dialog.js' type Props = { hookEventMetadata: Record diff --git a/src/components/hooks/SelectHookMode.tsx b/src/components/hooks/SelectHookMode.tsx index b27195b14..5764ae9d5 100644 --- a/src/components/hooks/SelectHookMode.tsx +++ b/src/components/hooks/SelectHookMode.tsx @@ -8,14 +8,14 @@ import * as React from 'react' import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js' import type { HookEventMetadata } from 'src/utils/hooks/hooksConfigManager.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { getHookDisplayText, hookSourceHeaderDisplayString, type IndividualHookConfig, } from '../../utils/hooks/hooksSettings.js' import { Select } from '../CustomSelect/select.js' -import { Dialog } from '@anthropic/ink' +import { Dialog } from '../design-system/Dialog.js' type Props = { selectedEvent: HookEvent diff --git a/src/components/hooks/SelectMatcherMode.tsx b/src/components/hooks/SelectMatcherMode.tsx index c82987f02..6792a47b1 100644 --- a/src/components/hooks/SelectMatcherMode.tsx +++ b/src/components/hooks/SelectMatcherMode.tsx @@ -6,7 +6,7 @@ */ import * as React from 'react' import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { type HookSource, hookSourceInlineDisplayString, @@ -14,7 +14,7 @@ import { } from '../../utils/hooks/hooksSettings.js' import { plural } from '../../utils/stringUtils.js' import { Select } from '../CustomSelect/select.js' -import { Dialog } from '@anthropic/ink' +import { Dialog } from '../design-system/Dialog.js' type MatcherWithSource = { matcher: string diff --git a/src/components/hooks/ViewHookMode.tsx b/src/components/hooks/ViewHookMode.tsx index b79c09cca..5766ead25 100644 --- a/src/components/hooks/ViewHookMode.tsx +++ b/src/components/hooks/ViewHookMode.tsx @@ -5,12 +5,12 @@ * confirmation screen and directs users to settings.json or Claude for edits. */ import * as React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { hookSourceDescriptionDisplayString, type IndividualHookConfig, } from '../../utils/hooks/hooksSettings.js' -import { Dialog } from '@anthropic/ink' +import { Dialog } from '../design-system/Dialog.js' type Props = { selectedHook: IndividualHookConfig diff --git a/src/components/mcp/CapabilitiesSection.tsx b/src/components/mcp/CapabilitiesSection.tsx index dd0033382..a5f98466a 100644 --- a/src/components/mcp/CapabilitiesSection.tsx +++ b/src/components/mcp/CapabilitiesSection.tsx @@ -1,6 +1,6 @@ import React from 'react' -import { Box, Text } from '@anthropic/ink' -import { Byline } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' +import { Byline } from '../design-system/Byline.js' type Props = { serverToolsCount: number diff --git a/src/components/mcp/ElicitationDialog.tsx b/src/components/mcp/ElicitationDialog.tsx index c93003106..dbf8f22f9 100644 --- a/src/components/mcp/ElicitationDialog.tsx +++ b/src/components/mcp/ElicitationDialog.tsx @@ -10,7 +10,7 @@ import { useRegisterOverlay } from '../../context/overlayContext.js' import { useNotifyAfterTimeout } from '../../hooks/useNotifyAfterTimeout.js' import { useTerminalSize } from '../../hooks/useTerminalSize.js' // eslint-disable-next-line custom-rules/prefer-use-keybindings -- raw text input for elicitation form -import { Box, Text, useInput } from '@anthropic/ink' +import { Box, Text, useInput } from '../../ink.js' import { useKeybinding } from '../../keybindings/useKeybinding.js' import type { ElicitationRequestEvent } from '../../services/mcp/elicitationHandler.js' import { openBrowser } from '../../utils/browser.js' @@ -27,7 +27,9 @@ import { } from '../../utils/mcp/elicitationValidation.js' import { plural } from '../../utils/stringUtils.js' import { ConfigurableShortcutHint } from '../ConfigurableShortcutHint.js' -import { Byline, Dialog, KeyboardShortcutHint } from '@anthropic/ink' +import { Byline } from '../design-system/Byline.js' +import { Dialog } from '../design-system/Dialog.js' +import { KeyboardShortcutHint } from '../design-system/KeyboardShortcutHint.js' import TextInput from '../TextInput.js' type Props = { diff --git a/src/components/mcp/MCPAgentServerMenu.tsx b/src/components/mcp/MCPAgentServerMenu.tsx index 913dee9bb..b02f79dac 100644 --- a/src/components/mcp/MCPAgentServerMenu.tsx +++ b/src/components/mcp/MCPAgentServerMenu.tsx @@ -1,7 +1,7 @@ import figures from 'figures' import React, { useCallback, useEffect, useRef, useState } from 'react' import type { CommandResultDisplay } from '../../commands.js' -import { Box, color, Link, Text, useTheme } from '@anthropic/ink' +import { Box, color, Link, Text, useTheme } from '../../ink.js' import { useKeybinding } from '../../keybindings/useKeybinding.js' import { AuthenticationCancelledError, @@ -10,7 +10,9 @@ import { import { capitalize } from '../../utils/stringUtils.js' import { ConfigurableShortcutHint } from '../ConfigurableShortcutHint.js' import { Select } from '../CustomSelect/index.js' -import { Byline, Dialog, KeyboardShortcutHint } from '@anthropic/ink' +import { Byline } from '../design-system/Byline.js' +import { Dialog } from '../design-system/Dialog.js' +import { KeyboardShortcutHint } from '../design-system/KeyboardShortcutHint.js' import { Spinner } from '../Spinner.js' import type { AgentMcpServerInfo } from './types.js' diff --git a/src/components/mcp/MCPListPanel.tsx b/src/components/mcp/MCPListPanel.tsx index 365599284..af93c5538 100644 --- a/src/components/mcp/MCPListPanel.tsx +++ b/src/components/mcp/MCPListPanel.tsx @@ -1,14 +1,16 @@ import figures from 'figures' import React, { useCallback, useState } from 'react' import type { CommandResultDisplay } from '../../commands.js' -import { Box, color, Link, Text, useTheme } from '@anthropic/ink' +import { Box, color, Link, Text, useTheme } from '../../ink.js' import { useKeybindings } from '../../keybindings/useKeybinding.js' import type { ConfigScope } from '../../services/mcp/types.js' import { describeMcpConfigFilePath } from '../../services/mcp/utils.js' import { isDebugMode } from '../../utils/debug.js' import { plural } from '../../utils/stringUtils.js' import { ConfigurableShortcutHint } from '../ConfigurableShortcutHint.js' -import { Byline, Dialog, KeyboardShortcutHint } from '@anthropic/ink' +import { Byline } from '../design-system/Byline.js' +import { Dialog } from '../design-system/Dialog.js' +import { KeyboardShortcutHint } from '../design-system/KeyboardShortcutHint.js' import { McpParsingWarnings } from './McpParsingWarnings.js' import type { AgentMcpServerInfo, ServerInfo } from './types.js' diff --git a/src/components/mcp/MCPReconnect.tsx b/src/components/mcp/MCPReconnect.tsx index 496edaf2f..209c80541 100644 --- a/src/components/mcp/MCPReconnect.tsx +++ b/src/components/mcp/MCPReconnect.tsx @@ -1,7 +1,7 @@ import figures from 'figures' import React, { useEffect, useState } from 'react' import type { CommandResultDisplay } from '../../commands.js' -import { Box, color, Text, useTheme } from '@anthropic/ink' +import { Box, color, Text, useTheme } from '../../ink.js' import { useMcpReconnect } from '../../services/mcp/MCPConnectionManager.js' import { useAppStateStore } from '../../state/AppState.js' import { Spinner } from '../Spinner.js' diff --git a/src/components/mcp/MCPRemoteServerMenu.tsx b/src/components/mcp/MCPRemoteServerMenu.tsx index 41665860e..0a8efb76d 100644 --- a/src/components/mcp/MCPRemoteServerMenu.tsx +++ b/src/components/mcp/MCPRemoteServerMenu.tsx @@ -8,9 +8,9 @@ import type { CommandResultDisplay } from '../../commands.js' import { getOauthConfig } from '../../constants/oauth.js' import { useExitOnCtrlCDWithKeybindings } from '../../hooks/useExitOnCtrlCDWithKeybindings.js' import { useTerminalSize } from '../../hooks/useTerminalSize.js' -import { setClipboard } from '@anthropic/ink' +import { setClipboard } from '../../ink/termio/osc.js' // eslint-disable-next-line custom-rules/prefer-use-keybindings -- raw j/k/arrow menu navigation -import { Box, color, Link, Text, useInput, useTheme } from '@anthropic/ink' +import { Box, color, Link, Text, useInput, useTheme } from '../../ink.js' import { useKeybinding } from '../../keybindings/useKeybinding.js' import { AuthenticationCancelledError, @@ -37,7 +37,8 @@ import { logMCPDebug } from '../../utils/log.js' import { capitalize } from '../../utils/stringUtils.js' import { ConfigurableShortcutHint } from '../ConfigurableShortcutHint.js' import { Select } from '../CustomSelect/index.js' -import { Byline, KeyboardShortcutHint } from '@anthropic/ink' +import { Byline } from '../design-system/Byline.js' +import { KeyboardShortcutHint } from '../design-system/KeyboardShortcutHint.js' import { Spinner } from '../Spinner.js' import TextInput from '../TextInput.js' import { CapabilitiesSection } from './CapabilitiesSection.js' diff --git a/src/components/mcp/MCPStdioServerMenu.tsx b/src/components/mcp/MCPStdioServerMenu.tsx index 4132cdfe7..7caba350e 100644 --- a/src/components/mcp/MCPStdioServerMenu.tsx +++ b/src/components/mcp/MCPStdioServerMenu.tsx @@ -2,7 +2,7 @@ import figures from 'figures' import React, { useState } from 'react' import type { CommandResultDisplay } from '../../commands.js' import { useExitOnCtrlCDWithKeybindings } from '../../hooks/useExitOnCtrlCDWithKeybindings.js' -import { Box, color, Text, useTheme } from '@anthropic/ink' +import { Box, color, Text, useTheme } from '../../ink.js' import { getMcpConfigByName } from '../../services/mcp/config.js' import { useMcpReconnect, @@ -17,7 +17,8 @@ import { errorMessage } from '../../utils/errors.js' import { capitalize } from '../../utils/stringUtils.js' import { ConfigurableShortcutHint } from '../ConfigurableShortcutHint.js' import { Select } from '../CustomSelect/index.js' -import { Byline, KeyboardShortcutHint } from '@anthropic/ink' +import { Byline } from '../design-system/Byline.js' +import { KeyboardShortcutHint } from '../design-system/KeyboardShortcutHint.js' import { Spinner } from '../Spinner.js' import { CapabilitiesSection } from './CapabilitiesSection.js' import type { StdioServerInfo } from './types.js' diff --git a/src/components/mcp/MCPToolDetailView.tsx b/src/components/mcp/MCPToolDetailView.tsx index 6b0b60173..b1ccb4d73 100644 --- a/src/components/mcp/MCPToolDetailView.tsx +++ b/src/components/mcp/MCPToolDetailView.tsx @@ -1,12 +1,12 @@ import React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { extractMcpToolDisplayName, getMcpDisplayName, } from '../../services/mcp/mcpStringUtils.js' import type { Tool } from '../../Tool.js' import { ConfigurableShortcutHint } from '../ConfigurableShortcutHint.js' -import { Dialog } from '@anthropic/ink' +import { Dialog } from '../design-system/Dialog.js' import type { ServerInfo } from './types.js' type Props = { diff --git a/src/components/mcp/MCPToolListView.tsx b/src/components/mcp/MCPToolListView.tsx index ad3eb3695..923791187 100644 --- a/src/components/mcp/MCPToolListView.tsx +++ b/src/components/mcp/MCPToolListView.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { Text } from '@anthropic/ink' +import { Text } from '../../ink.js' import { extractMcpToolDisplayName, getMcpDisplayName, @@ -10,7 +10,9 @@ import type { Tool } from '../../Tool.js' import { plural } from '../../utils/stringUtils.js' import { ConfigurableShortcutHint } from '../ConfigurableShortcutHint.js' import { Select } from '../CustomSelect/index.js' -import { Byline, Dialog, KeyboardShortcutHint } from '@anthropic/ink' +import { Byline } from '../design-system/Byline.js' +import { Dialog } from '../design-system/Dialog.js' +import { KeyboardShortcutHint } from '../design-system/KeyboardShortcutHint.js' import type { ServerInfo } from './types.js' type Props = { diff --git a/src/components/mcp/McpParsingWarnings.tsx b/src/components/mcp/McpParsingWarnings.tsx index c50946a91..49e8353b9 100644 --- a/src/components/mcp/McpParsingWarnings.tsx +++ b/src/components/mcp/McpParsingWarnings.tsx @@ -6,7 +6,7 @@ import { getScopeLabel, } from 'src/services/mcp/utils.js' import type { ValidationError } from 'src/utils/settings/validation.js' -import { Box, Link, Text } from '@anthropic/ink' +import { Box, Link, Text } from '../../ink.js' function McpConfigErrorSection({ scope, diff --git a/src/components/memory/MemoryFileSelector.tsx b/src/components/memory/MemoryFileSelector.tsx index fcd5834c6..2e2bdf623 100644 --- a/src/components/memory/MemoryFileSelector.tsx +++ b/src/components/memory/MemoryFileSelector.tsx @@ -6,7 +6,7 @@ import * as React from 'react' import { use, useEffect, useState } from 'react' import { getOriginalCwd } from '../../bootstrap/state.js' import { useExitOnCtrlCDWithKeybindings } from '../../hooks/useExitOnCtrlCDWithKeybindings.js' -import { Box, Text, ListItem } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { useKeybinding } from '../../keybindings/useKeybinding.js' import { getAutoMemPath, isAutoMemoryEnabled } from '../../memdir/paths.js' import { logEvent } from '../../services/analytics/index.js' @@ -22,6 +22,7 @@ import { formatRelativeTimeAgo } from '../../utils/format.js' import { projectIsInGitRepo } from '../../utils/memory/versions.js' import { updateSettingsForSource } from '../../utils/settings/settings.js' import { Select } from '../CustomSelect/index.js' +import { ListItem } from '../design-system/ListItem.js' /* eslint-disable @typescript-eslint/no-require-imports */ const teamMemPaths = feature('TEAMMEM') diff --git a/src/components/memory/MemoryUpdateNotification.tsx b/src/components/memory/MemoryUpdateNotification.tsx index 5e2db19b7..890a567b0 100644 --- a/src/components/memory/MemoryUpdateNotification.tsx +++ b/src/components/memory/MemoryUpdateNotification.tsx @@ -1,7 +1,7 @@ import { homedir } from 'os' import { relative } from 'path' import React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { getCwd } from '../../utils/cwd.js' export function getRelativeMemoryPath(path: string): string { diff --git a/src/components/messageActions.tsx b/src/components/messageActions.tsx index 2ecc0ab5a..3e368c306 100644 --- a/src/components/messageActions.tsx +++ b/src/components/messageActions.tsx @@ -1,7 +1,7 @@ import figures from 'figures' import type { RefObject } from 'react' import React, { useCallback, useMemo, useRef } from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import { useKeybindings } from '../keybindings/useKeybinding.js' import { logEvent } from '../services/analytics/index.js' import type { diff --git a/src/components/messages/AdvisorMessage.tsx b/src/components/messages/AdvisorMessage.tsx index 890e52405..4a77fe7ca 100644 --- a/src/components/messages/AdvisorMessage.tsx +++ b/src/components/messages/AdvisorMessage.tsx @@ -1,6 +1,6 @@ import figures from 'figures' import React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import type { AdvisorBlock } from '../../utils/advisor.js' import { renderModelName } from '../../utils/model/model.js' import { jsonStringify } from '../../utils/slowOperations.js' diff --git a/src/components/messages/AssistantRedactedThinkingMessage.tsx b/src/components/messages/AssistantRedactedThinkingMessage.tsx index 4d825701d..eb0f66d35 100644 --- a/src/components/messages/AssistantRedactedThinkingMessage.tsx +++ b/src/components/messages/AssistantRedactedThinkingMessage.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' type Props = { addMargin: boolean diff --git a/src/components/messages/AssistantTextMessage.tsx b/src/components/messages/AssistantTextMessage.tsx index 56ffa054e..005d2481e 100644 --- a/src/components/messages/AssistantTextMessage.tsx +++ b/src/components/messages/AssistantTextMessage.tsx @@ -3,7 +3,7 @@ import React, { useContext } from 'react' import { ERROR_MESSAGE_USER_ABORT } from 'src/services/compact/compact.js' import { isRateLimitErrorMessage } from 'src/services/rateLimitMessages.js' import { BLACK_CIRCLE } from '../../constants/figures.js' -import { Box, NoSelect, Text } from '@anthropic/ink' +import { Box, NoSelect, Text } from '../../ink.js' import { API_ERROR_MESSAGE_PREFIX, API_TIMEOUT_ERROR_MESSAGE, diff --git a/src/components/messages/AssistantThinkingMessage.tsx b/src/components/messages/AssistantThinkingMessage.tsx index 979f75637..2fc88512d 100644 --- a/src/components/messages/AssistantThinkingMessage.tsx +++ b/src/components/messages/AssistantThinkingMessage.tsx @@ -3,7 +3,7 @@ import type { ThinkingBlockParam, } from '@anthropic-ai/sdk/resources/index.mjs' import React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { CtrlOToExpand } from '../CtrlOToExpand.js' import { Markdown } from '../Markdown.js' diff --git a/src/components/messages/AssistantToolUseMessage.tsx b/src/components/messages/AssistantToolUseMessage.tsx index 8f2dd965b..65a92aad6 100644 --- a/src/components/messages/AssistantToolUseMessage.tsx +++ b/src/components/messages/AssistantToolUseMessage.tsx @@ -4,7 +4,8 @@ import { useTerminalSize } from 'src/hooks/useTerminalSize.js' import type { ThemeName } from 'src/utils/theme.js' import type { Command } from '../../commands.js' import { BLACK_CIRCLE } from '../../constants/figures.js' -import { Box, Text, stringWidth, useTheme } from '@anthropic/ink' +import { stringWidth } from '../../ink/stringWidth.js' +import { Box, Text, useTheme } from '../../ink.js' import { useAppStateMaybeOutsideOfProvider } from '../../state/AppState.js' import { findToolByName, diff --git a/src/components/messages/AttachmentMessage.tsx b/src/components/messages/AttachmentMessage.tsx index 77363371d..51f9ea67d 100644 --- a/src/components/messages/AttachmentMessage.tsx +++ b/src/components/messages/AttachmentMessage.tsx @@ -1,8 +1,6 @@ // biome-ignore-all assist/source/organizeImports: ANT-ONLY import markers must not be reordered import React, { useMemo } from 'react' -import { Ansi, Box, Text } from '@anthropic/ink' -import { FilePathLink } from '../FilePathLink.js' -import { toInkColor } from '../../utils/ink.js' +import { Ansi, Box, Text } from '../../ink.js' import type { Attachment } from 'src/utils/attachments.js' import type { NullRenderingAttachmentType } from './nullRenderingAttachments.js' import { useAppState } from '../../state/AppState.js' @@ -15,7 +13,7 @@ import { DiagnosticsDisplay } from '../DiagnosticsDisplay.js' import { getContentText } from 'src/utils/messages.js' import type { Theme } from 'src/utils/theme.js' import { UserImageMessage } from './UserImageMessage.js' - +import { toInkColor } from '../../utils/ink.js' import { jsonParse } from '../../utils/slowOperations.js' import { plural } from '../../utils/stringUtils.js' import { isEnvTruthy } from '../../utils/envUtils.js' @@ -28,7 +26,7 @@ import { BLACK_CIRCLE } from '../../constants/figures.js' import { TeammateMessageContent } from './UserTeammateMessage.js' import { isShutdownApproved } from '../../utils/teammateMailbox.js' import { CtrlOToExpand } from '../CtrlOToExpand.js' - +import { FilePathLink } from '../FilePathLink.js' import { feature } from 'bun:bundle' import { useSelectedMessageBg } from '../messageActions.js' diff --git a/src/components/messages/CollapsedReadSearchContent.tsx b/src/components/messages/CollapsedReadSearchContent.tsx index 5a0f1eb04..d8df34f69 100644 --- a/src/components/messages/CollapsedReadSearchContent.tsx +++ b/src/components/messages/CollapsedReadSearchContent.tsx @@ -2,7 +2,7 @@ import { feature } from 'bun:bundle' import { basename } from 'path' import React, { useRef } from 'react' import { useMinDisplayTime } from '../../hooks/useMinDisplayTime.js' -import { Ansi, Box, Text, useTheme } from '@anthropic/ink' +import { Ansi, Box, Text, useTheme } from '../../ink.js' import { findToolByName, type Tools } from '../../Tool.js' import { getReplPrimitiveTools } from '../../tools/REPLTool/primitiveTools.js' import type { diff --git a/src/components/messages/CompactBoundaryMessage.tsx b/src/components/messages/CompactBoundaryMessage.tsx index 54033b23e..7c4e87af1 100644 --- a/src/components/messages/CompactBoundaryMessage.tsx +++ b/src/components/messages/CompactBoundaryMessage.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { useShortcutDisplay } from '../../keybindings/useShortcutDisplay.js' export function CompactBoundaryMessage(): React.ReactNode { diff --git a/src/components/messages/HighlightedThinkingText.tsx b/src/components/messages/HighlightedThinkingText.tsx index 14c61ff09..1b4fd0c3c 100644 --- a/src/components/messages/HighlightedThinkingText.tsx +++ b/src/components/messages/HighlightedThinkingText.tsx @@ -2,7 +2,7 @@ import figures from 'figures' import * as React from 'react' import { useContext } from 'react' import { useQueuedMessage } from '../../context/QueuedMessageContext.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { formatBriefTimestamp } from '../../utils/formatBriefTimestamp.js' import { findThinkingTriggerPositions, diff --git a/src/components/messages/HookProgressMessage.tsx b/src/components/messages/HookProgressMessage.tsx index 68b1bd2d1..61bfddf96 100644 --- a/src/components/messages/HookProgressMessage.tsx +++ b/src/components/messages/HookProgressMessage.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js' import type { buildMessageLookups } from 'src/utils/messages.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { MessageResponse } from '../MessageResponse.js' type Props = { diff --git a/src/components/messages/PlanApprovalMessage.tsx b/src/components/messages/PlanApprovalMessage.tsx index bb0a582dd..a7fbced71 100644 --- a/src/components/messages/PlanApprovalMessage.tsx +++ b/src/components/messages/PlanApprovalMessage.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import { Markdown } from '../../components/Markdown.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { jsonParse } from '../../utils/slowOperations.js' import { type IdleNotificationMessage, diff --git a/src/components/messages/RateLimitMessage.tsx b/src/components/messages/RateLimitMessage.tsx index 8ec18da7e..c9a42815b 100644 --- a/src/components/messages/RateLimitMessage.tsx +++ b/src/components/messages/RateLimitMessage.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useMemo, useState } from 'react' import { extraUsage } from 'src/commands/extra-usage/index.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from 'src/ink.js' import { useClaudeAiLimits } from 'src/services/claudeAiLimitsHook.js' import { shouldProcessMockLimits } from 'src/services/rateLimitMocking.js' // Used for /mock-limits command import { diff --git a/src/components/messages/ShutdownMessage.tsx b/src/components/messages/ShutdownMessage.tsx index 6f0dcbd97..82e0d59e1 100644 --- a/src/components/messages/ShutdownMessage.tsx +++ b/src/components/messages/ShutdownMessage.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { isShutdownApproved, isShutdownRejected, diff --git a/src/components/messages/SystemAPIErrorMessage.tsx b/src/components/messages/SystemAPIErrorMessage.tsx index 30c5a2495..c87dec717 100644 --- a/src/components/messages/SystemAPIErrorMessage.tsx +++ b/src/components/messages/SystemAPIErrorMessage.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import { useState } from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from 'src/ink.js' import { formatAPIError } from 'src/services/api/errorUtils.js' import type { SystemAPIErrorMessage } from 'src/types/message.js' import { useInterval } from 'usehooks-ts' diff --git a/src/components/messages/SystemTextMessage.tsx b/src/components/messages/SystemTextMessage.tsx index a592c79de..7d05f054a 100644 --- a/src/components/messages/SystemTextMessage.tsx +++ b/src/components/messages/SystemTextMessage.tsx @@ -1,6 +1,5 @@ // biome-ignore-all assist/source/organizeImports: ANT-ONLY import markers must not be reordered -import { Box, Link, Text, type TextProps } from '@anthropic/ink' -import { FilePathLink } from '../FilePathLink.js' +import { Box, Text, type TextProps } from '../../ink.js' import { feature } from 'bun:bundle' import * as React from 'react' import { useState } from 'react' @@ -13,7 +12,7 @@ import { import figures from 'figures' import { basename } from 'path' import { MessageResponse } from '../MessageResponse.js' - +import { FilePathLink } from '../FilePathLink.js' import { openPath } from '../../utils/browser.js' /* eslint-disable @typescript-eslint/no-require-imports */ const teamMemSaved = feature('TEAMMEM') @@ -37,6 +36,7 @@ import { formatSecondsShort, } from '../../utils/format.js' import { getGlobalConfig } from '../../utils/config.js' +import Link from '../../ink/components/Link.js' import ThemedText from '../design-system/ThemedText.js' import { CtrlOToExpand } from '../CtrlOToExpand.js' import { useAppStateStore } from '../../state/AppState.js' @@ -108,6 +108,7 @@ export function SystemTextMessage({ return null } + if (message.subtype === 'bridge_status') { return } diff --git a/src/components/messages/TaskAssignmentMessage.tsx b/src/components/messages/TaskAssignmentMessage.tsx index 146068a4f..1f7797873 100644 --- a/src/components/messages/TaskAssignmentMessage.tsx +++ b/src/components/messages/TaskAssignmentMessage.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { isTaskAssignment, type TaskAssignmentMessage, diff --git a/src/components/messages/UserAgentNotificationMessage.tsx b/src/components/messages/UserAgentNotificationMessage.tsx index 8bf906bea..7e19c34d7 100644 --- a/src/components/messages/UserAgentNotificationMessage.tsx +++ b/src/components/messages/UserAgentNotificationMessage.tsx @@ -1,7 +1,7 @@ import type { TextBlockParam } from '@anthropic-ai/sdk/resources/index.mjs' import * as React from 'react' import { BLACK_CIRCLE } from '../../constants/figures.js' -import { Box, Text, type TextProps } from '@anthropic/ink' +import { Box, Text, type TextProps } from '../../ink.js' import { extractTag } from '../../utils/messages.js' type Props = { diff --git a/src/components/messages/UserBashInputMessage.tsx b/src/components/messages/UserBashInputMessage.tsx index 42e40df75..c78fafea1 100644 --- a/src/components/messages/UserBashInputMessage.tsx +++ b/src/components/messages/UserBashInputMessage.tsx @@ -1,6 +1,6 @@ import type { TextBlockParam } from '@anthropic-ai/sdk/resources/index.mjs' import * as React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { extractTag } from '../../utils/messages.js' type Props = { diff --git a/src/components/messages/UserChannelMessage.tsx b/src/components/messages/UserChannelMessage.tsx index a37f05c8c..8e7101b1a 100644 --- a/src/components/messages/UserChannelMessage.tsx +++ b/src/components/messages/UserChannelMessage.tsx @@ -2,7 +2,7 @@ import type { TextBlockParam } from '@anthropic-ai/sdk/resources/index.mjs' import * as React from 'react' import { CHANNEL_ARROW } from '../../constants/figures.js' import { CHANNEL_TAG } from '../../constants/xml.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { truncateToWidth } from '../../utils/format.js' type Props = { diff --git a/src/components/messages/UserCommandMessage.tsx b/src/components/messages/UserCommandMessage.tsx index 1c9502c32..31f6b2871 100644 --- a/src/components/messages/UserCommandMessage.tsx +++ b/src/components/messages/UserCommandMessage.tsx @@ -2,7 +2,7 @@ import type { TextBlockParam } from '@anthropic-ai/sdk/resources/index.mjs' import figures from 'figures' import * as React from 'react' import { COMMAND_MESSAGE_TAG } from '../../constants/xml.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { extractTag } from '../../utils/messages.js' type Props = { diff --git a/src/components/messages/UserImageMessage.tsx b/src/components/messages/UserImageMessage.tsx index ca960d59c..3f542dfb6 100644 --- a/src/components/messages/UserImageMessage.tsx +++ b/src/components/messages/UserImageMessage.tsx @@ -1,6 +1,8 @@ import * as React from 'react' import { pathToFileURL } from 'url' -import { Box, Link, supportsHyperlinks, Text } from '@anthropic/ink' +import Link from '../../ink/components/Link.js' +import { supportsHyperlinks } from '../../ink/supports-hyperlinks.js' +import { Box, Text } from '../../ink.js' import { getStoredImagePath } from '../../utils/imageStore.js' import { MessageResponse } from '../MessageResponse.js' diff --git a/src/components/messages/UserLocalCommandOutputMessage.tsx b/src/components/messages/UserLocalCommandOutputMessage.tsx index 3467686b4..b1c95616a 100644 --- a/src/components/messages/UserLocalCommandOutputMessage.tsx +++ b/src/components/messages/UserLocalCommandOutputMessage.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import { DIAMOND_FILLED, DIAMOND_OPEN } from '../../constants/figures.js' import { NO_CONTENT_MESSAGE } from '../../constants/messages.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { extractTag } from '../../utils/messages.js' import { Markdown } from '../Markdown.js' import { MessageResponse } from '../MessageResponse.js' diff --git a/src/components/messages/UserMemoryInputMessage.tsx b/src/components/messages/UserMemoryInputMessage.tsx index cbdfdcd5d..25a8d7a1c 100644 --- a/src/components/messages/UserMemoryInputMessage.tsx +++ b/src/components/messages/UserMemoryInputMessage.tsx @@ -1,7 +1,7 @@ import sample from 'lodash-es/sample.js' import * as React from 'react' import { useMemo } from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { extractTag } from '../../utils/messages.js' import { MessageResponse } from '../MessageResponse.js' diff --git a/src/components/messages/UserPlanMessage.tsx b/src/components/messages/UserPlanMessage.tsx index 30f2b369b..5ef8fa89a 100644 --- a/src/components/messages/UserPlanMessage.tsx +++ b/src/components/messages/UserPlanMessage.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { Markdown } from '../Markdown.js' type Props = { diff --git a/src/components/messages/UserPromptMessage.tsx b/src/components/messages/UserPromptMessage.tsx index 00f201368..090cac272 100644 --- a/src/components/messages/UserPromptMessage.tsx +++ b/src/components/messages/UserPromptMessage.tsx @@ -2,7 +2,7 @@ import { feature } from 'bun:bundle' import type { TextBlockParam } from '@anthropic-ai/sdk/resources/index.mjs' import React, { useContext, useMemo } from 'react' import { getKairosActive, getUserMsgOptIn } from '../../bootstrap/state.js' -import { Box } from '@anthropic/ink' +import { Box } from '../../ink.js' import { getFeatureValue_CACHED_MAY_BE_STALE } from '../../services/analytics/growthbook.js' import { useAppState } from '../../state/AppState.js' import { isEnvTruthy } from '../../utils/envUtils.js' diff --git a/src/components/messages/UserResourceUpdateMessage.tsx b/src/components/messages/UserResourceUpdateMessage.tsx index ec0f5a2ac..ce1f4f5d5 100644 --- a/src/components/messages/UserResourceUpdateMessage.tsx +++ b/src/components/messages/UserResourceUpdateMessage.tsx @@ -1,7 +1,7 @@ import type { TextBlockParam } from '@anthropic-ai/sdk/resources/index.mjs' import * as React from 'react' import { REFRESH_ARROW } from '../../constants/figures.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' type Props = { addMargin: boolean diff --git a/src/components/messages/UserTeammateMessage.tsx b/src/components/messages/UserTeammateMessage.tsx index e75256abc..4c174ff1c 100644 --- a/src/components/messages/UserTeammateMessage.tsx +++ b/src/components/messages/UserTeammateMessage.tsx @@ -2,9 +2,8 @@ import type { TextBlockParam } from '@anthropic-ai/sdk/resources/index.mjs' import figures from 'figures' import * as React from 'react' import { TEAMMATE_MESSAGE_TAG } from '../../constants/xml.js' -import { Ansi, Box, Text, type TextProps } from '@anthropic/ink' +import { Ansi, Box, Text, type TextProps } from '../../ink.js' import { toInkColor } from '../../utils/ink.js' - import { jsonParse } from '../../utils/slowOperations.js' import { isShutdownApproved } from '../../utils/teammateMailbox.js' import { MessageResponse } from '../MessageResponse.js' diff --git a/src/components/messages/UserToolResultMessage/RejectedPlanMessage.tsx b/src/components/messages/UserToolResultMessage/RejectedPlanMessage.tsx index ec2130230..bee8d5c3a 100644 --- a/src/components/messages/UserToolResultMessage/RejectedPlanMessage.tsx +++ b/src/components/messages/UserToolResultMessage/RejectedPlanMessage.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import { Markdown } from 'src/components/Markdown.js' import { MessageResponse } from 'src/components/MessageResponse.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../../ink.js' type Props = { plan: string diff --git a/src/components/messages/UserToolResultMessage/RejectedToolUseMessage.tsx b/src/components/messages/UserToolResultMessage/RejectedToolUseMessage.tsx index 5764a4e17..b387b0fea 100644 --- a/src/components/messages/UserToolResultMessage/RejectedToolUseMessage.tsx +++ b/src/components/messages/UserToolResultMessage/RejectedToolUseMessage.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { Text } from '@anthropic/ink' +import { Text } from '../../../ink.js' import { MessageResponse } from '../../MessageResponse.js' export function RejectedToolUseMessage(): React.ReactNode { diff --git a/src/components/messages/UserToolResultMessage/UserToolErrorMessage.tsx b/src/components/messages/UserToolResultMessage/UserToolErrorMessage.tsx index a178e8e34..33249d591 100644 --- a/src/components/messages/UserToolResultMessage/UserToolErrorMessage.tsx +++ b/src/components/messages/UserToolResultMessage/UserToolErrorMessage.tsx @@ -2,7 +2,7 @@ import { feature } from 'bun:bundle' import type { ToolResultBlockParam } from '@anthropic-ai/sdk/resources/index.mjs' import * as React from 'react' import { BULLET_OPERATOR } from '../../../constants/figures.js' -import { Text } from '@anthropic/ink' +import { Text } from '../../../ink.js' import { filterToolProgressMessages, type Tool, diff --git a/src/components/messages/UserToolResultMessage/UserToolRejectMessage.tsx b/src/components/messages/UserToolResultMessage/UserToolRejectMessage.tsx index c53d81e6d..c1a37ef4e 100644 --- a/src/components/messages/UserToolResultMessage/UserToolRejectMessage.tsx +++ b/src/components/messages/UserToolResultMessage/UserToolRejectMessage.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import { useTerminalSize } from '../../../hooks/useTerminalSize.js' -import { useTheme } from '@anthropic/ink' +import { useTheme } from '../../../ink.js' import { filterToolProgressMessages, type Tool, diff --git a/src/components/messages/UserToolResultMessage/UserToolSuccessMessage.tsx b/src/components/messages/UserToolResultMessage/UserToolSuccessMessage.tsx index ffb8e23d5..931e0df3e 100644 --- a/src/components/messages/UserToolResultMessage/UserToolSuccessMessage.tsx +++ b/src/components/messages/UserToolResultMessage/UserToolSuccessMessage.tsx @@ -2,7 +2,7 @@ import { feature } from 'bun:bundle' import figures from 'figures' import * as React from 'react' import { SentryErrorBoundary } from 'src/components/SentryErrorBoundary.js' -import { Box, Text, useTheme } from '@anthropic/ink' +import { Box, Text, useTheme } from '../../../ink.js' import { useAppState } from '../../../state/AppState.js' import { filterToolProgressMessages, diff --git a/src/components/messages/teamMemCollapsed.tsx b/src/components/messages/teamMemCollapsed.tsx index eaa9219ab..63fcdaf0e 100644 --- a/src/components/messages/teamMemCollapsed.tsx +++ b/src/components/messages/teamMemCollapsed.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { Text } from '@anthropic/ink' +import { Text } from '../../ink.js' import type { CollapsedReadSearchGroup } from '../../types/message.js' /** diff --git a/src/components/permissions/AskUserQuestionPermissionRequest/AskUserQuestionPermissionRequest.tsx b/src/components/permissions/AskUserQuestionPermissionRequest/AskUserQuestionPermissionRequest.tsx index 562c6653c..3768dbd73 100644 --- a/src/components/permissions/AskUserQuestionPermissionRequest/AskUserQuestionPermissionRequest.tsx +++ b/src/components/permissions/AskUserQuestionPermissionRequest/AskUserQuestionPermissionRequest.tsx @@ -12,7 +12,8 @@ import React, { } from 'react' import { useSettings } from '../../../hooks/useSettings.js' import { useTerminalSize } from '../../../hooks/useTerminalSize.js' -import { stringWidth, useTheme } from '@anthropic/ink' +import { stringWidth } from '../../../ink/stringWidth.js' +import { useTheme } from '../../../ink.js' import { useKeybindings } from '../../../keybindings/useKeybinding.js' import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, diff --git a/src/components/permissions/AskUserQuestionPermissionRequest/PreviewBox.tsx b/src/components/permissions/AskUserQuestionPermissionRequest/PreviewBox.tsx index 686f2f4c9..7b4fd6149 100644 --- a/src/components/permissions/AskUserQuestionPermissionRequest/PreviewBox.tsx +++ b/src/components/permissions/AskUserQuestionPermissionRequest/PreviewBox.tsx @@ -1,7 +1,8 @@ import React, { Suspense, use, useMemo } from 'react' import { useSettings } from '../../../hooks/useSettings.js' import { useTerminalSize } from '../../../hooks/useTerminalSize.js' -import { Ansi, Box, Text, stringWidth, useTheme } from '@anthropic/ink' +import { stringWidth } from '../../../ink/stringWidth.js' +import { Ansi, Box, Text, useTheme } from '../../../ink.js' import { type CliHighlight, getCliHighlightPromise, diff --git a/src/components/permissions/AskUserQuestionPermissionRequest/PreviewQuestionView.tsx b/src/components/permissions/AskUserQuestionPermissionRequest/PreviewQuestionView.tsx index 6e80ce25f..78289da5f 100644 --- a/src/components/permissions/AskUserQuestionPermissionRequest/PreviewQuestionView.tsx +++ b/src/components/permissions/AskUserQuestionPermissionRequest/PreviewQuestionView.tsx @@ -1,7 +1,8 @@ import figures from 'figures' import React, { useCallback, useMemo, useRef, useState } from 'react' import { useTerminalSize } from '../../../hooks/useTerminalSize.js' -import { type KeyboardEvent, Box, Text } from '@anthropic/ink' +import type { KeyboardEvent } from '../../../ink/events/keyboard-event.js' +import { Box, Text } from '../../../ink.js' import { useKeybinding, useKeybindings, @@ -11,7 +12,7 @@ import type { Question } from '../../../tools/AskUserQuestionTool/AskUserQuestio import { getExternalEditor } from '../../../utils/editor.js' import { toIDEDisplayName } from '../../../utils/ide.js' import { editPromptInEditor } from '../../../utils/promptEditor.js' -import { Divider } from '@anthropic/ink' +import { Divider } from '../../design-system/Divider.js' import TextInput from '../../TextInput.js' import { PermissionRequestTitle } from '../PermissionRequestTitle.js' import { PreviewBox } from './PreviewBox.js' diff --git a/src/components/permissions/AskUserQuestionPermissionRequest/QuestionNavigationBar.tsx b/src/components/permissions/AskUserQuestionPermissionRequest/QuestionNavigationBar.tsx index 082ba086c..3440e9daf 100644 --- a/src/components/permissions/AskUserQuestionPermissionRequest/QuestionNavigationBar.tsx +++ b/src/components/permissions/AskUserQuestionPermissionRequest/QuestionNavigationBar.tsx @@ -1,7 +1,8 @@ import figures from 'figures' import React, { useMemo } from 'react' import { useTerminalSize } from '../../../hooks/useTerminalSize.js' -import { Box, Text, stringWidth } from '@anthropic/ink' +import { stringWidth } from '../../../ink/stringWidth.js' +import { Box, Text } from '../../../ink.js' import type { Question } from '../../../tools/AskUserQuestionTool/AskUserQuestionTool.js' import { truncateToWidth } from '../../../utils/format.js' diff --git a/src/components/permissions/AskUserQuestionPermissionRequest/QuestionView.tsx b/src/components/permissions/AskUserQuestionPermissionRequest/QuestionView.tsx index ec4aa3ad7..ef45238ab 100644 --- a/src/components/permissions/AskUserQuestionPermissionRequest/QuestionView.tsx +++ b/src/components/permissions/AskUserQuestionPermissionRequest/QuestionView.tsx @@ -1,6 +1,7 @@ import figures from 'figures' import React, { useCallback, useState } from 'react' -import { type KeyboardEvent, Box, Text } from '@anthropic/ink' +import type { KeyboardEvent } from '../../../ink/events/keyboard-event.js' +import { Box, Text } from '../../../ink.js' import { useAppState } from '../../../state/AppState.js' import type { Question, @@ -16,9 +17,8 @@ import { Select, SelectMulti, } from '../../CustomSelect/index.js' -import { Divider } from '@anthropic/ink' +import { Divider } from '../../design-system/Divider.js' import { FilePathLink } from '../../FilePathLink.js' - import { PermissionRequestTitle } from '../PermissionRequestTitle.js' import { PreviewQuestionView } from './PreviewQuestionView.js' import { QuestionNavigationBar } from './QuestionNavigationBar.js' diff --git a/src/components/permissions/AskUserQuestionPermissionRequest/SubmitQuestionsView.tsx b/src/components/permissions/AskUserQuestionPermissionRequest/SubmitQuestionsView.tsx index 37e7e832c..b17a26c2a 100644 --- a/src/components/permissions/AskUserQuestionPermissionRequest/SubmitQuestionsView.tsx +++ b/src/components/permissions/AskUserQuestionPermissionRequest/SubmitQuestionsView.tsx @@ -1,10 +1,10 @@ import figures from 'figures' import React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../../ink.js' import type { Question } from '../../../tools/AskUserQuestionTool/AskUserQuestionTool.js' import type { PermissionDecision } from '../../../utils/permissions/PermissionResult.js' import { Select } from '../../CustomSelect/index.js' -import { Divider } from '@anthropic/ink' +import { Divider } from '../../design-system/Divider.js' import { PermissionRequestTitle } from '../PermissionRequestTitle.js' import { PermissionRuleExplanation } from '../PermissionRuleExplanation.js' import { QuestionNavigationBar } from './QuestionNavigationBar.js' diff --git a/src/components/permissions/BashPermissionRequest/BashPermissionRequest.tsx b/src/components/permissions/BashPermissionRequest/BashPermissionRequest.tsx index fb2c06da9..1eb1ffffe 100644 --- a/src/components/permissions/BashPermissionRequest/BashPermissionRequest.tsx +++ b/src/components/permissions/BashPermissionRequest/BashPermissionRequest.tsx @@ -1,7 +1,7 @@ import { feature } from 'bun:bundle' import figures from 'figures' import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' -import { Box, Text, useTheme } from '@anthropic/ink' +import { Box, Text, useTheme } from '../../../ink.js' import { useKeybinding } from '../../../keybindings/useKeybinding.js' import { getFeatureValue_CACHED_MAY_BE_STALE } from '../../../services/analytics/growthbook.js' import { diff --git a/src/components/permissions/ComputerUseApproval/ComputerUseApproval.tsx b/src/components/permissions/ComputerUseApproval/ComputerUseApproval.tsx index 6eecf1be3..c591082e4 100644 --- a/src/components/permissions/ComputerUseApproval/ComputerUseApproval.tsx +++ b/src/components/permissions/ComputerUseApproval/ComputerUseApproval.tsx @@ -7,12 +7,12 @@ import { DEFAULT_GRANT_FLAGS } from '@ant/computer-use-mcp/types' import figures from 'figures' import * as React from 'react' import { useMemo, useState } from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../../ink.js' import { execFileNoThrow } from '../../../utils/execFileNoThrow.js' import { plural } from '../../../utils/stringUtils.js' import type { OptionWithDescription } from '../../CustomSelect/select.js' import { Select } from '../../CustomSelect/select.js' -import { Dialog } from '@anthropic/ink' +import { Dialog } from '../../design-system/Dialog.js' type ComputerUseApprovalProps = { request: CuPermissionRequest diff --git a/src/components/permissions/EnterPlanModePermissionRequest/EnterPlanModePermissionRequest.tsx b/src/components/permissions/EnterPlanModePermissionRequest/EnterPlanModePermissionRequest.tsx index 9922f1680..4251891e0 100644 --- a/src/components/permissions/EnterPlanModePermissionRequest/EnterPlanModePermissionRequest.tsx +++ b/src/components/permissions/EnterPlanModePermissionRequest/EnterPlanModePermissionRequest.tsx @@ -1,6 +1,6 @@ import React from 'react' import { handlePlanModeTransition } from '../../../bootstrap/state.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../../ink.js' import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent, diff --git a/src/components/permissions/ExitPlanModePermissionRequest/ExitPlanModePermissionRequest.tsx b/src/components/permissions/ExitPlanModePermissionRequest/ExitPlanModePermissionRequest.tsx index 5a3ee5f1f..fddadaa7e 100644 --- a/src/components/permissions/ExitPlanModePermissionRequest/ExitPlanModePermissionRequest.tsx +++ b/src/components/permissions/ExitPlanModePermissionRequest/ExitPlanModePermissionRequest.tsx @@ -29,7 +29,8 @@ import { } from '../../../bootstrap/state.js' import { generateSessionName } from '../../../commands/rename/generateSessionName.js' import { launchUltraplan } from '../../../commands/ultraplan.js' -import { type KeyboardEvent, Box, Text } from '@anthropic/ink' +import type { KeyboardEvent } from '../../../ink/events/keyboard-event.js' +import { Box, Text } from '../../../ink.js' import type { AppState } from '../../../state/AppStateStore.js' import { AGENT_TOOL_NAME } from '../../../tools/AgentTool/constants.js' import { EXIT_PLAN_MODE_V2_TOOL_NAME } from '../../../tools/ExitPlanModeTool/constants.js' diff --git a/src/components/permissions/FallbackPermissionRequest.tsx b/src/components/permissions/FallbackPermissionRequest.tsx index 23075aa58..9b7fee994 100644 --- a/src/components/permissions/FallbackPermissionRequest.tsx +++ b/src/components/permissions/FallbackPermissionRequest.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useMemo } from 'react' import { getOriginalCwd } from '../../bootstrap/state.js' -import { Box, Text, useTheme } from '@anthropic/ink' +import { Box, Text, useTheme } from '../../ink.js' import { sanitizeToolNameForAnalytics } from '../../services/analytics/metadata.js' import { env } from '../../utils/env.js' import { shouldShowAlwaysAllowOptions } from '../../utils/permissions/permissionsLoader.js' diff --git a/src/components/permissions/FileEditPermissionRequest/FileEditPermissionRequest.tsx b/src/components/permissions/FileEditPermissionRequest/FileEditPermissionRequest.tsx index 9f8683017..d3bae2c17 100644 --- a/src/components/permissions/FileEditPermissionRequest/FileEditPermissionRequest.tsx +++ b/src/components/permissions/FileEditPermissionRequest/FileEditPermissionRequest.tsx @@ -3,7 +3,7 @@ import React from 'react' import { FileEditToolDiff } from 'src/components/FileEditToolDiff.js' import { getCwd } from 'src/utils/cwd.js' import type { z } from 'zod/v4' -import { Text } from '@anthropic/ink' +import { Text } from '../../../ink.js' import { FileEditTool } from '../../../tools/FileEditTool/FileEditTool.js' import { FilePermissionDialog } from '../FilePermissionDialog/FilePermissionDialog.js' import { diff --git a/src/components/permissions/FilePermissionDialog/FilePermissionDialog.tsx b/src/components/permissions/FilePermissionDialog/FilePermissionDialog.tsx index 58ac9b118..b645949dc 100644 --- a/src/components/permissions/FilePermissionDialog/FilePermissionDialog.tsx +++ b/src/components/permissions/FilePermissionDialog/FilePermissionDialog.tsx @@ -1,7 +1,7 @@ import { relative } from 'path' import React, { useMemo } from 'react' import { useDiffInIDE } from '../../../hooks/useDiffInIDE.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../../ink.js' import type { ToolUseContext } from '../../../Tool.js' import { getLanguageName } from '../../../utils/cliHighlight.js' import { getCwd } from '../../../utils/cwd.js' diff --git a/src/components/permissions/FilePermissionDialog/permissionOptions.tsx b/src/components/permissions/FilePermissionDialog/permissionOptions.tsx index 3709a6502..3a3507234 100644 --- a/src/components/permissions/FilePermissionDialog/permissionOptions.tsx +++ b/src/components/permissions/FilePermissionDialog/permissionOptions.tsx @@ -2,7 +2,7 @@ import { homedir } from 'os' import { basename, join, sep } from 'path' import React, { type ReactNode } from 'react' import { getOriginalCwd } from '../../../bootstrap/state.js' -import { Text } from '@anthropic/ink' +import { Text } from '../../../ink.js' import { getShortcutDisplay } from '../../../keybindings/shortcutFormat.js' import type { ToolPermissionContext } from '../../../Tool.js' import { expandPath, getDirectoryForPath } from '../../../utils/path.js' diff --git a/src/components/permissions/FileWritePermissionRequest/FileWritePermissionRequest.tsx b/src/components/permissions/FileWritePermissionRequest/FileWritePermissionRequest.tsx index 744673193..ce352858d 100644 --- a/src/components/permissions/FileWritePermissionRequest/FileWritePermissionRequest.tsx +++ b/src/components/permissions/FileWritePermissionRequest/FileWritePermissionRequest.tsx @@ -1,7 +1,7 @@ import { basename, relative } from 'path' import React, { useMemo } from 'react' import type { z } from 'zod/v4' -import { Text } from '@anthropic/ink' +import { Text } from '../../../ink.js' import { FileWriteTool } from '../../../tools/FileWriteTool/FileWriteTool.js' import { getCwd } from '../../../utils/cwd.js' import { isENOENT } from '../../../utils/errors.js' diff --git a/src/components/permissions/FileWritePermissionRequest/FileWriteToolDiff.tsx b/src/components/permissions/FileWritePermissionRequest/FileWriteToolDiff.tsx index 38084661f..36147ef03 100644 --- a/src/components/permissions/FileWritePermissionRequest/FileWriteToolDiff.tsx +++ b/src/components/permissions/FileWritePermissionRequest/FileWriteToolDiff.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import { useMemo } from 'react' import { useTerminalSize } from '../../../hooks/useTerminalSize.js' -import { Box, NoSelect, Text } from '@anthropic/ink' +import { Box, NoSelect, Text } from '../../../ink.js' import { intersperse } from '../../../utils/array.js' import { getPatchForDisplay } from '../../../utils/diff.js' import { HighlightedCode } from '../../HighlightedCode.js' diff --git a/src/components/permissions/FilesystemPermissionRequest/FilesystemPermissionRequest.tsx b/src/components/permissions/FilesystemPermissionRequest/FilesystemPermissionRequest.tsx index 67828f563..ebfdc8817 100644 --- a/src/components/permissions/FilesystemPermissionRequest/FilesystemPermissionRequest.tsx +++ b/src/components/permissions/FilesystemPermissionRequest/FilesystemPermissionRequest.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { Box, Text, useTheme } from '@anthropic/ink' +import { Box, Text, useTheme } from '../../../ink.js' import { FallbackPermissionRequest } from '../FallbackPermissionRequest.js' import { FilePermissionDialog } from '../FilePermissionDialog/FilePermissionDialog.js' import type { ToolInput } from '../FilePermissionDialog/useFilePermissionDialog.js' diff --git a/src/components/permissions/NotebookEditPermissionRequest/NotebookEditPermissionRequest.tsx b/src/components/permissions/NotebookEditPermissionRequest/NotebookEditPermissionRequest.tsx index 9ebc09642..6c03b94d3 100644 --- a/src/components/permissions/NotebookEditPermissionRequest/NotebookEditPermissionRequest.tsx +++ b/src/components/permissions/NotebookEditPermissionRequest/NotebookEditPermissionRequest.tsx @@ -1,7 +1,7 @@ import { basename } from 'path' import React from 'react' import type { z } from 'zod/v4' -import { Text } from '@anthropic/ink' +import { Text } from '../../../ink.js' import { NotebookEditTool } from '../../../tools/NotebookEditTool/NotebookEditTool.js' import { logError } from '../../../utils/log.js' import { FilePermissionDialog } from '../FilePermissionDialog/FilePermissionDialog.js' diff --git a/src/components/permissions/NotebookEditPermissionRequest/NotebookEditToolDiff.tsx b/src/components/permissions/NotebookEditPermissionRequest/NotebookEditToolDiff.tsx index 4822dac9a..9b5373142 100644 --- a/src/components/permissions/NotebookEditPermissionRequest/NotebookEditToolDiff.tsx +++ b/src/components/permissions/NotebookEditPermissionRequest/NotebookEditToolDiff.tsx @@ -1,7 +1,7 @@ import { relative } from 'path' import * as React from 'react' import { Suspense, use, useMemo } from 'react' -import { Box, NoSelect, Text } from '@anthropic/ink' +import { Box, NoSelect, Text } from '../../../ink.js' import type { NotebookCellType, NotebookContent, diff --git a/src/components/permissions/PermissionDecisionDebugInfo.tsx b/src/components/permissions/PermissionDecisionDebugInfo.tsx index fbbaf6b18..afd855343 100644 --- a/src/components/permissions/PermissionDecisionDebugInfo.tsx +++ b/src/components/permissions/PermissionDecisionDebugInfo.tsx @@ -2,7 +2,7 @@ import { feature } from 'bun:bundle' import chalk from 'chalk' import figures from 'figures' import React, { useMemo } from 'react' -import { Ansi, Box, color, Text, useTheme } from '@anthropic/ink' +import { Ansi, Box, color, Text, useTheme } from '../../ink.js' import { useAppState } from '../../state/AppState.js' import type { PermissionMode } from '../../utils/permissions/PermissionMode.js' import { permissionModeTitle } from '../../utils/permissions/PermissionMode.js' diff --git a/src/components/permissions/PermissionDialog.tsx b/src/components/permissions/PermissionDialog.tsx index 40e99f50f..210bbb16e 100644 --- a/src/components/permissions/PermissionDialog.tsx +++ b/src/components/permissions/PermissionDialog.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { Box } from '@anthropic/ink' +import { Box } from '../../ink.js' import type { Theme } from '../../utils/theme.js' import { PermissionRequestTitle } from './PermissionRequestTitle.js' import type { WorkerBadgeProps } from './WorkerBadge.js' diff --git a/src/components/permissions/PermissionExplanation.tsx b/src/components/permissions/PermissionExplanation.tsx index 367fc7ccb..2fe08a858 100644 --- a/src/components/permissions/PermissionExplanation.tsx +++ b/src/components/permissions/PermissionExplanation.tsx @@ -1,5 +1,5 @@ import React, { Suspense, use, useState } from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { useKeybinding } from '../../keybindings/useKeybinding.js' import { logEvent } from '../../services/analytics/index.js' import type { Message } from '../../types/message.js' diff --git a/src/components/permissions/PermissionPrompt.tsx b/src/components/permissions/PermissionPrompt.tsx index 0e16fad25..ae9ba0730 100644 --- a/src/components/permissions/PermissionPrompt.tsx +++ b/src/components/permissions/PermissionPrompt.tsx @@ -1,5 +1,5 @@ import React, { type ReactNode, useCallback, useMemo, useState } from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import type { KeybindingAction } from '../../keybindings/types.js' import { useKeybindings } from '../../keybindings/useKeybinding.js' import { diff --git a/src/components/permissions/PermissionRequestTitle.tsx b/src/components/permissions/PermissionRequestTitle.tsx index eafa1f71f..953cca22b 100644 --- a/src/components/permissions/PermissionRequestTitle.tsx +++ b/src/components/permissions/PermissionRequestTitle.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import type { Theme } from '../../utils/theme.js' import type { WorkerBadgeProps } from './WorkerBadge.js' diff --git a/src/components/permissions/PermissionRuleExplanation.tsx b/src/components/permissions/PermissionRuleExplanation.tsx index 3ed14a120..406f7e3b8 100644 --- a/src/components/permissions/PermissionRuleExplanation.tsx +++ b/src/components/permissions/PermissionRuleExplanation.tsx @@ -1,8 +1,7 @@ import { feature } from 'bun:bundle' import chalk from 'chalk' import React from 'react' -import { Ansi, Box, Text } from '@anthropic/ink' -import ThemedText from '../design-system/ThemedText.js' +import { Ansi, Box, Text } from '../../ink.js' import { useAppState } from '../../state/AppState.js' import type { PermissionDecision, @@ -10,6 +9,7 @@ import type { } from '../../utils/permissions/PermissionResult.js' import { permissionRuleValueToString } from '../../utils/permissions/permissionRuleParser.js' import type { Theme } from '../../utils/theme.js' +import ThemedText from '../design-system/ThemedText.js' export type PermissionRuleExplanationProps = { permissionResult: PermissionDecision diff --git a/src/components/permissions/PowerShellPermissionRequest/PowerShellPermissionRequest.tsx b/src/components/permissions/PowerShellPermissionRequest/PowerShellPermissionRequest.tsx index 16183ec79..5dcd0e488 100644 --- a/src/components/permissions/PowerShellPermissionRequest/PowerShellPermissionRequest.tsx +++ b/src/components/permissions/PowerShellPermissionRequest/PowerShellPermissionRequest.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' -import { Box, Text, useTheme } from '@anthropic/ink' +import { Box, Text, useTheme } from '../../../ink.js' import { useKeybinding } from '../../../keybindings/useKeybinding.js' import { getFeatureValue_CACHED_MAY_BE_STALE } from '../../../services/analytics/growthbook.js' import { diff --git a/src/components/permissions/SandboxPermissionRequest.tsx b/src/components/permissions/SandboxPermissionRequest.tsx index cf7ef1195..9dc4d6629 100644 --- a/src/components/permissions/SandboxPermissionRequest.tsx +++ b/src/components/permissions/SandboxPermissionRequest.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from 'src/ink.js' import { type NetworkHostPattern, shouldAllowManagedSandboxDomainsOnly, diff --git a/src/components/permissions/SedEditPermissionRequest/SedEditPermissionRequest.tsx b/src/components/permissions/SedEditPermissionRequest/SedEditPermissionRequest.tsx index 2135b5e22..209cd08f4 100644 --- a/src/components/permissions/SedEditPermissionRequest/SedEditPermissionRequest.tsx +++ b/src/components/permissions/SedEditPermissionRequest/SedEditPermissionRequest.tsx @@ -5,7 +5,7 @@ import { getCwd } from 'src/utils/cwd.js' import { isENOENT } from 'src/utils/errors.js' import { detectEncodingForResolvedPath } from 'src/utils/fileRead.js' import { getFsImplementation } from 'src/utils/fsOperations.js' -import { Text } from '@anthropic/ink' +import { Text } from '../../../ink.js' import { BashTool } from '../../../tools/BashTool/BashTool.js' import { applySedSubstitution, diff --git a/src/components/permissions/SkillPermissionRequest/SkillPermissionRequest.tsx b/src/components/permissions/SkillPermissionRequest/SkillPermissionRequest.tsx index d9e4050fc..799c88705 100644 --- a/src/components/permissions/SkillPermissionRequest/SkillPermissionRequest.tsx +++ b/src/components/permissions/SkillPermissionRequest/SkillPermissionRequest.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useMemo } from 'react' import { logError } from 'src/utils/log.js' import { getOriginalCwd } from '../../../bootstrap/state.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../../ink.js' import { sanitizeToolNameForAnalytics } from '../../../services/analytics/metadata.js' import { SKILL_TOOL_NAME } from '../../../tools/SkillTool/constants.js' import { SkillTool } from '../../../tools/SkillTool/SkillTool.js' diff --git a/src/components/permissions/WebFetchPermissionRequest/WebFetchPermissionRequest.tsx b/src/components/permissions/WebFetchPermissionRequest/WebFetchPermissionRequest.tsx index 5e0625498..da2498885 100644 --- a/src/components/permissions/WebFetchPermissionRequest/WebFetchPermissionRequest.tsx +++ b/src/components/permissions/WebFetchPermissionRequest/WebFetchPermissionRequest.tsx @@ -1,5 +1,5 @@ import React, { useMemo } from 'react' -import { Box, Text, useTheme } from '@anthropic/ink' +import { Box, Text, useTheme } from '../../../ink.js' import { WebFetchTool } from '../../../tools/WebFetchTool/WebFetchTool.js' import { shouldShowAlwaysAllowOptions } from '../../../utils/permissions/permissionsLoader.js' import { diff --git a/src/components/permissions/WorkerBadge.tsx b/src/components/permissions/WorkerBadge.tsx index 959346e6e..61d5873ab 100644 --- a/src/components/permissions/WorkerBadge.tsx +++ b/src/components/permissions/WorkerBadge.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import { BLACK_CIRCLE } from '../../constants/figures.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { toInkColor } from '../../utils/ink.js' export type WorkerBadgeProps = { diff --git a/src/components/permissions/WorkerPendingPermission.tsx b/src/components/permissions/WorkerPendingPermission.tsx index 2d7ef596e..06aab0334 100644 --- a/src/components/permissions/WorkerPendingPermission.tsx +++ b/src/components/permissions/WorkerPendingPermission.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { getAgentName, getTeammateColor, diff --git a/src/components/permissions/rules/AddPermissionRules.tsx b/src/components/permissions/rules/AddPermissionRules.tsx index e62442c3c..6e48e1dcb 100644 --- a/src/components/permissions/rules/AddPermissionRules.tsx +++ b/src/components/permissions/rules/AddPermissionRules.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import { useCallback } from 'react' import { Select } from '../../../components/CustomSelect/select.js' -import { Box, Dialog, Text } from '@anthropic/ink' +import { Box, Text } from '../../../ink.js' import type { ToolPermissionContext } from '../../../Tool.js' import type { PermissionBehavior, @@ -25,6 +25,7 @@ import { import { getRelativeSettingsFilePathForSource } from '../../../utils/settings/settings.js' import { plural } from '../../../utils/stringUtils.js' import type { OptionWithDescription } from '../../CustomSelect/select.js' +import { Dialog } from '../../design-system/Dialog.js' import { PermissionRuleDescription } from './PermissionRuleDescription.js' export function optionForPermissionSaveDestination( diff --git a/src/components/permissions/rules/AddWorkspaceDirectory.tsx b/src/components/permissions/rules/AddWorkspaceDirectory.tsx index 589928782..07d0a00ef 100644 --- a/src/components/permissions/rules/AddWorkspaceDirectory.tsx +++ b/src/components/permissions/rules/AddWorkspaceDirectory.tsx @@ -7,13 +7,16 @@ import { validateDirectoryForWorkspace, } from '../../../commands/add-dir/validation.js' import TextInput from '../../../components/TextInput.js' -import { type KeyboardEvent, Box, Text } from '@anthropic/ink' +import type { KeyboardEvent } from '../../../ink/events/keyboard-event.js' +import { Box, Text } from '../../../ink.js' import { useKeybinding } from '../../../keybindings/useKeybinding.js' import type { ToolPermissionContext } from '../../../Tool.js' import { getDirectoryCompletions } from '../../../utils/suggestions/directoryCompletion.js' import { ConfigurableShortcutHint } from '../../ConfigurableShortcutHint.js' import { Select } from '../../CustomSelect/select.js' -import { Byline, Dialog, KeyboardShortcutHint } from '@anthropic/ink' +import { Byline } from '../../design-system/Byline.js' +import { Dialog } from '../../design-system/Dialog.js' +import { KeyboardShortcutHint } from '../../design-system/KeyboardShortcutHint.js' import { PromptInputFooterSuggestions, type SuggestionItem, diff --git a/src/components/permissions/rules/PermissionRuleDescription.tsx b/src/components/permissions/rules/PermissionRuleDescription.tsx index d4591b8d9..ac8f0cd23 100644 --- a/src/components/permissions/rules/PermissionRuleDescription.tsx +++ b/src/components/permissions/rules/PermissionRuleDescription.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { Text } from '@anthropic/ink' +import { Text } from '../../../ink.js' import { BashTool } from '../../../tools/BashTool/BashTool.js' import type { PermissionRuleValue } from '../../../utils/permissions/PermissionRule.js' diff --git a/src/components/permissions/rules/PermissionRuleInput.tsx b/src/components/permissions/rules/PermissionRuleInput.tsx index fae8553d9..36dfb6b63 100644 --- a/src/components/permissions/rules/PermissionRuleInput.tsx +++ b/src/components/permissions/rules/PermissionRuleInput.tsx @@ -4,7 +4,7 @@ import { useState } from 'react' import TextInput from '../../../components/TextInput.js' import { useExitOnCtrlCDWithKeybindings } from '../../../hooks/useExitOnCtrlCDWithKeybindings.js' import { useTerminalSize } from '../../../hooks/useTerminalSize.js' -import { Box, Newline, Text } from '@anthropic/ink' +import { Box, Newline, Text } from '../../../ink.js' import { useKeybinding } from '../../../keybindings/useKeybinding.js' import { BashTool } from '../../../tools/BashTool/BashTool.js' import { WebFetchTool } from '../../../tools/WebFetchTool/WebFetchTool.js' diff --git a/src/components/permissions/rules/PermissionRuleList.tsx b/src/components/permissions/rules/PermissionRuleList.tsx index 035c23932..129b58083 100644 --- a/src/components/permissions/rules/PermissionRuleList.tsx +++ b/src/components/permissions/rules/PermissionRuleList.tsx @@ -12,7 +12,8 @@ import type { CommandResultDisplay } from '../../../commands.js' import { Select } from '../../../components/CustomSelect/select.js' import { useExitOnCtrlCDWithKeybindings } from '../../../hooks/useExitOnCtrlCDWithKeybindings.js' import { useSearchInput } from '../../../hooks/useSearchInput.js' -import { type KeyboardEvent, Box, Text, useTerminalFocus } from '@anthropic/ink' +import type { KeyboardEvent } from '../../../ink/events/keyboard-event.js' +import { Box, Text, useTerminalFocus } from '../../../ink.js' import { useKeybinding } from '../../../keybindings/useKeybinding.js' import { type AutoModeDenial, @@ -33,8 +34,13 @@ import { } from '../../../utils/permissions/permissions.js' import type { UnreachableRule } from '../../../utils/permissions/shadowedRuleDetection.js' import { jsonStringify } from '../../../utils/slowOperations.js' -import { Pane, Tabs } from '@anthropic/ink' -import { Tab, useTabHeaderFocus, useTabsWidth } from '../../design-system/Tabs.js' +import { Pane } from '../../design-system/Pane.js' +import { + Tab, + Tabs, + useTabHeaderFocus, + useTabsWidth, +} from '../../design-system/Tabs.js' import { SearchBox } from '../../SearchBox.js' import type { Option } from '../../ui/option.js' import { AddPermissionRules } from './AddPermissionRules.js' diff --git a/src/components/permissions/rules/RecentDenialsTab.tsx b/src/components/permissions/rules/RecentDenialsTab.tsx index 1006c98a7..17c13844d 100644 --- a/src/components/permissions/rules/RecentDenialsTab.tsx +++ b/src/components/permissions/rules/RecentDenialsTab.tsx @@ -1,13 +1,13 @@ import * as React from 'react' import { useCallback, useEffect, useState } from 'react' // eslint-disable-next-line custom-rules/prefer-use-keybindings -- 'r' is a view-specific key, not a global keybinding -import { Box, Text, useInput } from '@anthropic/ink' +import { Box, Text, useInput } from '../../../ink.js' import { type AutoModeDenial, getAutoModeDenials, } from '../../../utils/autoModeDenials.js' import { Select } from '../../CustomSelect/select.js' -import { StatusIcon } from '@anthropic/ink' +import { StatusIcon } from '../../design-system/StatusIcon.js' import { useTabHeaderFocus } from '../../design-system/Tabs.js' type Props = { diff --git a/src/components/permissions/rules/RemoveWorkspaceDirectory.tsx b/src/components/permissions/rules/RemoveWorkspaceDirectory.tsx index 8d7e6fa99..e6eefade2 100644 --- a/src/components/permissions/rules/RemoveWorkspaceDirectory.tsx +++ b/src/components/permissions/rules/RemoveWorkspaceDirectory.tsx @@ -1,10 +1,10 @@ import * as React from 'react' import { useCallback } from 'react' import { Select } from '../../../components/CustomSelect/select.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../../ink.js' import type { ToolPermissionContext } from '../../../Tool.js' import { applyPermissionUpdate } from '../../../utils/permissions/PermissionUpdate.js' -import { Dialog } from '@anthropic/ink' +import { Dialog } from '../../design-system/Dialog.js' type Props = { directoryPath: string diff --git a/src/components/permissions/rules/WorkspaceTab.tsx b/src/components/permissions/rules/WorkspaceTab.tsx index 65a747893..0dab0c7d0 100644 --- a/src/components/permissions/rules/WorkspaceTab.tsx +++ b/src/components/permissions/rules/WorkspaceTab.tsx @@ -4,7 +4,7 @@ import { useCallback, useEffect } from 'react' import { getOriginalCwd } from '../../../bootstrap/state.js' import type { CommandResultDisplay } from '../../../commands.js' import { Select } from '../../../components/CustomSelect/select.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../../ink.js' import type { ToolPermissionContext } from '../../../Tool.js' import { useTabHeaderFocus } from '../../design-system/Tabs.js' diff --git a/src/components/permissions/shellPermissionHelpers.tsx b/src/components/permissions/shellPermissionHelpers.tsx index 9b7c945f0..2c7a2db95 100644 --- a/src/components/permissions/shellPermissionHelpers.tsx +++ b/src/components/permissions/shellPermissionHelpers.tsx @@ -1,7 +1,7 @@ import { basename, sep } from 'path' import React, { type ReactNode } from 'react' import { getOriginalCwd } from '../../bootstrap/state.js' -import { Text } from '@anthropic/ink' +import { Text } from '../../ink.js' import type { PermissionUpdate } from '../../utils/permissions/PermissionUpdateSchema.js' import { permissionRuleExtractPrefix } from '../../utils/permissions/shellRuleMatching.js' diff --git a/src/components/sandbox/SandboxConfigTab.tsx b/src/components/sandbox/SandboxConfigTab.tsx index 37e00ce56..58bfba688 100644 --- a/src/components/sandbox/SandboxConfigTab.tsx +++ b/src/components/sandbox/SandboxConfigTab.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { SandboxManager, shouldAllowManagedSandboxDomainsOnly, diff --git a/src/components/sandbox/SandboxDependenciesTab.tsx b/src/components/sandbox/SandboxDependenciesTab.tsx index 24efdf1ef..75091910d 100644 --- a/src/components/sandbox/SandboxDependenciesTab.tsx +++ b/src/components/sandbox/SandboxDependenciesTab.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { getPlatform } from '../../utils/platform.js' import type { SandboxDependencyCheck } from '../../utils/sandbox/sandbox-adapter.js' diff --git a/src/components/sandbox/SandboxDoctorSection.tsx b/src/components/sandbox/SandboxDoctorSection.tsx index effa9500e..5e7198c38 100644 --- a/src/components/sandbox/SandboxDoctorSection.tsx +++ b/src/components/sandbox/SandboxDoctorSection.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { SandboxManager } from '../../utils/sandbox/sandbox-adapter.js' export function SandboxDoctorSection(): React.ReactNode { diff --git a/src/components/sandbox/SandboxOverridesTab.tsx b/src/components/sandbox/SandboxOverridesTab.tsx index 2c14400b2..74c6d224b 100644 --- a/src/components/sandbox/SandboxOverridesTab.tsx +++ b/src/components/sandbox/SandboxOverridesTab.tsx @@ -1,9 +1,8 @@ import React from 'react' -import { Box, color, Link, Text, useTheme } from '@anthropic/ink' +import { Box, color, Link, Text, useTheme } from '../../ink.js' import type { CommandResultDisplay } from '../../types/command.js' import { SandboxManager } from '../../utils/sandbox/sandbox-adapter.js' import { Select } from '../CustomSelect/select.js' -// useTabHeaderFocus not available in ink.js facade import { useTabHeaderFocus } from '../design-system/Tabs.js' type Props = { diff --git a/src/components/sandbox/SandboxSettings.tsx b/src/components/sandbox/SandboxSettings.tsx index 845540b07..05998577b 100644 --- a/src/components/sandbox/SandboxSettings.tsx +++ b/src/components/sandbox/SandboxSettings.tsx @@ -1,13 +1,13 @@ import React from 'react' -import { Box, color, Link, Text, useTheme } from '@anthropic/ink' +import { Box, color, Link, Text, useTheme } from '../../ink.js' import { useKeybindings } from '../../keybindings/useKeybinding.js' import type { CommandResultDisplay } from '../../types/command.js' import type { SandboxDependencyCheck } from '../../utils/sandbox/sandbox-adapter.js' import { SandboxManager } from '../../utils/sandbox/sandbox-adapter.js' import { getSettings_DEPRECATED } from '../../utils/settings/settings.js' import { Select } from '../CustomSelect/select.js' -import { Pane, Tabs } from '@anthropic/ink' -import { Tab, useTabHeaderFocus } from '../design-system/Tabs.js' +import { Pane } from '../design-system/Pane.js' +import { Tab, Tabs, useTabHeaderFocus } from '../design-system/Tabs.js' import { SandboxConfigTab } from './SandboxConfigTab.js' import { SandboxDependenciesTab } from './SandboxDependenciesTab.js' import { SandboxOverridesTab } from './SandboxOverridesTab.js' diff --git a/src/components/shell/OutputLine.tsx b/src/components/shell/OutputLine.tsx index 0b2c280af..cf72760db 100644 --- a/src/components/shell/OutputLine.tsx +++ b/src/components/shell/OutputLine.tsx @@ -1,9 +1,8 @@ import * as React from 'react' import { useMemo } from 'react' import { useTerminalSize } from '../../hooks/useTerminalSize.js' -import { Ansi, Text } from '@anthropic/ink' +import { Ansi, Text } from '../../ink.js' import { createHyperlink } from '../../utils/hyperlink.js' - import { jsonParse, jsonStringify } from '../../utils/slowOperations.js' import { renderTruncatedContent } from '../../utils/terminal.js' import { MessageResponse } from '../MessageResponse.js' diff --git a/src/components/shell/ShellProgressMessage.tsx b/src/components/shell/ShellProgressMessage.tsx index a99bdbd0d..99da5ac3b 100644 --- a/src/components/shell/ShellProgressMessage.tsx +++ b/src/components/shell/ShellProgressMessage.tsx @@ -1,6 +1,6 @@ import React from 'react' import stripAnsi from 'strip-ansi' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { formatFileSize } from '../../utils/format.js' import { MessageResponse } from '../MessageResponse.js' import { OffscreenFreeze } from '../OffscreenFreeze.js' diff --git a/src/components/shell/ShellTimeDisplay.tsx b/src/components/shell/ShellTimeDisplay.tsx index 67b5c373a..7e619dfba 100644 --- a/src/components/shell/ShellTimeDisplay.tsx +++ b/src/components/shell/ShellTimeDisplay.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { Text } from '@anthropic/ink' +import { Text } from '../../ink.js' import { formatDuration } from '../../utils/format.js' type Props = { diff --git a/src/components/skills/SkillsMenu.tsx b/src/components/skills/SkillsMenu.tsx index 4b33eee57..5733688b9 100644 --- a/src/components/skills/SkillsMenu.tsx +++ b/src/components/skills/SkillsMenu.tsx @@ -8,7 +8,7 @@ import { getCommandName, type PromptCommand, } from '../../commands.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { estimateSkillFrontmatterTokens, getSkillsPath, @@ -21,7 +21,7 @@ import { } from '../../utils/settings/constants.js' import { plural } from '../../utils/stringUtils.js' import { ConfigurableShortcutHint } from '../ConfigurableShortcutHint.js' -import { Dialog } from '@anthropic/ink' +import { Dialog } from '../design-system/Dialog.js' // Skills are always PromptCommands with CommandBase properties type SkillCommand = CommandBase & PromptCommand diff --git a/src/components/tasks/AsyncAgentDetailDialog.tsx b/src/components/tasks/AsyncAgentDetailDialog.tsx index 2070b9a68..4174d4fa5 100644 --- a/src/components/tasks/AsyncAgentDetailDialog.tsx +++ b/src/components/tasks/AsyncAgentDetailDialog.tsx @@ -1,14 +1,17 @@ import React, { useMemo } from 'react' import type { DeepImmutable } from 'src/types/utils.js' import { useElapsedTime } from '../../hooks/useElapsedTime.js' -import { type KeyboardEvent, Box, Text, useTheme } from '@anthropic/ink' +import type { KeyboardEvent } from '../../ink/events/keyboard-event.js' +import { Box, Text, useTheme } from '../../ink.js' import { useKeybindings } from '../../keybindings/useKeybinding.js' import { getEmptyToolPermissionContext } from '../../Tool.js' import type { LocalAgentTaskState } from '../../tasks/LocalAgentTask/LocalAgentTask.js' import { getTools } from '../../tools.js' import { formatNumber } from '../../utils/format.js' import { extractTag } from '../../utils/messages.js' -import { Byline, Dialog, KeyboardShortcutHint } from '@anthropic/ink' +import { Byline } from '../design-system/Byline.js' +import { Dialog } from '../design-system/Dialog.js' +import { KeyboardShortcutHint } from '../design-system/KeyboardShortcutHint.js' import { UserPlanMessage } from '../messages/UserPlanMessage.js' import { renderToolActivity } from './renderToolActivity.js' import { getTaskStatusColor, getTaskStatusIcon } from './taskStatusUtils.js' diff --git a/src/components/tasks/BackgroundTask.tsx b/src/components/tasks/BackgroundTask.tsx index 7fdf1fa4f..fd48d09e7 100644 --- a/src/components/tasks/BackgroundTask.tsx +++ b/src/components/tasks/BackgroundTask.tsx @@ -1,10 +1,9 @@ import * as React from 'react' -import { Text } from '@anthropic/ink' -import { toInkColor } from '../../utils/ink.js' +import { Text } from 'src/ink.js' import type { BackgroundTaskState } from 'src/tasks/types.js' import type { DeepImmutable } from 'src/types/utils.js' import { truncate } from 'src/utils/format.js' - +import { toInkColor } from 'src/utils/ink.js' import { plural } from 'src/utils/stringUtils.js' import { DIAMOND_FILLED, DIAMOND_OPEN } from '../../constants/figures.js' import { RemoteSessionProgress } from './RemoteSessionProgress.js' diff --git a/src/components/tasks/BackgroundTaskStatus.tsx b/src/components/tasks/BackgroundTaskStatus.tsx index c315bfa90..26d46cf98 100644 --- a/src/components/tasks/BackgroundTaskStatus.tsx +++ b/src/components/tasks/BackgroundTaskStatus.tsx @@ -2,7 +2,7 @@ import figures from 'figures' import * as React from 'react' import { useMemo, useState } from 'react' import { useTerminalSize } from 'src/hooks/useTerminalSize.js' -import { stringWidth } from '@anthropic/ink' +import { stringWidth } from 'src/ink/stringWidth.js' import { useAppState, useSetAppState } from 'src/state/AppState.js' import { enterTeammateView, @@ -16,14 +16,14 @@ import { type TaskState, } from 'src/tasks/types.js' import { calculateHorizontalScrollWindow } from 'src/utils/horizontalScroll.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { AGENT_COLOR_TO_THEME_COLOR, AGENT_COLORS, type AgentColorName, } from '../../tools/AgentTool/agentColorManager.js' import type { Theme } from '../../utils/theme.js' -import { KeyboardShortcutHint } from '@anthropic/ink' +import { KeyboardShortcutHint } from '../design-system/KeyboardShortcutHint.js' import { shouldHideTasksFooter } from './taskStatusUtils.js' type Props = { diff --git a/src/components/tasks/BackgroundTasksDialog.tsx b/src/components/tasks/BackgroundTasksDialog.tsx index 30eca7444..d9f119cf1 100644 --- a/src/components/tasks/BackgroundTasksDialog.tsx +++ b/src/components/tasks/BackgroundTasksDialog.tsx @@ -45,11 +45,14 @@ import { stopUltraplan } from '../../commands/ultraplan.js' import type { CommandResultDisplay } from '../../commands.js' import { useRegisterOverlay } from '../../context/overlayContext.js' import type { ExitState } from '../../hooks/useExitOnCtrlCDWithKeybindings.js' -import { type KeyboardEvent, Box, Text } from '@anthropic/ink' +import type { KeyboardEvent } from '../../ink/events/keyboard-event.js' +import { Box, Text } from '../../ink.js' import { useKeybindings } from '../../keybindings/useKeybinding.js' import { useShortcutDisplay } from '../../keybindings/useShortcutDisplay.js' import { count } from '../../utils/array.js' -import { Byline, Dialog, KeyboardShortcutHint } from '@anthropic/ink' +import { Byline } from '../design-system/Byline.js' +import { Dialog } from '../design-system/Dialog.js' +import { KeyboardShortcutHint } from '../design-system/KeyboardShortcutHint.js' import { AsyncAgentDetailDialog } from './AsyncAgentDetailDialog.js' import { BackgroundTask as BackgroundTaskComponent } from './BackgroundTask.js' import { DreamDetailDialog } from './DreamDetailDialog.js' diff --git a/src/components/tasks/DreamDetailDialog.tsx b/src/components/tasks/DreamDetailDialog.tsx index 67baab993..bea310946 100644 --- a/src/components/tasks/DreamDetailDialog.tsx +++ b/src/components/tasks/DreamDetailDialog.tsx @@ -1,11 +1,14 @@ import React from 'react' import type { DeepImmutable } from 'src/types/utils.js' import { useElapsedTime } from '../../hooks/useElapsedTime.js' -import { type KeyboardEvent, Box, Text } from '@anthropic/ink' +import type { KeyboardEvent } from '../../ink/events/keyboard-event.js' +import { Box, Text } from '../../ink.js' import { useKeybindings } from '../../keybindings/useKeybinding.js' import type { DreamTaskState } from '../../tasks/DreamTask/DreamTask.js' import { plural } from '../../utils/stringUtils.js' -import { Byline, Dialog, KeyboardShortcutHint } from '@anthropic/ink' +import { Byline } from '../design-system/Byline.js' +import { Dialog } from '../design-system/Dialog.js' +import { KeyboardShortcutHint } from '../design-system/KeyboardShortcutHint.js' type Props = { task: DeepImmutable diff --git a/src/components/tasks/InProcessTeammateDetailDialog.tsx b/src/components/tasks/InProcessTeammateDetailDialog.tsx index c0a755a60..b59bbbd5e 100644 --- a/src/components/tasks/InProcessTeammateDetailDialog.tsx +++ b/src/components/tasks/InProcessTeammateDetailDialog.tsx @@ -1,15 +1,17 @@ import React, { useMemo } from 'react' import type { DeepImmutable } from 'src/types/utils.js' import { useElapsedTime } from '../../hooks/useElapsedTime.js' -import { type KeyboardEvent, Box, Text, useTheme } from '@anthropic/ink' +import type { KeyboardEvent } from '../../ink/events/keyboard-event.js' +import { Box, Text, useTheme } from '../../ink.js' import { useKeybindings } from '../../keybindings/useKeybinding.js' import { getEmptyToolPermissionContext } from '../../Tool.js' import type { InProcessTeammateTaskState } from '../../tasks/InProcessTeammateTask/types.js' import { getTools } from '../../tools.js' import { formatNumber, truncateToWidth } from '../../utils/format.js' - -import { Byline, Dialog, KeyboardShortcutHint } from '@anthropic/ink' import { toInkColor } from '../../utils/ink.js' +import { Byline } from '../design-system/Byline.js' +import { Dialog } from '../design-system/Dialog.js' +import { KeyboardShortcutHint } from '../design-system/KeyboardShortcutHint.js' import { renderToolActivity } from './renderToolActivity.js' import { describeTeammateActivity } from './taskStatusUtils.js' diff --git a/src/components/tasks/RemoteSessionDetailDialog.tsx b/src/components/tasks/RemoteSessionDetailDialog.tsx index ec2c43b42..55c897fd9 100644 --- a/src/components/tasks/RemoteSessionDetailDialog.tsx +++ b/src/components/tasks/RemoteSessionDetailDialog.tsx @@ -6,7 +6,8 @@ import type { DeepImmutable } from 'src/types/utils.js' import type { CommandResultDisplay } from '../../commands.js' import { DIAMOND_FILLED, DIAMOND_OPEN } from '../../constants/figures.js' import { useElapsedTime } from '../../hooks/useElapsedTime.js' -import { type KeyboardEvent, Box, Link, Text } from '@anthropic/ink' +import type { KeyboardEvent } from '../../ink/events/keyboard-event.js' +import { Box, Link, Text } from '../../ink.js' import type { RemoteAgentTaskState } from '../../tasks/RemoteAgentTask/RemoteAgentTask.js' import { getRemoteTaskSessionUrl } from '../../tasks/RemoteAgentTask/RemoteAgentTask.js' import { @@ -23,7 +24,9 @@ import { EMPTY_LOOKUPS, normalizeMessages } from '../../utils/messages.js' import { plural } from '../../utils/stringUtils.js' import { teleportResumeCodeSession } from '../../utils/teleport.js' import { Select } from '../CustomSelect/select.js' -import { Byline, Dialog, KeyboardShortcutHint } from '@anthropic/ink' +import { Byline } from '../design-system/Byline.js' +import { Dialog } from '../design-system/Dialog.js' +import { KeyboardShortcutHint } from '../design-system/KeyboardShortcutHint.js' import { Message } from '../Message.js' import { formatReviewStageCounts, diff --git a/src/components/tasks/RemoteSessionProgress.tsx b/src/components/tasks/RemoteSessionProgress.tsx index d7e0f8e68..c1711cd8a 100644 --- a/src/components/tasks/RemoteSessionProgress.tsx +++ b/src/components/tasks/RemoteSessionProgress.tsx @@ -3,7 +3,7 @@ import type { RemoteAgentTaskState } from 'src/tasks/RemoteAgentTask/RemoteAgent import type { DeepImmutable } from 'src/types/utils.js' import { DIAMOND_FILLED, DIAMOND_OPEN } from '../../constants/figures.js' import { useSettings } from '../../hooks/useSettings.js' -import { Text, useAnimationFrame } from '@anthropic/ink' +import { Text, useAnimationFrame } from '../../ink.js' import { count } from '../../utils/array.js' import { getRainbowColor } from '../../utils/thinking.js' diff --git a/src/components/tasks/ShellDetailDialog.tsx b/src/components/tasks/ShellDetailDialog.tsx index 7627ec561..a81bafc8b 100644 --- a/src/components/tasks/ShellDetailDialog.tsx +++ b/src/components/tasks/ShellDetailDialog.tsx @@ -8,7 +8,8 @@ import React, { import type { DeepImmutable } from 'src/types/utils.js' import type { CommandResultDisplay } from '../../commands.js' import { useTerminalSize } from '../../hooks/useTerminalSize.js' -import { type KeyboardEvent, Box, Text } from '@anthropic/ink' +import type { KeyboardEvent } from '../../ink/events/keyboard-event.js' +import { Box, Text } from '../../ink.js' import { useKeybindings } from '../../keybindings/useKeybinding.js' import type { LocalShellTaskState } from '../../tasks/LocalShellTask/guards.js' import { @@ -18,7 +19,9 @@ import { } from '../../utils/format.js' import { tailFile } from '../../utils/fsOperations.js' import { getTaskOutputPath } from '../../utils/task/diskOutput.js' -import { Byline, Dialog, KeyboardShortcutHint } from '@anthropic/ink' +import { Byline } from '../design-system/Byline.js' +import { Dialog } from '../design-system/Dialog.js' +import { KeyboardShortcutHint } from '../design-system/KeyboardShortcutHint.js' type Props = { shell: DeepImmutable diff --git a/src/components/tasks/ShellProgress.tsx b/src/components/tasks/ShellProgress.tsx index d5da46f63..b70494c16 100644 --- a/src/components/tasks/ShellProgress.tsx +++ b/src/components/tasks/ShellProgress.tsx @@ -1,6 +1,6 @@ import type { ReactNode } from 'react' import React from 'react' -import { Text } from '@anthropic/ink' +import { Text } from 'src/ink.js' import type { TaskStatus } from 'src/Task.js' import type { LocalShellTaskState } from 'src/tasks/LocalShellTask/guards.js' import type { DeepImmutable } from 'src/types/utils.js' diff --git a/src/components/tasks/renderToolActivity.tsx b/src/components/tasks/renderToolActivity.tsx index 70abaa1bb..a6e1c60a2 100644 --- a/src/components/tasks/renderToolActivity.tsx +++ b/src/components/tasks/renderToolActivity.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { Text } from '@anthropic/ink' +import { Text } from '../../ink.js' import type { Tools } from '../../Tool.js' import { findToolByName } from '../../Tool.js' import type { ToolActivity } from '../../tasks/LocalAgentTask/LocalAgentTask.js' diff --git a/src/components/teams/TeamStatus.tsx b/src/components/teams/TeamStatus.tsx index 79bf89667..ee066875b 100644 --- a/src/components/teams/TeamStatus.tsx +++ b/src/components/teams/TeamStatus.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { Text } from '@anthropic/ink' +import { Text } from '../../ink.js' import { useAppState } from '../../state/AppState.js' type Props = { diff --git a/src/components/teams/TeamsDialog.tsx b/src/components/teams/TeamsDialog.tsx index a21caaebe..872212115 100644 --- a/src/components/teams/TeamsDialog.tsx +++ b/src/components/teams/TeamsDialog.tsx @@ -4,8 +4,9 @@ import * as React from 'react' import { useCallback, useEffect, useMemo, useState } from 'react' import { useInterval } from 'usehooks-ts' import { useRegisterOverlay } from '../../context/overlayContext.js' +import { stringWidth } from '../../ink/stringWidth.js' // eslint-disable-next-line custom-rules/prefer-use-keybindings -- raw j/k/arrow dialog navigation -import { Box, Text, useInput, stringWidth } from '@anthropic/ink' +import { Box, Text, useInput } from '../../ink.js' import { useKeybindings } from '../../keybindings/useKeybinding.js' import { useShortcutDisplay } from '../../keybindings/useShortcutDisplay.js' import { @@ -62,7 +63,7 @@ import { sendShutdownRequestToMailbox, writeToMailbox, } from '../../utils/teammateMailbox.js' -import { Dialog } from '@anthropic/ink' +import { Dialog } from '../design-system/Dialog.js' import ThemedText from '../design-system/ThemedText.js' type Props = { diff --git a/src/components/ui/OrderedList.tsx b/src/components/ui/OrderedList.tsx index dde5e5998..ef468156f 100644 --- a/src/components/ui/OrderedList.tsx +++ b/src/components/ui/OrderedList.tsx @@ -4,7 +4,7 @@ import React, { type ReactNode, useContext, } from 'react' -import { Box } from '@anthropic/ink' +import { Box } from '../../ink.js' import { OrderedListItem, OrderedListItemContext } from './OrderedListItem.js' const OrderedListContext = createContext({ marker: '' }) diff --git a/src/components/ui/OrderedListItem.tsx b/src/components/ui/OrderedListItem.tsx index 7b60152c1..f217f0eaf 100644 --- a/src/components/ui/OrderedListItem.tsx +++ b/src/components/ui/OrderedListItem.tsx @@ -1,5 +1,5 @@ import React, { createContext, type ReactNode, useContext } from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' export const OrderedListItemContext = createContext({ marker: '' }) diff --git a/src/components/ui/TreeSelect.tsx b/src/components/ui/TreeSelect.tsx index 9e6ec7f49..55ca33c88 100644 --- a/src/components/ui/TreeSelect.tsx +++ b/src/components/ui/TreeSelect.tsx @@ -1,5 +1,6 @@ import React from 'react' -import { type KeyboardEvent, Box } from '@anthropic/ink' +import type { KeyboardEvent } from '../../ink/events/keyboard-event.js' +import { Box } from '../../ink.js' import { type OptionWithDescription, Select } from '../CustomSelect/select.js' export type TreeNode = { diff --git a/src/components/ultraplan/UltraplanChoiceDialog.tsx b/src/components/ultraplan/UltraplanChoiceDialog.tsx index 28a225088..f1fd40186 100644 --- a/src/components/ultraplan/UltraplanChoiceDialog.tsx +++ b/src/components/ultraplan/UltraplanChoiceDialog.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { join } from 'path'; import { writeFile } from 'fs/promises'; import figures from 'figures'; -import { Box, Text, useInput, wrapText } from '@anthropic/ink'; +import { Box, Text, useInput, wrapText } from '../../ink.js'; import { useTerminalSize } from '../../hooks/useTerminalSize.js'; import { Select } from '../CustomSelect/select.js'; import { PermissionDialog } from '../permissions/PermissionDialog.js'; diff --git a/src/components/ultraplan/UltraplanLaunchDialog.tsx b/src/components/ultraplan/UltraplanLaunchDialog.tsx index 612384b58..bdba02de3 100644 --- a/src/components/ultraplan/UltraplanLaunchDialog.tsx +++ b/src/components/ultraplan/UltraplanLaunchDialog.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { Box, Text, Link } from '@anthropic/ink'; +import { Box, Text, Link } from '../../ink.js'; import { Select } from '../CustomSelect/select.js'; import { PermissionDialog } from '../permissions/PermissionDialog.js'; import { useAppState, useSetAppState } from '../../state/AppState.js'; diff --git a/src/components/wizard/WizardDialogLayout.tsx b/src/components/wizard/WizardDialogLayout.tsx index 0a03b255e..34f20a261 100644 --- a/src/components/wizard/WizardDialogLayout.tsx +++ b/src/components/wizard/WizardDialogLayout.tsx @@ -1,6 +1,6 @@ import React, { type ReactNode } from 'react' import type { Theme } from '../../utils/theme.js' -import { Dialog } from '@anthropic/ink' +import { Dialog } from '../design-system/Dialog.js' import { useWizard } from './useWizard.js' import { WizardNavigationFooter } from './WizardNavigationFooter.js' diff --git a/src/components/wizard/WizardNavigationFooter.tsx b/src/components/wizard/WizardNavigationFooter.tsx index 96c547c65..35a03ee81 100644 --- a/src/components/wizard/WizardNavigationFooter.tsx +++ b/src/components/wizard/WizardNavigationFooter.tsx @@ -1,8 +1,9 @@ import React, { type ReactNode } from 'react' import { useExitOnCtrlCDWithKeybindings } from '../../hooks/useExitOnCtrlCDWithKeybindings.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { ConfigurableShortcutHint } from '../ConfigurableShortcutHint.js' -import { Byline, KeyboardShortcutHint } from '@anthropic/ink' +import { Byline } from '../design-system/Byline.js' +import { KeyboardShortcutHint } from '../design-system/KeyboardShortcutHint.js' type Props = { instructions?: ReactNode diff --git a/src/context/QueuedMessageContext.tsx b/src/context/QueuedMessageContext.tsx index d78c9487b..575fc8619 100644 --- a/src/context/QueuedMessageContext.tsx +++ b/src/context/QueuedMessageContext.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { Box } from '@anthropic/ink' +import { Box } from '../ink.js' type QueuedMessageContextValue = { isQueued: boolean diff --git a/src/context/modalContext.tsx b/src/context/modalContext.tsx index 7d8498784..b2263a071 100644 --- a/src/context/modalContext.tsx +++ b/src/context/modalContext.tsx @@ -1,5 +1,5 @@ import { createContext, type RefObject, useContext } from 'react' -import type { ScrollBoxHandle } from '@anthropic/ink' +import type { ScrollBoxHandle } from '../ink/components/ScrollBox.js' /** * Set by FullscreenLayout when rendering content in its `modal` slot — diff --git a/src/context/overlayContext.tsx b/src/context/overlayContext.tsx index 406e03f98..602c1268d 100644 --- a/src/context/overlayContext.tsx +++ b/src/context/overlayContext.tsx @@ -13,7 +13,7 @@ * so no manual cleanup or state management is needed. */ import { useContext, useEffect, useLayoutEffect } from 'react' -import { instances } from '@anthropic/ink' +import instances from '../ink/instances.js' import { AppStoreContext, useAppState } from '../state/AppState.js' // Non-modal overlays that shouldn't disable TextInput focus diff --git a/src/dialogLaunchers.tsx b/src/dialogLaunchers.tsx index 5a6ed7372..3d2e01e14 100644 --- a/src/dialogLaunchers.tsx +++ b/src/dialogLaunchers.tsx @@ -9,7 +9,7 @@ import React from 'react' import type { AssistantSession } from './assistant/sessionDiscovery.js' import type { StatsStore } from './context/stats.js' -import type { Root } from '@anthropic/ink' +import type { Root } from './ink.js' import { renderAndRun, showSetupDialog } from './interactiveHelpers.js' import { KeybindingSetup } from './keybindings/KeybindingProviderSetup.js' import type { AppState } from './state/AppStateStore.js' diff --git a/src/hooks/notifs/useCanSwitchToExistingSubscription.tsx b/src/hooks/notifs/useCanSwitchToExistingSubscription.tsx index 37d8542b0..0d70d5a2b 100644 --- a/src/hooks/notifs/useCanSwitchToExistingSubscription.tsx +++ b/src/hooks/notifs/useCanSwitchToExistingSubscription.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import { getOauthProfileFromApiKey } from 'src/services/oauth/getOauthProfile.js' import { isClaudeAISubscriber } from 'src/utils/auth.js' -import { Text } from '@anthropic/ink' +import { Text } from '../../ink.js' import { logEvent } from '../../services/analytics/index.js' import { getGlobalConfig, saveGlobalConfig } from '../../utils/config.js' import { useStartupNotification } from './useStartupNotification.js' diff --git a/src/hooks/notifs/useIDEStatusIndicator.tsx b/src/hooks/notifs/useIDEStatusIndicator.tsx index 70175d10f..4be07f551 100644 --- a/src/hooks/notifs/useIDEStatusIndicator.tsx +++ b/src/hooks/notifs/useIDEStatusIndicator.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useRef } from 'react' import { useNotifications } from 'src/context/notifications.js' -import { Text } from '@anthropic/ink' +import { Text } from 'src/ink.js' import type { MCPServerConnection } from 'src/services/mcp/types.js' import { getGlobalConfig, saveGlobalConfig } from 'src/utils/config.js' import { diff --git a/src/hooks/notifs/useLspInitializationNotification.tsx b/src/hooks/notifs/useLspInitializationNotification.tsx index 7b096f2b0..f86243a51 100644 --- a/src/hooks/notifs/useLspInitializationNotification.tsx +++ b/src/hooks/notifs/useLspInitializationNotification.tsx @@ -2,7 +2,7 @@ import * as React from 'react' import { useInterval } from 'usehooks-ts' import { getIsRemoteMode, getIsScrollDraining } from '../../bootstrap/state.js' import { useNotifications } from '../../context/notifications.js' -import { Text } from '@anthropic/ink' +import { Text } from '../../ink.js' import { getInitializationStatus, getLspServerManager, diff --git a/src/hooks/notifs/useMcpConnectivityStatus.tsx b/src/hooks/notifs/useMcpConnectivityStatus.tsx index 89adb8382..83072ba0f 100644 --- a/src/hooks/notifs/useMcpConnectivityStatus.tsx +++ b/src/hooks/notifs/useMcpConnectivityStatus.tsx @@ -2,7 +2,7 @@ import * as React from 'react' import { useEffect } from 'react' import { useNotifications } from 'src/context/notifications.js' import { getIsRemoteMode } from '../../bootstrap/state.js' -import { Text } from '@anthropic/ink' +import { Text } from '../../ink.js' import { hasClaudeAiMcpEverConnected } from '../../services/mcp/claudeai.js' import type { MCPServerConnection } from '../../services/mcp/types.js' diff --git a/src/hooks/notifs/usePluginAutoupdateNotification.tsx b/src/hooks/notifs/usePluginAutoupdateNotification.tsx index 7468e8ba0..bec229328 100644 --- a/src/hooks/notifs/usePluginAutoupdateNotification.tsx +++ b/src/hooks/notifs/usePluginAutoupdateNotification.tsx @@ -2,7 +2,7 @@ import * as React from 'react' import { useEffect, useState } from 'react' import { getIsRemoteMode } from '../../bootstrap/state.js' import { useNotifications } from '../../context/notifications.js' -import { Text } from '@anthropic/ink' +import { Text } from '../../ink.js' import { logForDebugging } from '../../utils/debug.js' import { onPluginsAutoUpdated } from '../../utils/plugins/pluginAutoupdate.js' diff --git a/src/hooks/notifs/usePluginInstallationStatus.tsx b/src/hooks/notifs/usePluginInstallationStatus.tsx index 9ab3de8fe..20055403d 100644 --- a/src/hooks/notifs/usePluginInstallationStatus.tsx +++ b/src/hooks/notifs/usePluginInstallationStatus.tsx @@ -2,7 +2,7 @@ import * as React from 'react' import { useEffect, useMemo } from 'react' import { getIsRemoteMode } from '../../bootstrap/state.js' import { useNotifications } from '../../context/notifications.js' -import { Text } from '@anthropic/ink' +import { Text } from '../../ink.js' import { useAppState } from '../../state/AppState.js' import { logForDebugging } from '../../utils/debug.js' import { plural } from '../../utils/stringUtils.js' diff --git a/src/hooks/notifs/useRateLimitWarningNotification.tsx b/src/hooks/notifs/useRateLimitWarningNotification.tsx index 0e8520e96..bfd3f193f 100644 --- a/src/hooks/notifs/useRateLimitWarningNotification.tsx +++ b/src/hooks/notifs/useRateLimitWarningNotification.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import { useEffect, useMemo, useRef, useState } from 'react' import { useNotifications } from 'src/context/notifications.js' -import { Text } from '@anthropic/ink' +import { Text } from 'src/ink.js' import { getRateLimitWarning, getUsingOverageText, diff --git a/src/hooks/toolPermission/handlers/interactiveHandler.ts b/src/hooks/toolPermission/handlers/interactiveHandler.ts index 3439b9fcd..6b3e4e80d 100644 --- a/src/hooks/toolPermission/handlers/interactiveHandler.ts +++ b/src/hooks/toolPermission/handlers/interactiveHandler.ts @@ -4,7 +4,7 @@ import { randomUUID } from 'crypto' import { logForDebugging } from 'src/utils/debug.js' import { getAllowedChannels } from '../../../bootstrap/state.js' import type { BridgePermissionCallbacks } from '../../../bridge/bridgePermissionCallbacks.js' -import { getTerminalFocused } from '@anthropic/ink' +import { getTerminalFocused } from '../../../ink/terminal-focus-state.js' import { CHANNEL_PERMISSION_REQUEST_METHOD, type ChannelPermissionRequestParams, diff --git a/src/hooks/useArrowKeyHistory.tsx b/src/hooks/useArrowKeyHistory.tsx index d874e32b0..69e7d6460 100644 --- a/src/hooks/useArrowKeyHistory.tsx +++ b/src/hooks/useArrowKeyHistory.tsx @@ -4,7 +4,7 @@ import { useNotifications } from 'src/context/notifications.js' import { ConfigurableShortcutHint } from '../components/ConfigurableShortcutHint.js' import { FOOTER_TEMPORARY_STATUS_TIMEOUT } from '../components/PromptInput/Notifications.js' import { getHistory } from '../history.js' -import { Text } from '@anthropic/ink' +import { Text } from '../ink.js' import type { PromptInputMode } from '../types/textInputTypes.js' import type { HistoryEntry, PastedContent } from '../utils/config.js' diff --git a/src/hooks/useAssistantHistory.ts b/src/hooks/useAssistantHistory.ts index 36da73b92..c5348d20e 100644 --- a/src/hooks/useAssistantHistory.ts +++ b/src/hooks/useAssistantHistory.ts @@ -13,7 +13,7 @@ import { type HistoryAuthCtx, type HistoryPage, } from '../assistant/sessionHistory.js' -import type { ScrollBoxHandle } from '@anthropic/ink' +import type { ScrollBoxHandle } from '../ink/components/ScrollBox.js' import type { RemoteSessionConfig } from '../remote/RemoteSessionManager.js' import { convertSDKMessage } from '../remote/sdkMessageAdapter.js' import type { Message, SystemInformationalMessage } from '../types/message.js' diff --git a/src/hooks/useAwaySummary.ts b/src/hooks/useAwaySummary.ts index 9caf55f24..115209ae6 100644 --- a/src/hooks/useAwaySummary.ts +++ b/src/hooks/useAwaySummary.ts @@ -1,6 +1,9 @@ import { feature } from 'bun:bundle' import { useEffect, useRef } from 'react' -import { getTerminalFocusState, subscribeTerminalFocus } from '@anthropic/ink' +import { + getTerminalFocusState, + subscribeTerminalFocus, +} from '../ink/terminal-focus-state.js' import { getFeatureValue_CACHED_MAY_BE_STALE } from '../services/analytics/growthbook.js' import { generateAwaySummary } from '../services/awaySummary.js' import type { Message } from '../types/message.js' diff --git a/src/hooks/useBackgroundTaskNavigation.ts b/src/hooks/useBackgroundTaskNavigation.ts index ad94c641d..c1a9b14fd 100644 --- a/src/hooks/useBackgroundTaskNavigation.ts +++ b/src/hooks/useBackgroundTaskNavigation.ts @@ -1,6 +1,7 @@ import { useEffect, useRef } from 'react' -import { KeyboardEvent, useInput } from '@anthropic/ink' -// backward-compat bridge until REPL wires handleKeyDown to +import { KeyboardEvent } from '../ink/events/keyboard-event.js' +// eslint-disable-next-line custom-rules/prefer-use-keybindings -- backward-compat bridge until REPL wires handleKeyDown to +import { useInput } from '../ink.js' import { type AppState, useAppState, diff --git a/src/hooks/useBlink.ts b/src/hooks/useBlink.ts index cecf54c53..33cac6974 100644 --- a/src/hooks/useBlink.ts +++ b/src/hooks/useBlink.ts @@ -1,4 +1,4 @@ -import { type DOMElement, useAnimationFrame, useTerminalFocus } from '@anthropic/ink' +import { type DOMElement, useAnimationFrame, useTerminalFocus } from '../ink.js' const BLINK_INTERVAL_MS = 600 diff --git a/src/hooks/useCanUseTool.tsx b/src/hooks/useCanUseTool.tsx index cf8af94f8..78b9397c0 100644 --- a/src/hooks/useCanUseTool.tsx +++ b/src/hooks/useCanUseTool.tsx @@ -8,7 +8,7 @@ import { } from 'src/services/analytics/index.js' import { sanitizeToolNameForAnalytics } from 'src/services/analytics/metadata.js' import type { ToolUseConfirm } from '../components/permissions/PermissionRequest.js' -import { Text } from '@anthropic/ink' +import { Text } from '../ink.js' import type { ToolPermissionContext, Tool as ToolType, diff --git a/src/hooks/useChromeExtensionNotification.tsx b/src/hooks/useChromeExtensionNotification.tsx index 4f043de2e..dc058df0e 100644 --- a/src/hooks/useChromeExtensionNotification.tsx +++ b/src/hooks/useChromeExtensionNotification.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { Text } from '@anthropic/ink' +import { Text } from '../ink.js' import { isClaudeAISubscriber } from '../utils/auth.js' import { isChromeExtensionInstalled, diff --git a/src/hooks/useCopyOnSelect.ts b/src/hooks/useCopyOnSelect.ts index e1a239009..778ef5a1d 100644 --- a/src/hooks/useCopyOnSelect.ts +++ b/src/hooks/useCopyOnSelect.ts @@ -1,6 +1,6 @@ import { useEffect, useRef } from 'react' -import { useTheme } from '@anthropic/ink' -import type { useSelection } from '@anthropic/ink' +import { useTheme } from '../components/design-system/ThemeProvider.js' +import type { useSelection } from '../ink/hooks/use-selection.js' import { getGlobalConfig } from '../utils/config.js' import { getTheme } from '../utils/theme.js' diff --git a/src/hooks/useExitOnCtrlCD.ts b/src/hooks/useExitOnCtrlCD.ts index e9af0157c..23ba7ad58 100644 --- a/src/hooks/useExitOnCtrlCD.ts +++ b/src/hooks/useExitOnCtrlCD.ts @@ -1,5 +1,5 @@ import { useCallback, useMemo, useState } from 'react' -import { useApp } from '@anthropic/ink' +import useApp from '../ink/hooks/use-app.js' import type { KeybindingContextName } from '../keybindings/types.js' import { useDoublePress } from './useDoublePress.js' diff --git a/src/hooks/useGlobalKeybindings.tsx b/src/hooks/useGlobalKeybindings.tsx index 1dd171cb8..a41b1b6a5 100644 --- a/src/hooks/useGlobalKeybindings.tsx +++ b/src/hooks/useGlobalKeybindings.tsx @@ -6,7 +6,7 @@ */ import { feature } from 'bun:bundle' import { useCallback } from 'react' -import { instances } from '@anthropic/ink' +import instances from '../ink/instances.js' import { useKeybinding } from '../keybindings/useKeybinding.js' import type { Screen } from '../screens/REPL.js' import { getFeatureValue_CACHED_MAY_BE_STALE } from '../services/analytics/growthbook.js' diff --git a/src/hooks/useHistorySearch.ts b/src/hooks/useHistorySearch.ts index 04bf31b67..b48c880b7 100644 --- a/src/hooks/useHistorySearch.ts +++ b/src/hooks/useHistorySearch.ts @@ -5,8 +5,9 @@ import { getValueFromInput, } from '../components/PromptInput/inputModes.js' import { makeHistoryReader } from '../history.js' -import { KeyboardEvent, useInput } from '@anthropic/ink' -// backward-compat bridge until consumers wire handleKeyDown to +import { KeyboardEvent } from '../ink/events/keyboard-event.js' +// eslint-disable-next-line custom-rules/prefer-use-keybindings -- backward-compat bridge until consumers wire handleKeyDown to +import { useInput } from '../ink.js' import { useKeybinding, useKeybindings } from '../keybindings/useKeybinding.js' import type { PromptInputMode } from '../types/textInputTypes.js' import type { HistoryEntry } from '../utils/config.js' diff --git a/src/hooks/useInboxPoller.ts b/src/hooks/useInboxPoller.ts index ac2c98555..361ba636d 100644 --- a/src/hooks/useInboxPoller.ts +++ b/src/hooks/useInboxPoller.ts @@ -3,7 +3,7 @@ import { useCallback, useEffect, useRef } from 'react' import { useInterval } from 'usehooks-ts' import type { ToolUseConfirm } from '../components/permissions/PermissionRequest.js' import { TEAMMATE_MESSAGE_TAG } from '../constants/xml.js' -import { useTerminalNotification } from '@anthropic/ink' +import { useTerminalNotification } from '../ink/useTerminalNotification.js' import { sendNotification } from '../services/notifier.js' import { type AppState, diff --git a/src/hooks/useNotifyAfterTimeout.ts b/src/hooks/useNotifyAfterTimeout.ts index c9c19f191..8b0ce315c 100644 --- a/src/hooks/useNotifyAfterTimeout.ts +++ b/src/hooks/useNotifyAfterTimeout.ts @@ -3,7 +3,7 @@ import { getLastInteractionTime, updateLastInteractionTime, } from '../bootstrap/state.js' -import { useTerminalNotification } from '@anthropic/ink' +import { useTerminalNotification } from '../ink/useTerminalNotification.js' import { sendNotification } from '../services/notifier.js' // The time threshold in milliseconds for considering an interaction "recent" (6 seconds) export const DEFAULT_INTERACTION_THRESHOLD_MS = 6000 diff --git a/src/hooks/useOfficialMarketplaceNotification.tsx b/src/hooks/useOfficialMarketplaceNotification.tsx index 6ba179e9b..25cf62254 100644 --- a/src/hooks/useOfficialMarketplaceNotification.tsx +++ b/src/hooks/useOfficialMarketplaceNotification.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import type { Notification } from '../context/notifications.js' -import { Text } from '@anthropic/ink' +import { Text } from '../ink.js' import { logForDebugging } from '../utils/debug.js' import { checkAndInstallOfficialMarketplace } from '../utils/plugins/officialMarketplaceStartupCheck.js' import { useStartupNotification } from './notifs/useStartupNotification.js' diff --git a/src/hooks/usePasteHandler.ts b/src/hooks/usePasteHandler.ts index a2ed7f306..d6257b9a2 100644 --- a/src/hooks/usePasteHandler.ts +++ b/src/hooks/usePasteHandler.ts @@ -2,7 +2,7 @@ import { basename } from 'path' import React from 'react' import { logError } from 'src/utils/log.js' import { useDebounceCallback } from 'usehooks-ts' -import type { InputEvent, Key } from '@anthropic/ink' +import type { InputEvent, Key } from '../ink.js' import { getImageFromClipboard, isImageFilePath, diff --git a/src/hooks/usePluginRecommendationBase.tsx b/src/hooks/usePluginRecommendationBase.tsx index 1e2fd7b5d..23930fba4 100644 --- a/src/hooks/usePluginRecommendationBase.tsx +++ b/src/hooks/usePluginRecommendationBase.tsx @@ -8,7 +8,7 @@ import figures from 'figures' import * as React from 'react' import { getIsRemoteMode } from '../bootstrap/state.js' import type { useNotifications } from '../context/notifications.js' -import { Text } from '@anthropic/ink' +import { Text } from '../ink.js' import { logError } from '../utils/log.js' import { getPluginById } from '../utils/plugins/marketplaceManager.js' diff --git a/src/hooks/usePromptSuggestion.ts b/src/hooks/usePromptSuggestion.ts index e080314a8..0a0a35f9c 100644 --- a/src/hooks/usePromptSuggestion.ts +++ b/src/hooks/usePromptSuggestion.ts @@ -1,5 +1,5 @@ import { useCallback, useRef } from 'react' -import { useTerminalFocus } from '@anthropic/ink' +import { useTerminalFocus } from '../ink/hooks/use-terminal-focus.js' import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent, diff --git a/src/hooks/useReplBridge.tsx b/src/hooks/useReplBridge.tsx index 7be394869..522202891 100644 --- a/src/hooks/useReplBridge.tsx +++ b/src/hooks/useReplBridge.tsx @@ -19,7 +19,7 @@ import type { SDKMessage, } from '../entrypoints/agentSdkTypes.js' import type { SDKControlResponse } from '../entrypoints/sdk/controlTypes.js' -import { Text } from '@anthropic/ink' +import { Text } from '../ink.js' import { getFeatureValue_CACHED_MAY_BE_STALE } from '../services/analytics/growthbook.js' import { useAppState, diff --git a/src/hooks/useSearchInput.ts b/src/hooks/useSearchInput.ts index 41860b0eb..a72fbf4b4 100644 --- a/src/hooks/useSearchInput.ts +++ b/src/hooks/useSearchInput.ts @@ -1,6 +1,7 @@ import { useCallback, useState } from 'react' -import { KeyboardEvent, useInput } from '@anthropic/ink' -// backward-compat bridge until consumers wire handleKeyDown to +import { KeyboardEvent } from '../ink/events/keyboard-event.js' +// eslint-disable-next-line custom-rules/prefer-use-keybindings -- backward-compat bridge until consumers wire handleKeyDown to +import { useInput } from '../ink.js' import { Cursor, getLastKill, diff --git a/src/hooks/useTerminalSize.ts b/src/hooks/useTerminalSize.ts index 944a5b0e0..68e24df87 100644 --- a/src/hooks/useTerminalSize.ts +++ b/src/hooks/useTerminalSize.ts @@ -1,5 +1,8 @@ import { useContext } from 'react' -import { type TerminalSize, TerminalSizeContext } from '@anthropic/ink' +import { + type TerminalSize, + TerminalSizeContext, +} from 'src/ink/components/TerminalSizeContext.js' export function useTerminalSize(): TerminalSize { const size = useContext(TerminalSizeContext) diff --git a/src/hooks/useTextInput.ts b/src/hooks/useTextInput.ts index 21e0dbf19..90c4c4f82 100644 --- a/src/hooks/useTextInput.ts +++ b/src/hooks/useTextInput.ts @@ -3,7 +3,7 @@ import { useNotifications } from 'src/context/notifications.js' import stripAnsi from 'strip-ansi' import { markBackslashReturnUsed } from '../commands/terminalSetup/terminalSetup.js' import { addToHistory } from '../history.js' -import type { Key } from '@anthropic/ink' +import type { Key } from '../ink.js' import type { InlineGhostText, TextInputState, diff --git a/src/hooks/useTypeahead.tsx b/src/hooks/useTypeahead.tsx index 5e0a736bf..3e2dbd220 100644 --- a/src/hooks/useTypeahead.tsx +++ b/src/hooks/useTypeahead.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useNotifications } from 'src/context/notifications.js' -import { Text } from '@anthropic/ink' +import { Text } from 'src/ink.js' import { logEvent } from 'src/services/analytics/index.js' import { useDebounceCallback } from 'usehooks-ts' import { type Command, getCommandName } from '../commands.js' @@ -17,8 +17,9 @@ import { useIsModalOverlayActive, useRegisterOverlay, } from '../context/overlayContext.js' -import { KeyboardEvent, useInput } from '@anthropic/ink' -// backward-compat bridge until consumers wire handleKeyDown to +import { KeyboardEvent } from '../ink/events/keyboard-event.js' +// eslint-disable-next-line custom-rules/prefer-use-keybindings -- backward-compat bridge until consumers wire handleKeyDown to +import { useInput } from '../ink.js' import { useOptionalKeybindingContext, useRegisterKeybindingContext, diff --git a/src/hooks/useVimInput.ts b/src/hooks/useVimInput.ts index f49bb5003..0aabc9117 100644 --- a/src/hooks/useVimInput.ts +++ b/src/hooks/useVimInput.ts @@ -1,5 +1,5 @@ import React, { useCallback, useState } from 'react' -import type { Key } from '@anthropic/ink' +import type { Key } from '../ink.js' import type { VimInputState, VimMode } from '../types/textInputTypes.js' import { Cursor } from '../utils/Cursor.js' import { lastGrapheme } from '../utils/intl.js' diff --git a/src/hooks/useVirtualScroll.ts b/src/hooks/useVirtualScroll.ts index bdfe337dd..388b0bad9 100644 --- a/src/hooks/useVirtualScroll.ts +++ b/src/hooks/useVirtualScroll.ts @@ -7,7 +7,8 @@ import { useRef, useSyncExternalStore, } from 'react' -import type { ScrollBoxHandle, DOMElement } from '@anthropic/ink' +import type { ScrollBoxHandle } from '../ink/components/ScrollBox.js' +import type { DOMElement } from '../ink/dom.js' /** * Estimated height (rows) for items not yet measured. Intentionally LOW: diff --git a/src/hooks/useVoice.ts b/src/hooks/useVoice.ts index 0ac154e37..30c099170 100644 --- a/src/hooks/useVoice.ts +++ b/src/hooks/useVoice.ts @@ -8,7 +8,7 @@ import { useCallback, useEffect, useRef, useState } from 'react' import { useSetVoiceState } from '../context/voice.js' -import { useTerminalFocus } from '@anthropic/ink' +import { useTerminalFocus } from '../ink/hooks/use-terminal-focus.js' import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent, diff --git a/src/hooks/useVoiceIntegration.tsx b/src/hooks/useVoiceIntegration.tsx index b798a61ae..7cedb1c0f 100644 --- a/src/hooks/useVoiceIntegration.tsx +++ b/src/hooks/useVoiceIntegration.tsx @@ -8,8 +8,9 @@ import { useSetVoiceState, useVoiceState, } from '../context/voice.js' -import { KeyboardEvent, useInput } from '@anthropic/ink' -// backward-compat bridge until REPL wires handleKeyDown to +import { KeyboardEvent } from '../ink/events/keyboard-event.js' +// eslint-disable-next-line custom-rules/prefer-use-keybindings -- backward-compat bridge until REPL wires handleKeyDown to +import { useInput } from '../ink.js' import { useOptionalKeybindingContext } from '../keybindings/KeybindingContext.js' import { keystrokesEqual } from '../keybindings/resolver.js' import type { ParsedKeystroke } from '../keybindings/types.js' diff --git a/src/ink.ts b/src/ink.ts new file mode 100644 index 000000000..a06b343cc --- /dev/null +++ b/src/ink.ts @@ -0,0 +1,85 @@ +import { createElement, type ReactNode } from 'react' +import { ThemeProvider } from './components/design-system/ThemeProvider.js' +import inkRender, { + type Instance, + createRoot as inkCreateRoot, + type RenderOptions, + type Root, +} from './ink/root.js' + +export type { RenderOptions, Instance, Root } + +// Wrap all CC render calls with ThemeProvider so ThemedBox/ThemedText work +// without every call site having to mount it. Ink itself is theme-agnostic. +function withTheme(node: ReactNode): ReactNode { + return createElement(ThemeProvider, null, node) +} + +export async function render( + node: ReactNode, + options?: NodeJS.WriteStream | RenderOptions, +): Promise { + return inkRender(withTheme(node), options) +} + +export async function createRoot(options?: RenderOptions): Promise { + const root = await inkCreateRoot(options) + return { + ...root, + render: node => root.render(withTheme(node)), + } +} + +export { color } from './components/design-system/color.js' +export type { Props as BoxProps } from './components/design-system/ThemedBox.js' +export { default as Box } from './components/design-system/ThemedBox.js' +export type { Props as TextProps } from './components/design-system/ThemedText.js' +export { default as Text } from './components/design-system/ThemedText.js' +export { + ThemeProvider, + usePreviewTheme, + useTheme, + useThemeSetting, +} from './components/design-system/ThemeProvider.js' +export { Ansi } from './ink/Ansi.js' +export type { Props as AppProps } from './ink/components/AppContext.js' +export type { Props as BaseBoxProps } from './ink/components/Box.js' +export { default as BaseBox } from './ink/components/Box.js' +export type { + ButtonState, + Props as ButtonProps, +} from './ink/components/Button.js' +export { default as Button } from './ink/components/Button.js' +export type { Props as LinkProps } from './ink/components/Link.js' +export { default as Link } from './ink/components/Link.js' +export type { Props as NewlineProps } from './ink/components/Newline.js' +export { default as Newline } from './ink/components/Newline.js' +export { NoSelect } from './ink/components/NoSelect.js' +export { RawAnsi } from './ink/components/RawAnsi.js' +export { default as Spacer } from './ink/components/Spacer.js' +export type { Props as StdinProps } from './ink/components/StdinContext.js' +export type { Props as BaseTextProps } from './ink/components/Text.js' +export { default as BaseText } from './ink/components/Text.js' +export type { DOMElement } from './ink/dom.js' +export { ClickEvent } from './ink/events/click-event.js' +export { EventEmitter } from './ink/events/emitter.js' +export { Event } from './ink/events/event.js' +export type { Key } from './ink/events/input-event.js' +export { InputEvent } from './ink/events/input-event.js' +export type { TerminalFocusEventType } from './ink/events/terminal-focus-event.js' +export { TerminalFocusEvent } from './ink/events/terminal-focus-event.js' +export { FocusManager } from './ink/focus.js' +export type { FlickerReason } from './ink/frame.js' +export { useAnimationFrame } from './ink/hooks/use-animation-frame.js' +export { default as useApp } from './ink/hooks/use-app.js' +export { default as useInput } from './ink/hooks/use-input.js' +export { useAnimationTimer, useInterval } from './ink/hooks/use-interval.js' +export { useSelection } from './ink/hooks/use-selection.js' +export { default as useStdin } from './ink/hooks/use-stdin.js' +export { useTabStatus } from './ink/hooks/use-tab-status.js' +export { useTerminalFocus } from './ink/hooks/use-terminal-focus.js' +export { useTerminalTitle } from './ink/hooks/use-terminal-title.js' +export { useTerminalViewport } from './ink/hooks/use-terminal-viewport.js' +export { default as measureElement } from './ink/measure-element.js' +export { supportsTabStatus } from './ink/termio/osc.js' +export { default as wrapText } from './ink/wrap-text.js' diff --git a/packages/@ant/ink/src/core/Ansi.tsx b/src/ink/Ansi.tsx similarity index 98% rename from packages/@ant/ink/src/core/Ansi.tsx rename to src/ink/Ansi.tsx index a8a0999a0..f6ff7f7de 100644 --- a/packages/@ant/ink/src/core/Ansi.tsx +++ b/src/ink/Ansi.tsx @@ -1,6 +1,6 @@ import React from 'react' -import Link from '../components/Link.js' -import Text from '../components/Text.js' +import Link from './components/Link.js' +import Text from './components/Text.js' import type { Color } from './styles.js' import { type NamedColor, diff --git a/packages/@ant/ink/src/core/bidi.ts b/src/ink/bidi.ts similarity index 100% rename from packages/@ant/ink/src/core/bidi.ts rename to src/ink/bidi.ts diff --git a/packages/@ant/ink/src/core/clearTerminal.ts b/src/ink/clearTerminal.ts similarity index 100% rename from packages/@ant/ink/src/core/clearTerminal.ts rename to src/ink/clearTerminal.ts diff --git a/packages/@ant/ink/src/core/colorize.ts b/src/ink/colorize.ts similarity index 100% rename from packages/@ant/ink/src/core/colorize.ts rename to src/ink/colorize.ts diff --git a/packages/@ant/ink/src/components/AlternateScreen.tsx b/src/ink/components/AlternateScreen.tsx similarity index 95% rename from packages/@ant/ink/src/components/AlternateScreen.tsx rename to src/ink/components/AlternateScreen.tsx index 2e07a4cc7..eeeb1152e 100644 --- a/packages/@ant/ink/src/components/AlternateScreen.tsx +++ b/src/ink/components/AlternateScreen.tsx @@ -3,14 +3,14 @@ import React, { useContext, useInsertionEffect, } from 'react' -import instances from '../core/instances.js' +import instances from '../instances.js' import { DISABLE_MOUSE_TRACKING, ENABLE_MOUSE_TRACKING, ENTER_ALT_SCREEN, EXIT_ALT_SCREEN, -} from '../core/termio/dec.js' -import { TerminalWriteContext } from '../hooks/useTerminalNotification.js' +} from '../termio/dec.js' +import { TerminalWriteContext } from '../useTerminalNotification.js' import Box from './Box.js' import { TerminalSizeContext } from './TerminalSizeContext.js' diff --git a/packages/@ant/ink/src/components/App.tsx b/src/ink/components/App.tsx similarity index 93% rename from packages/@ant/ink/src/components/App.tsx rename to src/ink/components/App.tsx index 6796e1370..9bbb0c06a 100644 --- a/packages/@ant/ink/src/components/App.tsx +++ b/src/ink/components/App.tsx @@ -1,62 +1,37 @@ import React, { PureComponent, type ReactNode } from 'react' -// Business-layer callbacks — replaced with inline defaults so this package -// has zero dependencies on business code. The business layer can inject -// implementations via AppCallbacks when needed. -type AppCallbacks = { - updateLastInteractionTime?: () => void - stopCapturingEarlyInput?: () => void - isMouseClicksDisabled?: () => boolean - logError?: (error: unknown) => void - logForDebugging?: (message: string, opts?: { level?: string }) => void -} - -/** Default no-op / safe-default implementations */ -const defaultCallbacks: Required = { - updateLastInteractionTime: () => {}, - stopCapturingEarlyInput: () => {}, - isMouseClicksDisabled: () => false, - logError: (error: unknown) => console.error(error), - logForDebugging: (_message: string, _opts?: { level?: string }) => {}, -} - -/** - * Override the default no-op callbacks. Call this from the business layer - * (e.g. src/ink.tsx) before mounting . - */ -export function setAppCallbacks(cb: AppCallbacks): void { - Object.assign(defaultCallbacks, cb) -} - -function isEnvTruthy(value: string | undefined): boolean { - return value === '1' || value === 'true' -} -import { EventEmitter } from '../core/events/emitter.js' -import { InputEvent } from '../core/events/input-event.js' -import { TerminalFocusEvent } from '../core/events/terminal-focus-event.js' +import { updateLastInteractionTime } from '../../bootstrap/state.js' +import { logForDebugging } from '../../utils/debug.js' +import { stopCapturingEarlyInput } from '../../utils/earlyInput.js' +import { isEnvTruthy } from '../../utils/envUtils.js' +import { isMouseClicksDisabled } from '../../utils/fullscreen.js' +import { logError } from '../../utils/log.js' +import { EventEmitter } from '../events/emitter.js' +import { InputEvent } from '../events/input-event.js' +import { TerminalFocusEvent } from '../events/terminal-focus-event.js' import { INITIAL_STATE, type ParsedInput, type ParsedKey, type ParsedMouse, parseMultipleKeypresses, -} from '../core/parse-keypress.js' -import reconciler from '../core/reconciler.js' +} from '../parse-keypress.js' +import reconciler from '../reconciler.js' import { finishSelection, hasSelection, type SelectionState, startSelection, -} from '../core/selection.js' +} from '../selection.js' import { isXtermJs, setXtversionName, supportsExtendedKeys, -} from '../core/terminal.js' +} from '../terminal.js' import { getTerminalFocused, setTerminalFocused, -} from '../core/terminal-focus-state.js' -import { TerminalQuerier, xtversion } from '../core/terminal-querier.js' +} from '../terminal-focus-state.js' +import { TerminalQuerier, xtversion } from '../terminal-querier.js' import { DISABLE_KITTY_KEYBOARD, DISABLE_MODIFY_OTHER_KEYS, @@ -64,7 +39,7 @@ import { ENABLE_MODIFY_OTHER_KEYS, FOCUS_IN, FOCUS_OUT, -} from '../core/termio/csi.js' +} from '../termio/csi.js' import { DBP, DFE, @@ -73,7 +48,7 @@ import { EFE, HIDE_CURSOR, SHOW_CURSOR, -} from '../core/termio/dec.js' +} from '../termio/dec.js' import AppContext from './AppContext.js' import { ClockProvider } from './ClockContext.js' import CursorDeclarationContext, { @@ -317,7 +292,7 @@ export default class App extends PureComponent { // Both use the same stdin 'readable' + read() pattern, so they can't // coexist -- our handler would drain stdin before Ink's can see it. // The buffered text is preserved for REPL.tsx via consumeEarlyInput(). - defaultCallbacks.stopCapturingEarlyInput() + stopCapturingEarlyInput() stdin.ref() stdin.setRawMode(true) stdin.addListener('readable', this.handleReadable) @@ -349,9 +324,9 @@ export default class App extends PureComponent { ]).then(([r]) => { if (r) { setXtversionName(r.name) - defaultCallbacks.logForDebugging(`XTVERSION: terminal identified as "${r.name}"`) + logForDebugging(`XTVERSION: terminal identified as "${r.name}"`) } else { - defaultCallbacks.logForDebugging('XTVERSION: no reply (terminal ignored query)') + logForDebugging('XTVERSION: no reply (terminal ignored query)') } }) }) @@ -461,7 +436,7 @@ export default class App extends PureComponent { // permanently wedge the stream: data stays buffered and 'readable' // never re-emits. Catching here ensures the stream stays healthy so // subsequent keystrokes are still delivered. - defaultCallbacks.logError(error) + logError(error) // Re-attach the listener in case the exception detached it. // Bun may remove the listener after an error; without this, @@ -471,7 +446,7 @@ export default class App extends PureComponent { this.rawModeEnabledCount > 0 && !stdin.listeners('readable').includes(this.handleReadable) ) { - defaultCallbacks.logForDebugging( + logForDebugging( 'handleReadable: re-attaching stdin readable listener after error recovery', { level: 'warn' }, ) @@ -581,7 +556,7 @@ function processKeysInBatch( !((i.button & 0x20) !== 0 && (i.button & 0x03) === 3)), ) ) { - defaultCallbacks.updateLastInteractionTime() + updateLastInteractionTime() } for (const item of items) { @@ -650,7 +625,7 @@ function processKeysInBatch( export function handleMouseEvent(app: App, m: ParsedMouse): void { // Allow disabling click handling while keeping wheel scroll (which goes // through the keybinding system as 'wheelup'/'wheeldown', not here). - if (defaultCallbacks.isMouseClicksDisabled()) return + if (isMouseClicksDisabled()) return const sel = app.props.selection // Terminal coords are 1-indexed; screen buffer is 0-indexed diff --git a/packages/@ant/ink/src/components/AppContext.ts b/src/ink/components/AppContext.ts similarity index 100% rename from packages/@ant/ink/src/components/AppContext.ts rename to src/ink/components/AppContext.ts diff --git a/packages/@ant/ink/src/components/Box.tsx b/src/ink/components/Box.tsx similarity index 91% rename from packages/@ant/ink/src/components/Box.tsx rename to src/ink/components/Box.tsx index d07a2dd3d..42785f523 100644 --- a/packages/@ant/ink/src/components/Box.tsx +++ b/src/ink/components/Box.tsx @@ -1,11 +1,11 @@ import React, { type PropsWithChildren, type Ref } from 'react' import type { Except } from 'type-fest' -import type { DOMElement } from '../core/dom.js' -import type { ClickEvent } from '../core/events/click-event.js' -import type { FocusEvent } from '../core/events/focus-event.js' -import type { KeyboardEvent } from '../core/events/keyboard-event.js' -import type { Styles } from '../core/styles.js' -import * as warn from '../core/warn.js' +import type { DOMElement } from '../dom.js' +import type { ClickEvent } from '../events/click-event.js' +import type { FocusEvent } from '../events/focus-event.js' +import type { KeyboardEvent } from '../events/keyboard-event.js' +import type { Styles } from '../styles.js' +import * as warn from '../warn.js' export type Props = Except & { ref?: Ref diff --git a/packages/@ant/ink/src/components/Button.tsx b/src/ink/components/Button.tsx similarity index 90% rename from packages/@ant/ink/src/components/Button.tsx rename to src/ink/components/Button.tsx index 487c38e13..0095d9c59 100644 --- a/packages/@ant/ink/src/components/Button.tsx +++ b/src/ink/components/Button.tsx @@ -6,11 +6,11 @@ import React, { useState, } from 'react' import type { Except } from 'type-fest' -import type { DOMElement } from '../core/dom.js' -import type { ClickEvent } from '../core/events/click-event.js' -import type { FocusEvent } from '../core/events/focus-event.js' -import type { KeyboardEvent } from '../core/events/keyboard-event.js' -import type { Styles } from '../core/styles.js' +import type { DOMElement } from '../dom.js' +import type { ClickEvent } from '../events/click-event.js' +import type { FocusEvent } from '../events/focus-event.js' +import type { KeyboardEvent } from '../events/keyboard-event.js' +import type { Styles } from '../styles.js' import Box from './Box.js' type ButtonState = { diff --git a/packages/@ant/ink/src/components/ClockContext.tsx b/src/ink/components/ClockContext.tsx similarity index 98% rename from packages/@ant/ink/src/components/ClockContext.tsx rename to src/ink/components/ClockContext.tsx index 2822a84ba..32a8b9a28 100644 --- a/packages/@ant/ink/src/components/ClockContext.tsx +++ b/src/ink/components/ClockContext.tsx @@ -1,5 +1,5 @@ import React, { createContext, useEffect, useState } from 'react' -import { FRAME_INTERVAL_MS } from '../core/constants.js' +import { FRAME_INTERVAL_MS } from '../constants.js' import { useTerminalFocus } from '../hooks/use-terminal-focus.js' export type Clock = { diff --git a/packages/@ant/ink/src/components/CursorDeclarationContext.ts b/src/ink/components/CursorDeclarationContext.ts similarity index 95% rename from packages/@ant/ink/src/components/CursorDeclarationContext.ts rename to src/ink/components/CursorDeclarationContext.ts index eb5b4d0f9..358c80409 100644 --- a/packages/@ant/ink/src/components/CursorDeclarationContext.ts +++ b/src/ink/components/CursorDeclarationContext.ts @@ -1,5 +1,5 @@ import { createContext } from 'react' -import type { DOMElement } from '../core/dom.js' +import type { DOMElement } from '../dom.js' export type CursorDeclaration = { /** Display column (terminal cell width) within the declared node */ diff --git a/packages/@ant/ink/src/components/ErrorOverview.tsx b/src/ink/components/ErrorOverview.tsx similarity index 100% rename from packages/@ant/ink/src/components/ErrorOverview.tsx rename to src/ink/components/ErrorOverview.tsx diff --git a/packages/@ant/ink/src/components/Link.tsx b/src/ink/components/Link.tsx similarity index 90% rename from packages/@ant/ink/src/components/Link.tsx rename to src/ink/components/Link.tsx index c3ad1e2f3..ee7f04d14 100644 --- a/packages/@ant/ink/src/components/Link.tsx +++ b/src/ink/components/Link.tsx @@ -1,6 +1,6 @@ import type { ReactNode } from 'react' import React from 'react' -import { supportsHyperlinks } from '../core/supports-hyperlinks.js' +import { supportsHyperlinks } from '../supports-hyperlinks.js' import Text from './Text.js' export type Props = { diff --git a/packages/@ant/ink/src/components/Newline.tsx b/src/ink/components/Newline.tsx similarity index 100% rename from packages/@ant/ink/src/components/Newline.tsx rename to src/ink/components/Newline.tsx diff --git a/packages/@ant/ink/src/components/NoSelect.tsx b/src/ink/components/NoSelect.tsx similarity index 100% rename from packages/@ant/ink/src/components/NoSelect.tsx rename to src/ink/components/NoSelect.tsx diff --git a/packages/@ant/ink/src/components/RawAnsi.tsx b/src/ink/components/RawAnsi.tsx similarity index 100% rename from packages/@ant/ink/src/components/RawAnsi.tsx rename to src/ink/components/RawAnsi.tsx diff --git a/packages/@ant/ink/src/components/ScrollBox.tsx b/src/ink/components/ScrollBox.tsx similarity index 97% rename from packages/@ant/ink/src/components/ScrollBox.tsx rename to src/ink/components/ScrollBox.tsx index 371ab0ab0..c2d432be2 100644 --- a/packages/@ant/ink/src/components/ScrollBox.tsx +++ b/src/ink/components/ScrollBox.tsx @@ -6,10 +6,11 @@ import React, { useState, } from 'react' import type { Except } from 'type-fest' -import type { DOMElement } from '../core/dom.js' -import { markDirty, scheduleRenderFrom } from '../core/dom.js' -import { markCommitStart } from '../core/reconciler.js' -import type { Styles } from '../core/styles.js' +import { markScrollActivity } from '../../bootstrap/state.js' +import type { DOMElement } from '../dom.js' +import { markDirty, scheduleRenderFrom } from '../dom.js' +import { markCommitStart } from '../reconciler.js' +import type { Styles } from '../styles.js' import Box from './Box.js' export type ScrollBoxHandle = { @@ -115,7 +116,7 @@ function ScrollBox({ // Signal background intervals (IDE poll, LSP poll, GCS fetch, orphan // check) to skip their next tick — they compete for the event loop and // contributed to 1402ms max frame gaps during scroll drain. - // noop — injected by business layer via onScrollActivity callback + markScrollActivity() markDirty(el) markCommitStart() notify() diff --git a/packages/@ant/ink/src/components/Spacer.tsx b/src/ink/components/Spacer.tsx similarity index 100% rename from packages/@ant/ink/src/components/Spacer.tsx rename to src/ink/components/Spacer.tsx diff --git a/packages/@ant/ink/src/components/StdinContext.ts b/src/ink/components/StdinContext.ts similarity index 92% rename from packages/@ant/ink/src/components/StdinContext.ts rename to src/ink/components/StdinContext.ts index 34de48e5e..0b1a49717 100644 --- a/packages/@ant/ink/src/components/StdinContext.ts +++ b/src/ink/components/StdinContext.ts @@ -1,6 +1,6 @@ import { createContext } from 'react' -import { EventEmitter } from '../core/events/emitter.js' -import type { TerminalQuerier } from '../core/terminal-querier.js' +import { EventEmitter } from '../events/emitter.js' +import type { TerminalQuerier } from '../terminal-querier.js' export type Props = { /** diff --git a/packages/@ant/ink/src/components/TerminalFocusContext.tsx b/src/ink/components/TerminalFocusContext.tsx similarity index 97% rename from packages/@ant/ink/src/components/TerminalFocusContext.tsx rename to src/ink/components/TerminalFocusContext.tsx index e1fca2563..81dbaf60b 100644 --- a/packages/@ant/ink/src/components/TerminalFocusContext.tsx +++ b/src/ink/components/TerminalFocusContext.tsx @@ -4,7 +4,7 @@ import { getTerminalFocusState, subscribeTerminalFocus, type TerminalFocusState, -} from '../core/terminal-focus-state.js' +} from '../terminal-focus-state.js' export type { TerminalFocusState } diff --git a/packages/@ant/ink/src/components/TerminalSizeContext.tsx b/src/ink/components/TerminalSizeContext.tsx similarity index 100% rename from packages/@ant/ink/src/components/TerminalSizeContext.tsx rename to src/ink/components/TerminalSizeContext.tsx diff --git a/packages/@ant/ink/src/components/Text.tsx b/src/ink/components/Text.tsx similarity index 97% rename from packages/@ant/ink/src/components/Text.tsx rename to src/ink/components/Text.tsx index 620881450..f2e2bdb77 100644 --- a/packages/@ant/ink/src/components/Text.tsx +++ b/src/ink/components/Text.tsx @@ -1,6 +1,6 @@ import type { ReactNode } from 'react' import React from 'react' -import type { Color, Styles, TextStyles } from '../core/styles.js' +import type { Color, Styles, TextStyles } from '../styles.js' type BaseProps = { /** diff --git a/packages/@ant/ink/src/core/constants.ts b/src/ink/constants.ts similarity index 100% rename from packages/@ant/ink/src/core/constants.ts rename to src/ink/constants.ts diff --git a/packages/@ant/ink/src/core/cursor.ts b/src/ink/cursor.ts similarity index 100% rename from packages/@ant/ink/src/core/cursor.ts rename to src/ink/cursor.ts diff --git a/packages/@ant/ink/src/core/devtools.ts b/src/ink/devtools.ts similarity index 100% rename from packages/@ant/ink/src/core/devtools.ts rename to src/ink/devtools.ts diff --git a/packages/@ant/ink/src/core/dom.ts b/src/ink/dom.ts similarity index 100% rename from packages/@ant/ink/src/core/dom.ts rename to src/ink/dom.ts diff --git a/packages/@ant/ink/src/core/events/click-event.ts b/src/ink/events/click-event.ts similarity index 100% rename from packages/@ant/ink/src/core/events/click-event.ts rename to src/ink/events/click-event.ts diff --git a/packages/@ant/ink/src/core/events/dispatcher.ts b/src/ink/events/dispatcher.ts similarity index 97% rename from packages/@ant/ink/src/core/events/dispatcher.ts rename to src/ink/events/dispatcher.ts index 46466e88f..a310d389e 100644 --- a/packages/@ant/ink/src/core/events/dispatcher.ts +++ b/src/ink/events/dispatcher.ts @@ -4,14 +4,10 @@ import { DiscreteEventPriority, NoEventPriority, } from 'react-reconciler/constants.js' +import { logError } from '../../utils/log.js' import { HANDLER_FOR_EVENT } from './event-handlers.js' import type { EventTarget, TerminalEvent } from './terminal-event.js' -// logError stub — replaced from business dependency; use injected logger in production -const logError = (error: unknown): void => { - console.error(error) -} - // -- type DispatchListener = { diff --git a/packages/@ant/ink/src/core/events/emitter.ts b/src/ink/events/emitter.ts similarity index 100% rename from packages/@ant/ink/src/core/events/emitter.ts rename to src/ink/events/emitter.ts diff --git a/packages/@ant/ink/src/core/events/event-handlers.ts b/src/ink/events/event-handlers.ts similarity index 100% rename from packages/@ant/ink/src/core/events/event-handlers.ts rename to src/ink/events/event-handlers.ts diff --git a/packages/@ant/ink/src/core/events/event.ts b/src/ink/events/event.ts similarity index 100% rename from packages/@ant/ink/src/core/events/event.ts rename to src/ink/events/event.ts diff --git a/packages/@ant/ink/src/core/events/focus-event.ts b/src/ink/events/focus-event.ts similarity index 100% rename from packages/@ant/ink/src/core/events/focus-event.ts rename to src/ink/events/focus-event.ts diff --git a/packages/@ant/ink/src/core/events/input-event.ts b/src/ink/events/input-event.ts similarity index 100% rename from packages/@ant/ink/src/core/events/input-event.ts rename to src/ink/events/input-event.ts diff --git a/packages/@ant/ink/src/core/events/keyboard-event.ts b/src/ink/events/keyboard-event.ts similarity index 100% rename from packages/@ant/ink/src/core/events/keyboard-event.ts rename to src/ink/events/keyboard-event.ts diff --git a/packages/@ant/ink/src/core/events/paste-event.ts b/src/ink/events/paste-event.ts similarity index 100% rename from packages/@ant/ink/src/core/events/paste-event.ts rename to src/ink/events/paste-event.ts diff --git a/packages/@ant/ink/src/core/events/resize-event.ts b/src/ink/events/resize-event.ts similarity index 100% rename from packages/@ant/ink/src/core/events/resize-event.ts rename to src/ink/events/resize-event.ts diff --git a/packages/@ant/ink/src/core/events/terminal-event.ts b/src/ink/events/terminal-event.ts similarity index 100% rename from packages/@ant/ink/src/core/events/terminal-event.ts rename to src/ink/events/terminal-event.ts diff --git a/packages/@ant/ink/src/core/events/terminal-focus-event.ts b/src/ink/events/terminal-focus-event.ts similarity index 100% rename from packages/@ant/ink/src/core/events/terminal-focus-event.ts rename to src/ink/events/terminal-focus-event.ts diff --git a/packages/@ant/ink/src/core/focus.ts b/src/ink/focus.ts similarity index 100% rename from packages/@ant/ink/src/core/focus.ts rename to src/ink/focus.ts diff --git a/packages/@ant/ink/src/core/frame.ts b/src/ink/frame.ts similarity index 100% rename from packages/@ant/ink/src/core/frame.ts rename to src/ink/frame.ts diff --git a/packages/@ant/ink/src/core/get-max-width.ts b/src/ink/get-max-width.ts similarity index 100% rename from packages/@ant/ink/src/core/get-max-width.ts rename to src/ink/get-max-width.ts diff --git a/packages/@ant/ink/src/core/hit-test.ts b/src/ink/hit-test.ts similarity index 100% rename from packages/@ant/ink/src/core/hit-test.ts rename to src/ink/hit-test.ts diff --git a/packages/@ant/ink/src/hooks/use-animation-frame.ts b/src/ink/hooks/use-animation-frame.ts similarity index 97% rename from packages/@ant/ink/src/hooks/use-animation-frame.ts rename to src/ink/hooks/use-animation-frame.ts index a6ef6b4e2..d4dd38a16 100644 --- a/packages/@ant/ink/src/hooks/use-animation-frame.ts +++ b/src/ink/hooks/use-animation-frame.ts @@ -1,6 +1,6 @@ import { useContext, useEffect, useState } from 'react' import { ClockContext } from '../components/ClockContext.js' -import type { DOMElement } from '../core/dom.js' +import type { DOMElement } from '../dom.js' import { useTerminalViewport } from './use-terminal-viewport.js' /** diff --git a/packages/@ant/ink/src/hooks/use-app.ts b/src/ink/hooks/use-app.ts similarity index 100% rename from packages/@ant/ink/src/hooks/use-app.ts rename to src/ink/hooks/use-app.ts diff --git a/packages/@ant/ink/src/hooks/use-declared-cursor.ts b/src/ink/hooks/use-declared-cursor.ts similarity index 98% rename from packages/@ant/ink/src/hooks/use-declared-cursor.ts rename to src/ink/hooks/use-declared-cursor.ts index d1aea014c..e49668b38 100644 --- a/packages/@ant/ink/src/hooks/use-declared-cursor.ts +++ b/src/ink/hooks/use-declared-cursor.ts @@ -1,6 +1,6 @@ import { useCallback, useContext, useLayoutEffect, useRef } from 'react' import CursorDeclarationContext from '../components/CursorDeclarationContext.js' -import type { DOMElement } from '../core/dom.js' +import type { DOMElement } from '../dom.js' /** * Declares where the terminal cursor should be parked after each frame. diff --git a/packages/@ant/ink/src/hooks/use-input.ts b/src/ink/hooks/use-input.ts similarity index 97% rename from packages/@ant/ink/src/hooks/use-input.ts rename to src/ink/hooks/use-input.ts index 0d5cd55b7..7cf75b311 100644 --- a/packages/@ant/ink/src/hooks/use-input.ts +++ b/src/ink/hooks/use-input.ts @@ -1,6 +1,6 @@ import { useEffect, useLayoutEffect } from 'react' import { useEventCallback } from 'usehooks-ts' -import type { InputEvent, Key } from '../core/events/input-event.js' +import type { InputEvent, Key } from '../events/input-event.js' import useStdin from './use-stdin.js' type Handler = (input: string, key: Key, event: InputEvent) => void diff --git a/packages/@ant/ink/src/hooks/use-interval.ts b/src/ink/hooks/use-interval.ts similarity index 100% rename from packages/@ant/ink/src/hooks/use-interval.ts rename to src/ink/hooks/use-interval.ts diff --git a/packages/@ant/ink/src/hooks/use-search-highlight.ts b/src/ink/hooks/use-search-highlight.ts similarity index 92% rename from packages/@ant/ink/src/hooks/use-search-highlight.ts rename to src/ink/hooks/use-search-highlight.ts index 6c79d5ebe..ce9fc364d 100644 --- a/packages/@ant/ink/src/hooks/use-search-highlight.ts +++ b/src/ink/hooks/use-search-highlight.ts @@ -1,8 +1,8 @@ import { useContext, useMemo } from 'react' import StdinContext from '../components/StdinContext.js' -import type { DOMElement } from '../core/dom.js' -import instances from '../core/instances.js' -import type { MatchPosition } from '../core/render-to-screen.js' +import type { DOMElement } from '../dom.js' +import instances from '../instances.js' +import type { MatchPosition } from '../render-to-screen.js' /** * Set the search highlight query on the Ink instance. Non-empty → all diff --git a/packages/@ant/ink/src/hooks/use-selection.ts b/src/ink/hooks/use-selection.ts similarity index 98% rename from packages/@ant/ink/src/hooks/use-selection.ts rename to src/ink/hooks/use-selection.ts index 1a29e384d..f7e1d4571 100644 --- a/packages/@ant/ink/src/hooks/use-selection.ts +++ b/src/ink/hooks/use-selection.ts @@ -1,11 +1,11 @@ import { useContext, useMemo, useSyncExternalStore } from 'react' import StdinContext from '../components/StdinContext.js' -import instances from '../core/instances.js' +import instances from '../instances.js' import { type FocusMove, type SelectionState, shiftAnchor, -} from '../core/selection.js' +} from '../selection.js' /** * Access to text selection operations on the Ink instance (fullscreen only). diff --git a/packages/@ant/ink/src/hooks/use-stdin.ts b/src/ink/hooks/use-stdin.ts similarity index 100% rename from packages/@ant/ink/src/hooks/use-stdin.ts rename to src/ink/hooks/use-stdin.ts diff --git a/packages/@ant/ink/src/hooks/use-tab-status.ts b/src/ink/hooks/use-tab-status.ts similarity index 93% rename from packages/@ant/ink/src/hooks/use-tab-status.ts rename to src/ink/hooks/use-tab-status.ts index cfd32e253..be6014204 100644 --- a/packages/@ant/ink/src/hooks/use-tab-status.ts +++ b/src/ink/hooks/use-tab-status.ts @@ -4,9 +4,9 @@ import { supportsTabStatus, tabStatus, wrapForMultiplexer, -} from '../core/termio/osc.js' -import type { Color } from '../core/termio/types.js' -import { TerminalWriteContext } from './useTerminalNotification.js' +} from '../termio/osc.js' +import type { Color } from '../termio/types.js' +import { TerminalWriteContext } from '../useTerminalNotification.js' export type TabStatusKind = 'idle' | 'busy' | 'waiting' diff --git a/packages/@ant/ink/src/hooks/use-terminal-focus.ts b/src/ink/hooks/use-terminal-focus.ts similarity index 100% rename from packages/@ant/ink/src/hooks/use-terminal-focus.ts rename to src/ink/hooks/use-terminal-focus.ts diff --git a/packages/@ant/ink/src/hooks/use-terminal-title.ts b/src/ink/hooks/use-terminal-title.ts similarity index 88% rename from packages/@ant/ink/src/hooks/use-terminal-title.ts rename to src/ink/hooks/use-terminal-title.ts index 4645179a7..d820cd7ae 100644 --- a/packages/@ant/ink/src/hooks/use-terminal-title.ts +++ b/src/ink/hooks/use-terminal-title.ts @@ -1,7 +1,7 @@ import { useContext, useEffect } from 'react' import stripAnsi from 'strip-ansi' -import { OSC, osc } from '../core/termio/osc.js' -import { TerminalWriteContext } from './useTerminalNotification.js' +import { OSC, osc } from '../termio/osc.js' +import { TerminalWriteContext } from '../useTerminalNotification.js' /** * Declaratively set the terminal tab/window title. diff --git a/packages/@ant/ink/src/hooks/use-terminal-viewport.ts b/src/ink/hooks/use-terminal-viewport.ts similarity index 98% rename from packages/@ant/ink/src/hooks/use-terminal-viewport.ts rename to src/ink/hooks/use-terminal-viewport.ts index 82d96f341..91193bf73 100644 --- a/packages/@ant/ink/src/hooks/use-terminal-viewport.ts +++ b/src/ink/hooks/use-terminal-viewport.ts @@ -1,6 +1,6 @@ import { useCallback, useContext, useLayoutEffect, useRef } from 'react' import { TerminalSizeContext } from '../components/TerminalSizeContext.js' -import type { DOMElement } from '../core/dom.js' +import type { DOMElement } from '../dom.js' type ViewportEntry = { /** diff --git a/packages/@ant/ink/src/core/ink.tsx b/src/ink/ink.tsx similarity index 98% rename from packages/@ant/ink/src/core/ink.tsx rename to src/ink/ink.tsx index f18f0ddec..65bf32bd3 100644 --- a/packages/@ant/ink/src/core/ink.tsx +++ b/src/ink/ink.tsx @@ -12,14 +12,17 @@ import React, { type ReactNode } from 'react' import type { FiberRoot } from 'react-reconciler' import { ConcurrentRoot } from 'react-reconciler/constants.js' import { onExit } from 'signal-exit' -import { getYogaCounters } from './yoga-layout/index.js' +import { flushInteractionTime } from 'src/bootstrap/state.js' +import { getYogaCounters } from 'src/native-ts/yoga-layout/index.js' +import { logForDebugging } from 'src/utils/debug.js' +import { logError } from 'src/utils/log.js' import { format } from 'util' import { colorize } from './colorize.js' -import App from '../components/App.js' +import App from './components/App.js' import type { CursorDeclaration, CursorDeclarationSetter, -} from '../components/CursorDeclarationContext.js' +} from './components/CursorDeclarationContext.js' import { FRAME_INTERVAL_MS } from './constants.js' import * as dom from './dom.js' import { KeyboardEvent } from './events/keyboard-event.js' @@ -113,7 +116,7 @@ import { supportsTabStatus, wrapForMultiplexer, } from './termio/osc.js' -import { TerminalWriteProvider } from '../hooks/useTerminalNotification.js' +import { TerminalWriteProvider } from './useTerminalNotification.js' // Alt-screen: renderer.ts sets cursor.visible = !isTTY || screen.height===0, // which is always false in alt-screen (TTY + content fills screen). @@ -137,11 +140,6 @@ function makeAltScreenParkPatch(terminalRows: number) { }) } -export type Logger = { - debug(message: string, options?: { level?: string }): void - error(error: Error | unknown): void -} - export type Options = { stdout: NodeJS.WriteStream stdin: NodeJS.ReadStream @@ -150,16 +148,6 @@ export type Options = { patchConsole: boolean waitUntilExit?: () => Promise onFrame?: (event: FrameEvent) => void - /** Called before each render cycle. Replaces flushInteractionTime(). */ - onBeforeRender?: () => void - /** Injected logger. Replaces logForDebugging / logError imports. */ - logger?: Logger -} - -/** No-op logger used when no logger is injected. */ -const noopLogger: Logger = { - debug() {}, - error() {}, } export default class Ink { @@ -252,11 +240,9 @@ export default class Ink { // for log-update's relative-move invariants). Alt-screen doesn't need // this — every frame begins with CSI H. null = no move emitted last frame. private displayCursor: { x: number; y: number } | null = null - private readonly logger: Logger constructor(private readonly options: Options) { autoBind(this) - this.logger = options.logger ?? noopLogger if (this.options.patchConsole) { this.restoreConsole = this.patchConsole() @@ -547,7 +533,7 @@ export default class Ink { // Date.now() at most once per frame instead of once per keypress. // Done before the render to avoid dirtying state that would trigger // an extra React re-render cycle. - this.options.onBeforeRender?.() + flushInteractionTime() const renderStart = performance.now() const terminalWidth = this.options.stdout.columns || 80 @@ -768,7 +754,7 @@ export default class Ink { this.rootNode, patch.debug.triggerY, ) - this.logger.debug( + logForDebugging( `[REPAINT] full reset · ${patch.reason} · row ${patch.debug.triggerY}\n` + ` prev: "${patch.debug.prevLine}"\n` + ` next: "${patch.debug.nextLine}"\n` + @@ -1288,7 +1274,7 @@ export default class Ink { // correctly. One extra paint of this message, but correct > fast. dom.markDirty(el) const positions = scanPositions(rendered, this.searchHighlightQuery) - this.logger.debug( + logForDebugging( `scanElementSubtree: q='${this.searchHighlightQuery}' ` + `el=${width}x${height}@(${elLeft},${elTop}) n=${positions.length} ` + `[${positions @@ -1594,7 +1580,7 @@ export default class Ink { // Store and remove all 'readable' event listeners temporarily // This prevents Ink from consuming stdin while the editor is active const readableListeners = stdin.listeners('readable') - this.logger.debug( + logForDebugging( `[stdin] suspendStdin: removing ${readableListeners.length} readable listener(s), wasRawMode=${(stdin as NodeJS.ReadStream & { isRaw?: boolean }).isRaw ?? false}`, ) readableListeners.forEach(listener => { @@ -1624,12 +1610,12 @@ export default class Ink { // Re-attach all the stored listeners if (this.stdinListeners.length === 0 && !this.wasRawMode) { - this.logger.debug( + logForDebugging( '[stdin] resumeStdin: called with no stored listeners and wasRawMode=false (possible desync)', { level: 'warn' }, ) } - this.logger.debug( + logForDebugging( `[stdin] resumeStdin: re-attaching ${this.stdinListeners.length} listener(s), wasRawMode=${this.wasRawMode}`, ) this.stdinListeners.forEach(({ event, listener }) => { @@ -1847,9 +1833,9 @@ export default class Ink { const con = console const originals: Partial> = {} const toDebug = (...args: unknown[]) => - this.logger.debug(`console.log: ${format(...args)}`) + logForDebugging(`console.log: ${format(...args)}`) const toError = (...args: unknown[]) => - this.logger.error(new Error(`console.error: ${format(...args)}`)) + logError(new Error(`console.error: ${format(...args)}`)) for (const m of CONSOLE_STDOUT_METHODS) { originals[m] = con[m] con[m] = toDebug @@ -1887,7 +1873,7 @@ export default class Ink { cb?: (err?: Error) => void, ): boolean => { const callback = typeof encodingOrCb === 'function' ? encodingOrCb : cb - // Reentrancy guard: logger.debug → writeToStderr → here. Pass + // Reentrancy guard: logForDebugging → writeToStderr → here. Pass // through to the original so --debug-to-stderr still works and we // don't stack-overflow. if (reentered) { @@ -1901,7 +1887,7 @@ export default class Ink { typeof chunk === 'string' ? chunk : Buffer.from(chunk).toString('utf8') - this.logger.debug(`[stderr] ${text}`, { level: 'warn' }) + logForDebugging(`[stderr] ${text}`, { level: 'warn' }) if (this.altScreenActive && !this.isUnmounted && !this.isPaused) { this.prevFrameContaminated = true this.scheduleRender() diff --git a/packages/@ant/ink/src/core/instances.ts b/src/ink/instances.ts similarity index 100% rename from packages/@ant/ink/src/core/instances.ts rename to src/ink/instances.ts diff --git a/packages/@ant/ink/src/core/layout/engine.ts b/src/ink/layout/engine.ts similarity index 100% rename from packages/@ant/ink/src/core/layout/engine.ts rename to src/ink/layout/engine.ts diff --git a/packages/@ant/ink/src/core/layout/geometry.ts b/src/ink/layout/geometry.ts similarity index 100% rename from packages/@ant/ink/src/core/layout/geometry.ts rename to src/ink/layout/geometry.ts diff --git a/packages/@ant/ink/src/core/layout/node.ts b/src/ink/layout/node.ts similarity index 100% rename from packages/@ant/ink/src/core/layout/node.ts rename to src/ink/layout/node.ts diff --git a/packages/@ant/ink/src/core/layout/yoga.ts b/src/ink/layout/yoga.ts similarity index 99% rename from packages/@ant/ink/src/core/layout/yoga.ts rename to src/ink/layout/yoga.ts index 1d5729e8c..58f2646fb 100644 --- a/packages/@ant/ink/src/core/layout/yoga.ts +++ b/src/ink/layout/yoga.ts @@ -11,7 +11,7 @@ import Yoga, { PositionType, Wrap, type Node as YogaNode, -} from '../yoga-layout/index.js' +} from 'src/native-ts/yoga-layout/index.js' import { type LayoutAlign, LayoutDisplay, diff --git a/packages/@ant/ink/src/core/line-width-cache.ts b/src/ink/line-width-cache.ts similarity index 100% rename from packages/@ant/ink/src/core/line-width-cache.ts rename to src/ink/line-width-cache.ts diff --git a/packages/@ant/ink/src/core/log-update.ts b/src/ink/log-update.ts similarity index 99% rename from packages/@ant/ink/src/core/log-update.ts rename to src/ink/log-update.ts index 210b1e313..4434b9418 100644 --- a/packages/@ant/ink/src/core/log-update.ts +++ b/src/ink/log-update.ts @@ -3,8 +3,7 @@ import { ansiCodesToString, diffAnsiCodes, } from '@alcalzone/ansi-tokenize' -/** Debug logger — no-op placeholder until proper logger injection is added */ -const logForDebugging = (_message: string) => {} +import { logForDebugging } from '../utils/debug.js' import type { Diff, FlickerReason, Frame } from './frame.js' import type { Point } from './layout/geometry.js' import { diff --git a/packages/@ant/ink/src/core/measure-element.ts b/src/ink/measure-element.ts similarity index 100% rename from packages/@ant/ink/src/core/measure-element.ts rename to src/ink/measure-element.ts diff --git a/packages/@ant/ink/src/core/measure-text.ts b/src/ink/measure-text.ts similarity index 100% rename from packages/@ant/ink/src/core/measure-text.ts rename to src/ink/measure-text.ts diff --git a/packages/@ant/ink/src/core/node-cache.ts b/src/ink/node-cache.ts similarity index 100% rename from packages/@ant/ink/src/core/node-cache.ts rename to src/ink/node-cache.ts diff --git a/packages/@ant/ink/src/core/optimizer.ts b/src/ink/optimizer.ts similarity index 100% rename from packages/@ant/ink/src/core/optimizer.ts rename to src/ink/optimizer.ts diff --git a/packages/@ant/ink/src/core/output.ts b/src/ink/output.ts similarity index 99% rename from packages/@ant/ink/src/core/output.ts rename to src/ink/output.ts index edee080c9..16b5ae27f 100644 --- a/packages/@ant/ink/src/core/output.ts +++ b/src/ink/output.ts @@ -4,11 +4,9 @@ import { styledCharsFromTokens, tokenize, } from '@alcalzone/ansi-tokenize' -import { getGraphemeSegmenter } from './utils/grapheme.js' -import sliceAnsi from './utils/sliceAnsi.js' - -/** Debug logger — no-op placeholder until proper logger injection is added */ -const logForDebugging = (_message: string) => {} +import { logForDebugging } from '../utils/debug.js' +import { getGraphemeSegmenter } from '../utils/intl.js' +import sliceAnsi from '../utils/sliceAnsi.js' import { reorderBidi } from './bidi.js' import { type Rectangle, unionRect } from './layout/geometry.js' import { diff --git a/packages/@ant/ink/src/core/parse-keypress.ts b/src/ink/parse-keypress.ts similarity index 100% rename from packages/@ant/ink/src/core/parse-keypress.ts rename to src/ink/parse-keypress.ts diff --git a/packages/@ant/ink/src/core/reconciler.ts b/src/ink/reconciler.ts similarity index 93% rename from packages/@ant/ink/src/core/reconciler.ts rename to src/ink/reconciler.ts index 4d75923b8..831366f7a 100644 --- a/packages/@ant/ink/src/core/reconciler.ts +++ b/src/ink/reconciler.ts @@ -1,7 +1,9 @@ /* eslint-disable custom-rules/no-top-level-side-effects */ +import { appendFileSync } from 'fs' import createReconciler from 'react-reconciler' -import { getYogaCounters } from './yoga-layout/index.js' +import { getYogaCounters } from 'src/native-ts/yoga-layout/index.js' +import { isEnvTruthy } from '../utils/envUtils.js' import { appendChildNode, clearYogaNodeReferences, @@ -177,16 +179,15 @@ export function getOwnerChain(fiber: unknown): string[] { let debugRepaints: boolean | undefined export function isDebugRepaintsEnabled(): boolean { if (debugRepaints === undefined) { - debugRepaints = process.env.CLAUDE_CODE_DEBUG_REPAINTS === '1' + debugRepaints = isEnvTruthy(process.env.CLAUDE_CODE_DEBUG_REPAINTS) } return debugRepaints } export const dispatcher = new Dispatcher() -// --- COMMIT INSTRUMENTATION (debug logging) --- -// Uses console.warn instead of fs.appendFileSync to avoid filesystem dependencies. -// Set CLAUDE_CODE_COMMIT_LOG=1 to enable debug logging to stderr. +// --- COMMIT INSTRUMENTATION (temp debugging) --- +// eslint-disable-next-line custom-rules/no-process-env-top-level -- debug instrumentation, read-once is fine const COMMIT_LOG = process.env.CLAUDE_CODE_COMMIT_LOG let _commits = 0 let _lastLog = 0 @@ -194,12 +195,6 @@ let _lastCommitAt = 0 let _maxGapMs = 0 let _createCount = 0 let _prepareAt = 0 - -/** Debug log helper — replaces fs.appendFileSync with console.warn. */ -function debugLog(message: string): void { - // biome-ignore lint/suspicious/noConsole: debug instrumentation - console.warn(`[ink-commit] ${message}`) -} // --- END --- // --- SCROLL PROFILING (bench/scroll-e2e.sh reads via getLastYogaMs) --- @@ -260,14 +255,18 @@ const reconciler = createReconciler< _lastCommitAt = now const reconcileMs = _prepareAt > 0 ? now - _prepareAt : 0 if (gap > 30 || reconcileMs > 20 || _createCount > 50) { - debugLog( - `${now.toFixed(1)} gap=${gap.toFixed(1)}ms reconcile=${reconcileMs.toFixed(1)}ms creates=${_createCount}`, + // eslint-disable-next-line custom-rules/no-sync-fs -- debug instrumentation + appendFileSync( + COMMIT_LOG, + `${now.toFixed(1)} gap=${gap.toFixed(1)}ms reconcile=${reconcileMs.toFixed(1)}ms creates=${_createCount}\n`, ) } _createCount = 0 if (now - _lastLog > 1000) { - debugLog( - `${now.toFixed(1)} commits=${_commits}/s maxGap=${_maxGapMs.toFixed(1)}ms`, + // eslint-disable-next-line custom-rules/no-sync-fs -- debug instrumentation + appendFileSync( + COMMIT_LOG, + `${now.toFixed(1)} commits=${_commits}/s maxGap=${_maxGapMs.toFixed(1)}ms\n`, ) _commits = 0 _maxGapMs = 0 @@ -282,8 +281,10 @@ const reconciler = createReconciler< const layoutMs = performance.now() - _t0 if (layoutMs > 20) { const c = getYogaCounters() - debugLog( - `${_t0.toFixed(1)} SLOW_YOGA ${layoutMs.toFixed(1)}ms visited=${c.visited} measured=${c.measured} hits=${c.cacheHits} live=${c.live}`, + // eslint-disable-next-line custom-rules/no-sync-fs -- debug instrumentation + appendFileSync( + COMMIT_LOG, + `${_t0.toFixed(1)} SLOW_YOGA ${layoutMs.toFixed(1)}ms visited=${c.visited} measured=${c.measured} hits=${c.cacheHits} live=${c.live}\n`, ) } } @@ -304,8 +305,10 @@ const reconciler = createReconciler< if (COMMIT_LOG) { const renderMs = performance.now() - _tr if (renderMs > 10) { - debugLog( - `${_tr.toFixed(1)} SLOW_PAINT ${renderMs.toFixed(1)}ms`, + // eslint-disable-next-line custom-rules/no-sync-fs -- debug instrumentation + appendFileSync( + COMMIT_LOG, + `${_tr.toFixed(1)} SLOW_PAINT ${renderMs.toFixed(1)}ms\n`, ) } } diff --git a/packages/@ant/ink/src/core/render-border.ts b/src/ink/render-border.ts similarity index 100% rename from packages/@ant/ink/src/core/render-border.ts rename to src/ink/render-border.ts diff --git a/packages/@ant/ink/src/core/render-node-to-output.ts b/src/ink/render-node-to-output.ts similarity index 100% rename from packages/@ant/ink/src/core/render-node-to-output.ts rename to src/ink/render-node-to-output.ts diff --git a/packages/@ant/ink/src/core/render-to-screen.ts b/src/ink/render-to-screen.ts similarity index 98% rename from packages/@ant/ink/src/core/render-to-screen.ts rename to src/ink/render-to-screen.ts index dcf6407c6..0992dc946 100644 --- a/packages/@ant/ink/src/core/render-to-screen.ts +++ b/src/ink/render-to-screen.ts @@ -1,8 +1,7 @@ import noop from 'lodash-es/noop.js' import type { ReactElement } from 'react' import { LegacyRoot } from 'react-reconciler/constants.js' -/** Debug logger — no-op placeholder until proper logger injection is added */ -const logForDebugging = (_message: string) => {} +import { logForDebugging } from '../utils/debug.js' import { createNode, type DOMElement } from './dom.js' import { FocusManager } from './focus.js' import Output from './output.js' diff --git a/packages/@ant/ink/src/core/renderer.ts b/src/ink/renderer.ts similarity index 97% rename from packages/@ant/ink/src/core/renderer.ts rename to src/ink/renderer.ts index aa2cdeaa3..d87fb3db9 100644 --- a/packages/@ant/ink/src/core/renderer.ts +++ b/src/ink/renderer.ts @@ -1,5 +1,4 @@ -/** Debug logger — no-op placeholder until proper logger injection is added */ -const logForDebugging = (_message: string, _opts?: { level?: string }) => {} +import { logForDebugging } from 'src/utils/debug.js' import { type DOMElement, markDirty } from './dom.js' import type { Frame } from './frame.js' import { consumeAbsoluteRemovedFlag } from './node-cache.js' diff --git a/packages/@ant/ink/src/core/root.ts b/src/ink/root.ts similarity index 94% rename from packages/@ant/ink/src/core/root.ts rename to src/ink/root.ts index 36df98c70..067bbd496 100644 --- a/packages/@ant/ink/src/core/root.ts +++ b/src/ink/root.ts @@ -1,4 +1,5 @@ import type { ReactNode } from 'react' +import { logForDebugging } from 'src/utils/debug.js' import { Stream } from 'stream' import type { FrameEvent } from './frame.js' import Ink, { type Options as InkOptions } from './ink.js' @@ -113,12 +114,9 @@ const wrappedRender = async ( // write overwrites scrollback instead of appending below the logo. await Promise.resolve() const instance = renderSync(node, options) - if (process.env.CLAUDE_CODE_DEBUG_REPAINTS === '1') { - // biome-ignore lint/suspicious/noConsole: debug instrumentation - console.warn( - `[render] first ink render: ${Math.round(process.uptime() * 1000)}ms since process start`, - ) - } + logForDebugging( + `[render] first ink render: ${Math.round(process.uptime() * 1000)}ms since process start`, + ) return instance } diff --git a/packages/@ant/ink/src/core/screen.ts b/src/ink/screen.ts similarity index 100% rename from packages/@ant/ink/src/core/screen.ts rename to src/ink/screen.ts diff --git a/packages/@ant/ink/src/core/searchHighlight.ts b/src/ink/searchHighlight.ts similarity index 100% rename from packages/@ant/ink/src/core/searchHighlight.ts rename to src/ink/searchHighlight.ts diff --git a/packages/@ant/ink/src/core/selection.ts b/src/ink/selection.ts similarity index 100% rename from packages/@ant/ink/src/core/selection.ts rename to src/ink/selection.ts diff --git a/packages/@ant/ink/src/core/squash-text-nodes.ts b/src/ink/squash-text-nodes.ts similarity index 100% rename from packages/@ant/ink/src/core/squash-text-nodes.ts rename to src/ink/squash-text-nodes.ts diff --git a/src/ink/src/bootstrap/state.ts b/src/ink/src/bootstrap/state.ts new file mode 100644 index 000000000..875ce2bdc --- /dev/null +++ b/src/ink/src/bootstrap/state.ts @@ -0,0 +1,2 @@ +// Auto-generated type stub — replace with real implementation +export type flushInteractionTime = any; diff --git a/src/ink/src/native-ts/yoga-layout/index.ts b/src/ink/src/native-ts/yoga-layout/index.ts new file mode 100644 index 000000000..c75a2b090 --- /dev/null +++ b/src/ink/src/native-ts/yoga-layout/index.ts @@ -0,0 +1,2 @@ +// Auto-generated type stub — replace with real implementation +export type getYogaCounters = any; diff --git a/src/ink/src/utils/debug.ts b/src/ink/src/utils/debug.ts new file mode 100644 index 000000000..c64d5960c --- /dev/null +++ b/src/ink/src/utils/debug.ts @@ -0,0 +1,2 @@ +// Auto-generated type stub — replace with real implementation +export type logForDebugging = any; diff --git a/src/ink/src/utils/log.ts b/src/ink/src/utils/log.ts new file mode 100644 index 000000000..cf30e90da --- /dev/null +++ b/src/ink/src/utils/log.ts @@ -0,0 +1,2 @@ +// Auto-generated type stub — replace with real implementation +export type logError = any; diff --git a/packages/@ant/ink/src/core/stringWidth.ts b/src/ink/stringWidth.ts similarity index 99% rename from packages/@ant/ink/src/core/stringWidth.ts rename to src/ink/stringWidth.ts index 2c3fa13b8..83f7bcb5e 100644 --- a/packages/@ant/ink/src/core/stringWidth.ts +++ b/src/ink/stringWidth.ts @@ -1,7 +1,7 @@ import emojiRegex from 'emoji-regex' import { eastAsianWidth } from 'get-east-asian-width' import stripAnsi from 'strip-ansi' -import { getGraphemeSegmenter } from './utils/grapheme.js' +import { getGraphemeSegmenter } from '../utils/intl.js' const EMOJI_REGEX = emojiRegex() diff --git a/packages/@ant/ink/src/core/styles.ts b/src/ink/styles.ts similarity index 100% rename from packages/@ant/ink/src/core/styles.ts rename to src/ink/styles.ts diff --git a/packages/@ant/ink/src/core/supports-hyperlinks.ts b/src/ink/supports-hyperlinks.ts similarity index 100% rename from packages/@ant/ink/src/core/supports-hyperlinks.ts rename to src/ink/supports-hyperlinks.ts diff --git a/packages/@ant/ink/src/core/tabstops.ts b/src/ink/tabstops.ts similarity index 100% rename from packages/@ant/ink/src/core/tabstops.ts rename to src/ink/tabstops.ts diff --git a/packages/@ant/ink/src/core/terminal-focus-state.ts b/src/ink/terminal-focus-state.ts similarity index 100% rename from packages/@ant/ink/src/core/terminal-focus-state.ts rename to src/ink/terminal-focus-state.ts diff --git a/packages/@ant/ink/src/core/terminal-querier.ts b/src/ink/terminal-querier.ts similarity index 100% rename from packages/@ant/ink/src/core/terminal-querier.ts rename to src/ink/terminal-querier.ts diff --git a/packages/@ant/ink/src/core/terminal.ts b/src/ink/terminal.ts similarity index 97% rename from packages/@ant/ink/src/core/terminal.ts rename to src/ink/terminal.ts index 1331376c3..2aad947d7 100644 --- a/packages/@ant/ink/src/core/terminal.ts +++ b/src/ink/terminal.ts @@ -1,5 +1,7 @@ -import { coerce, gte } from 'semver' +import { coerce } from 'semver' import type { Writable } from 'stream' +import { env } from '../utils/env.js' +import { gte } from '../utils/semver.js' import { getClearTerminalSequence } from './clearTerminal.js' import type { Diff } from './frame.js' import { cursorMove, cursorTo, eraseLines } from './termio/csi.js' @@ -163,7 +165,7 @@ const EXTENDED_KEYS_TERMINALS = [ /** True if this terminal correctly handles extended key reporting * (Kitty keyboard protocol + xterm modifyOtherKeys). */ export function supportsExtendedKeys(): boolean { - return EXTENDED_KEYS_TERMINALS.includes(process.env.TERM_PROGRAM ?? '') + return EXTENDED_KEYS_TERMINALS.includes(env.terminal ?? '') } /** True if the terminal scrolls the viewport when it receives cursor-up diff --git a/packages/@ant/ink/src/core/termio.ts b/src/ink/termio.ts similarity index 100% rename from packages/@ant/ink/src/core/termio.ts rename to src/ink/termio.ts diff --git a/packages/@ant/ink/src/core/termio/ansi.ts b/src/ink/termio/ansi.ts similarity index 100% rename from packages/@ant/ink/src/core/termio/ansi.ts rename to src/ink/termio/ansi.ts diff --git a/packages/@ant/ink/src/core/termio/csi.ts b/src/ink/termio/csi.ts similarity index 100% rename from packages/@ant/ink/src/core/termio/csi.ts rename to src/ink/termio/csi.ts diff --git a/packages/@ant/ink/src/core/termio/dec.ts b/src/ink/termio/dec.ts similarity index 100% rename from packages/@ant/ink/src/core/termio/dec.ts rename to src/ink/termio/dec.ts diff --git a/packages/@ant/ink/src/core/termio/esc.ts b/src/ink/termio/esc.ts similarity index 100% rename from packages/@ant/ink/src/core/termio/esc.ts rename to src/ink/termio/esc.ts diff --git a/packages/@ant/ink/src/core/termio/osc.ts b/src/ink/termio/osc.ts similarity index 95% rename from packages/@ant/ink/src/core/termio/osc.ts rename to src/ink/termio/osc.ts index f5e6f3712..9bef51532 100644 --- a/packages/@ant/ink/src/core/termio/osc.ts +++ b/src/ink/termio/osc.ts @@ -3,26 +3,9 @@ */ import { Buffer } from 'buffer' -import { execFile as nodeExecFile } from 'child_process' +import { env } from '../../utils/env.js' +import { execFileNoThrow } from '../../utils/execFileNoThrow.js' import { BEL, ESC, ESC_TYPE, SEP } from './ansi.js' - -/** Promise-based wrapper around child_process.execFile */ -function execFileNoThrow( - command: string, - args: string[], - options: { input?: string; useCwd?: boolean; timeout?: number } = {}, -): Promise<{ code: number; stdout: string; stderr: string }> { - return new Promise(resolve => { - const { input, timeout } = options - const proc = nodeExecFile(command, args, { timeout }, (error, stdout, stderr) => { - resolve({ code: error ? 1 : 0, stdout: stdout ?? '', stderr: stderr ?? '' }) - }) - if (input && proc.stdin) { - proc.stdin.write(input) - proc.stdin.end() - } - }) -} import type { Action, Color, TabStatusAction } from './types.js' export const OSC_PREFIX = ESC + String.fromCharCode(ESC_TYPE.OSC) @@ -33,7 +16,7 @@ export const ST = ESC + '\\' /** Generate an OSC sequence: ESC ] p1;p2;...;pN * Uses ST terminator for Kitty (avoids beeps), BEL for others */ export function osc(...parts: (string | number)[]): string { - const terminator = process.env.TERM_PROGRAM === 'kitty' ? ST : BEL + const terminator = env.terminal === 'kitty' ? ST : BEL return `${OSC_PREFIX}${parts.join(SEP)}${terminator}` } diff --git a/packages/@ant/ink/src/core/termio/parser.ts b/src/ink/termio/parser.ts similarity index 99% rename from packages/@ant/ink/src/core/termio/parser.ts rename to src/ink/termio/parser.ts index 0ae9e7fa5..301f14c5a 100644 --- a/packages/@ant/ink/src/core/termio/parser.ts +++ b/src/ink/termio/parser.ts @@ -11,7 +11,7 @@ * - Style tracking: maintains current text style state */ -import { getGraphemeSegmenter } from '../utils/grapheme.js' +import { getGraphemeSegmenter } from '../../utils/intl.js' import { C0 } from './ansi.js' import { CSI, CURSOR_STYLES, ERASE_DISPLAY, ERASE_LINE_REGION } from './csi.js' import { DEC } from './dec.js' diff --git a/packages/@ant/ink/src/core/termio/sgr.ts b/src/ink/termio/sgr.ts similarity index 100% rename from packages/@ant/ink/src/core/termio/sgr.ts rename to src/ink/termio/sgr.ts diff --git a/packages/@ant/ink/src/core/termio/tokenize.ts b/src/ink/termio/tokenize.ts similarity index 100% rename from packages/@ant/ink/src/core/termio/tokenize.ts rename to src/ink/termio/tokenize.ts diff --git a/packages/@ant/ink/src/core/termio/types.ts b/src/ink/termio/types.ts similarity index 100% rename from packages/@ant/ink/src/core/termio/types.ts rename to src/ink/termio/types.ts diff --git a/packages/@ant/ink/src/hooks/useTerminalNotification.ts b/src/ink/useTerminalNotification.ts similarity index 96% rename from packages/@ant/ink/src/hooks/useTerminalNotification.ts rename to src/ink/useTerminalNotification.ts index 083a94ad0..90e53eb63 100644 --- a/packages/@ant/ink/src/hooks/useTerminalNotification.ts +++ b/src/ink/useTerminalNotification.ts @@ -1,7 +1,7 @@ import { createContext, useCallback, useContext, useMemo } from 'react' -import { isProgressReportingAvailable, type Progress } from '../core/terminal.js' -import { BEL } from '../core/termio/ansi.js' -import { ITERM2, OSC, osc, PROGRESS, wrapForMultiplexer } from '../core/termio/osc.js' +import { isProgressReportingAvailable, type Progress } from './terminal.js' +import { BEL } from './termio/ansi.js' +import { ITERM2, OSC, osc, PROGRESS, wrapForMultiplexer } from './termio/osc.js' type WriteRaw = (data: string) => void diff --git a/packages/@ant/ink/src/core/warn.ts b/src/ink/warn.ts similarity index 100% rename from packages/@ant/ink/src/core/warn.ts rename to src/ink/warn.ts diff --git a/packages/@ant/ink/src/core/widest-line.ts b/src/ink/widest-line.ts similarity index 100% rename from packages/@ant/ink/src/core/widest-line.ts rename to src/ink/widest-line.ts diff --git a/packages/@ant/ink/src/core/wrap-text.ts b/src/ink/wrap-text.ts similarity index 97% rename from packages/@ant/ink/src/core/wrap-text.ts rename to src/ink/wrap-text.ts index b97f64235..434412cc9 100644 --- a/packages/@ant/ink/src/core/wrap-text.ts +++ b/src/ink/wrap-text.ts @@ -1,4 +1,4 @@ -import sliceAnsi from './utils/sliceAnsi.js' +import sliceAnsi from '../utils/sliceAnsi.js' import { stringWidth } from './stringWidth.js' import type { Styles } from './styles.js' import { wrapAnsi } from './wrapAnsi.js' diff --git a/packages/@ant/ink/src/core/wrapAnsi.ts b/src/ink/wrapAnsi.ts similarity index 100% rename from packages/@ant/ink/src/core/wrapAnsi.ts rename to src/ink/wrapAnsi.ts diff --git a/src/interactiveHelpers.tsx b/src/interactiveHelpers.tsx index 3c9946b29..72bf5e7be 100644 --- a/src/interactiveHelpers.tsx +++ b/src/interactiveHelpers.tsx @@ -18,8 +18,8 @@ import type { Command } from './commands.js' import { createStatsStore, type StatsStore } from './context/stats.js' import { getSystemContext } from './context.js' import { initializeTelemetryAfterTrust } from './entrypoints/init.js' -import { isSynchronizedOutputSupported } from '@anthropic/ink' -import type { RenderOptions, Root, TextProps } from '@anthropic/ink' +import { isSynchronizedOutputSupported } from './ink/terminal.js' +import type { RenderOptions, Root, TextProps } from './ink.js' import { KeybindingSetup } from './keybindings/KeybindingProviderSetup.js' import { startDeferredPrefetches } from './main.js' import { @@ -102,7 +102,7 @@ export async function exitWithMessage( beforeExit?: () => Promise }, ): Promise { - const { Text } = await import('@anthropic/ink') + const { Text } = await import('./ink.js') const color = options?.color const exitCode = options?.exitCode ?? 1 root.render( diff --git a/src/keybindings/KeybindingContext.tsx b/src/keybindings/KeybindingContext.tsx index edfedad54..bc85e81c0 100644 --- a/src/keybindings/KeybindingContext.tsx +++ b/src/keybindings/KeybindingContext.tsx @@ -5,7 +5,7 @@ import React, { useLayoutEffect, useMemo, } from 'react' -import type { Key } from '@anthropic/ink' +import type { Key } from '../ink.js' import { type ChordResolveResult, getBindingDisplayText, diff --git a/src/keybindings/KeybindingProviderSetup.tsx b/src/keybindings/KeybindingProviderSetup.tsx index bb0f7ddca..2397468c8 100644 --- a/src/keybindings/KeybindingProviderSetup.tsx +++ b/src/keybindings/KeybindingProviderSetup.tsx @@ -8,11 +8,11 @@ */ import React, { useCallback, useEffect, useRef, useState } from 'react' import { useNotifications } from '../context/notifications.js' -import type { InputEvent } from '@anthropic/ink' +import type { InputEvent } from '../ink/events/input-event.js' // ChordInterceptor intentionally uses useInput to intercept all keystrokes before // other handlers process them - this is required for chord sequence support // eslint-disable-next-line custom-rules/prefer-use-keybindings -import { type Key, useInput } from '@anthropic/ink' +import { type Key, useInput } from '../ink.js' import { count } from '../utils/array.js' import { logForDebugging } from '../utils/debug.js' import { plural } from '../utils/stringUtils.js' diff --git a/src/keybindings/match.ts b/src/keybindings/match.ts index c915f328e..2b407173f 100644 --- a/src/keybindings/match.ts +++ b/src/keybindings/match.ts @@ -1,4 +1,4 @@ -import type { Key } from '@anthropic/ink' +import type { Key } from '../ink.js' import type { ParsedBinding, ParsedKeystroke } from './types.js' /** diff --git a/src/keybindings/resolver.ts b/src/keybindings/resolver.ts index f58babeb6..746404957 100644 --- a/src/keybindings/resolver.ts +++ b/src/keybindings/resolver.ts @@ -1,4 +1,4 @@ -import type { Key } from '@anthropic/ink' +import type { Key } from '../ink.js' import { getKeyName, matchesBinding } from './match.js' import { chordToString } from './parser.js' import type { diff --git a/src/keybindings/useKeybinding.ts b/src/keybindings/useKeybinding.ts index 1068852be..02b07ce19 100644 --- a/src/keybindings/useKeybinding.ts +++ b/src/keybindings/useKeybinding.ts @@ -1,6 +1,6 @@ import { useCallback, useEffect } from 'react' -import type { InputEvent } from '@anthropic/ink' -import { type Key, useInput } from '@anthropic/ink' +import type { InputEvent } from '../ink/events/input-event.js' +import { type Key, useInput } from '../ink.js' import { useOptionalKeybindingContext } from './KeybindingContext.js' import type { KeybindingContextName } from './types.js' diff --git a/src/main.tsx b/src/main.tsx index bf7b2a252..5ceb6405d 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -41,7 +41,7 @@ import { getRemoteSessionUrl } from './constants/product.js' import { getSystemContext, getUserContext } from './context.js' import { init, initializeTelemetryAfterTrust } from './entrypoints/init.js' import { addToHistory } from './history.js' -import type { Root } from '@anthropic/ink' +import type { Root } from './ink.js' import { launchRepl } from './replLauncher.js' import { hasGrowthBookEnvOverride, @@ -153,7 +153,7 @@ import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent, } from 'src/services/analytics/index.js' - +import { initializeAnalyticsGates } from 'src/services/analytics/sink.js' import { getOriginalCwd, setAdditionalDirectoriesForClaudeMd, @@ -173,7 +173,7 @@ import { launchTeleportRepoMismatchDialog, launchTeleportResumeWrapper, } from './dialogLaunchers.js' -import { SHOW_CURSOR } from '@anthropic/ink' +import { SHOW_CURSOR } from './ink/termio/dec.js' import { exitWithError, exitWithMessage, @@ -441,7 +441,6 @@ import { type ThinkingConfig, } from './utils/thinking.js' import { initUser, resetUserCache } from './utils/user.js' -import { initializeAnalyticsGates } from './services/analytics/sink.js' import { getTmuxInstallInstructions, isTmuxAvailable, @@ -3233,7 +3232,7 @@ async function run(): Promise { installAsciicastRecorder() } - const { createRoot } = await import('@anthropic/ink') + const { createRoot } = await import('./ink.js') root = await createRoot(ctx.renderOptions) // Log startup time now, before any blocking dialog renders. Logging @@ -6025,7 +6024,7 @@ async function run(): Promise { .action(async () => { const [{ setupTokenHandler }, { createRoot }] = await Promise.all([ import('./cli/handlers/util.js'), - import('@anthropic/ink'), + import('./ink.js'), ]) const root = await createRoot(getBaseRenderOptions(false)) await setupTokenHandler(root) @@ -6145,7 +6144,7 @@ async function run(): Promise { .action(async () => { const [{ doctorHandler }, { createRoot }] = await Promise.all([ import('./cli/handlers/util.js'), - import('@anthropic/ink'), + import('./ink.js'), ]) const root = await createRoot(getBaseRenderOptions(false)) await doctorHandler(root) diff --git a/src/replLauncher.tsx b/src/replLauncher.tsx index 91550a3bd..664e95839 100644 --- a/src/replLauncher.tsx +++ b/src/replLauncher.tsx @@ -1,6 +1,6 @@ import React from 'react' import type { StatsStore } from './context/stats.js' -import type { Root } from '@anthropic/ink' +import type { Root } from './ink.js' import type { Props as REPLProps } from './screens/REPL.js' import type { AppState } from './state/AppStateStore.js' import type { FpsMetrics } from './utils/fpsTracker.js' diff --git a/src/screens/Doctor.tsx b/src/screens/Doctor.tsx index 6ba73f2a4..d8de3714a 100644 --- a/src/screens/Doctor.tsx +++ b/src/screens/Doctor.tsx @@ -15,13 +15,13 @@ import { getClaudeConfigHomeDir } from 'src/utils/envUtils.js' import type { SettingSource } from 'src/utils/settings/constants.js' import { getOriginalCwd } from '../bootstrap/state.js' import type { CommandResultDisplay } from '../commands.js' -import { Pane } from '@anthropic/ink' +import { Pane } from '../components/design-system/Pane.js' import { PressEnterToContinue } from '../components/PressEnterToContinue.js' import { SandboxDoctorSection } from '../components/sandbox/SandboxDoctorSection.js' import { ValidationErrorsList } from '../components/ValidationErrorsList.js' import { useSettingsErrors } from '../hooks/notifs/useSettingsErrors.js' import { useExitOnCtrlCDWithKeybindings } from '../hooks/useExitOnCtrlCDWithKeybindings.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import { useKeybindings } from '../keybindings/useKeybinding.js' import { useAppState } from '../state/AppState.js' import { getPluginErrorMessage } from '../types/plugin.js' diff --git a/src/screens/REPL.tsx b/src/screens/REPL.tsx index be3fe3d68..83669907c 100644 --- a/src/screens/REPL.tsx +++ b/src/screens/REPL.tsx @@ -14,18 +14,19 @@ import { dirname, join } from 'path'; import { tmpdir } from 'os'; import figures from 'figures'; // eslint-disable-next-line custom-rules/prefer-use-keybindings -- / n N Esc [ v are bare letters in transcript modal context, same class as g/G/j/k in ScrollKeybindingHandler -import { useInput } from '@anthropic/ink' -import { useSearchInput } from '../hooks/useSearchInput.js' -import { useTerminalSize } from '../hooks/useTerminalSize.js' -import { useSearchHighlight } from '@anthropic/ink' -import type { JumpHandle } from '../components/VirtualMessageList.js' -import { renderMessagesToPlainText } from '../utils/exportRenderer.js' -import { openFileInExternalEditor } from '../utils/editor.js' -import { writeFile } from 'fs/promises' -import { type TabStatusKind, Box, Text, useStdin, useTheme, useTerminalFocus, useTerminalTitle, useTabStatus } from '@anthropic/ink' -import { CostThresholdDialog } from '../components/CostThresholdDialog.js' -import { IdleReturnDialog } from '../components/IdleReturnDialog.js' -import * as React from 'react' +import { useInput } from '../ink.js'; +import { useSearchInput } from '../hooks/useSearchInput.js'; +import { useTerminalSize } from '../hooks/useTerminalSize.js'; +import { useSearchHighlight } from '../ink/hooks/use-search-highlight.js'; +import type { JumpHandle } from '../components/VirtualMessageList.js'; +import { renderMessagesToPlainText } from '../utils/exportRenderer.js'; +import { openFileInExternalEditor } from '../utils/editor.js'; +import { writeFile } from 'fs/promises'; +import { Box, Text, useStdin, useTheme, useTerminalFocus, useTerminalTitle, useTabStatus } from '../ink.js'; +import type { TabStatusKind } from '../ink/hooks/use-tab-status.js'; +import { CostThresholdDialog } from '../components/CostThresholdDialog.js'; +import { IdleReturnDialog } from '../components/IdleReturnDialog.js'; +import * as React from 'react'; import { useEffect, useMemo, @@ -35,14 +36,12 @@ import { useDeferredValue, useLayoutEffect, type RefObject, -} from 'react' -import { useNotifications } from '../context/notifications.js' -import { sendNotification } from '../services/notifier.js' -import { - startPreventSleep, - stopPreventSleep, -} from '../services/preventSleep.js' -import { useTerminalNotification, hasCursorUpViewportYankBug } from '@anthropic/ink' +} from 'react'; +import { useNotifications } from '../context/notifications.js'; +import { sendNotification } from '../services/notifier.js'; +import { startPreventSleep, stopPreventSleep } from '../services/preventSleep.js'; +import { useTerminalNotification } from '../ink/useTerminalNotification.js'; +import { hasCursorUpViewportYankBug } from '../ink/terminal.js'; import { createFileStateCacheWithSizeLimit, mergeFileStateCaches, @@ -447,21 +446,13 @@ import { UltraplanChoiceDialog } from '../components/ultraplan/UltraplanChoiceDi import { UltraplanLaunchDialog } from '../components/ultraplan/UltraplanLaunchDialog.js'; import { launchUltraplan } from '../commands/ultraplan.js'; // Session manager removed - using AppState now -import type { RemoteSessionConfig } from '../remote/RemoteSessionManager.js' -import { REMOTE_SAFE_COMMANDS } from '../commands.js' -import type { RemoteMessageContent } from '../utils/teleport/api.js' -import { - FullscreenLayout, - useUnseenDivider, - computeUnseenDivider, -} from '../components/FullscreenLayout.js' -import { - isFullscreenEnvEnabled, - maybeGetTmuxMouseHint, - isMouseTrackingEnabled, -} from '../utils/fullscreen.js' -import { AlternateScreen } from '@anthropic/ink' -import { ScrollKeybindingHandler } from '../components/ScrollKeybindingHandler.js' +import type { RemoteSessionConfig } from '../remote/RemoteSessionManager.js'; +import { REMOTE_SAFE_COMMANDS } from '../commands.js'; +import type { RemoteMessageContent } from '../utils/teleport/api.js'; +import { FullscreenLayout, useUnseenDivider, computeUnseenDivider } from '../components/FullscreenLayout.js'; +import { isFullscreenEnvEnabled, maybeGetTmuxMouseHint, isMouseTrackingEnabled } from '../utils/fullscreen.js'; +import { AlternateScreen } from '../ink/components/AlternateScreen.js'; +import { ScrollKeybindingHandler } from '../components/ScrollKeybindingHandler.js'; import { useMessageActions, MessageActionsKeybindings, @@ -469,13 +460,10 @@ import { type MessageActionsState, type MessageActionsNav, type MessageActionCaps, -} from '../components/messageActions.js' -import { setClipboard } from '@anthropic/ink' -import type { ScrollBoxHandle } from '@anthropic/ink' -import { - createAttachmentMessage, - getQueuedCommandAttachments, -} from '../utils/attachments.js' +} from '../components/messageActions.js'; +import { setClipboard } from '../ink/termio/osc.js'; +import type { ScrollBoxHandle } from '../ink/components/ScrollBox.js'; +import { createAttachmentMessage, getQueuedCommandAttachments } from '../utils/attachments.js'; // Stable empty array for hooks that accept MCPServerConnection[] — avoids // creating a new [] literal on every render in remote mode, which would diff --git a/src/screens/ResumeConversation.tsx b/src/screens/ResumeConversation.tsx index a3a8229a2..019327ff3 100644 --- a/src/screens/ResumeConversation.tsx +++ b/src/screens/ResumeConversation.tsx @@ -7,8 +7,8 @@ import type { Command } from '../commands.js' import { LogSelector } from '../components/LogSelector.js' import { Spinner } from '../components/Spinner.js' import { restoreCostStateForSession } from '../cost-tracker.js' -import { setClipboard } from '@anthropic/ink' -import { Box, Text } from '@anthropic/ink' +import { setClipboard } from '../ink/termio/osc.js' +import { Box, Text } from '../ink.js' import { useKeybinding } from '../keybindings/useKeybinding.js' import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, diff --git a/src/services/mcpServerApproval.tsx b/src/services/mcpServerApproval.tsx index 78be862f3..4b92d1280 100644 --- a/src/services/mcpServerApproval.tsx +++ b/src/services/mcpServerApproval.tsx @@ -1,7 +1,7 @@ import React from 'react' import { MCPServerApprovalDialog } from '../components/MCPServerApprovalDialog.js' import { MCPServerMultiselectDialog } from '../components/MCPServerMultiselectDialog.js' -import type { Root } from '@anthropic/ink' +import type { Root } from '../ink.js' import { KeybindingSetup } from '../keybindings/KeybindingProviderSetup.js' import { AppStateProvider } from '../state/AppState.js' import { getMcpConfigsByScope } from './mcp/config.js' diff --git a/src/services/notifier.ts b/src/services/notifier.ts index 1eb8f99db..a1a865cf1 100644 --- a/src/services/notifier.ts +++ b/src/services/notifier.ts @@ -1,4 +1,4 @@ -import type { TerminalNotification } from '@anthropic/ink' +import type { TerminalNotification } from '../ink/useTerminalNotification.js' import { getGlobalConfig } from '../utils/config.js' import { env } from '../utils/env.js' import { execFileNoThrow } from '../utils/execFileNoThrow.js' diff --git a/src/services/remoteManagedSettings/securityCheck.tsx b/src/services/remoteManagedSettings/securityCheck.tsx index f0d9b1ca7..857103408 100644 --- a/src/services/remoteManagedSettings/securityCheck.tsx +++ b/src/services/remoteManagedSettings/securityCheck.tsx @@ -6,7 +6,7 @@ import { hasDangerousSettings, hasDangerousSettingsChanged, } from '../../components/ManagedSettingsSecurityDialog/utils.js' -import { wrappedRender as render } from '@anthropic/ink' +import { render } from '../../ink.js' import { KeybindingSetup } from '../../keybindings/KeybindingProviderSetup.js' import { AppStateProvider } from '../../state/AppState.js' import { gracefulShutdownSync } from '../../utils/gracefulShutdown.js' diff --git a/src/services/tips/tipRegistry.ts b/src/services/tips/tipRegistry.ts index 05b727dcc..c99edfdc9 100644 --- a/src/services/tips/tipRegistry.ts +++ b/src/services/tips/tipRegistry.ts @@ -8,7 +8,7 @@ import { } from 'src/utils/settings/settings.js' import { shouldOfferTerminalSetup } from '../../commands/terminalSetup/terminalSetup.js' import { getDesktopUpsellConfig } from '../../components/DesktopUpsell/DesktopUpsellStartup.js' -import { color } from '@anthropic/ink' +import { color } from '../../components/design-system/color.js' import { shouldShowOverageCreditUpsell } from '../../components/LogoV2/OverageCreditUpsell.js' import { getShortcutDisplay } from '../../keybindings/shortcutFormat.js' import { isKairosCronEnabled } from '../../tools/ScheduleCronTool/prompt.js' diff --git a/src/tools/AgentTool/UI.tsx b/src/tools/AgentTool/UI.tsx index b6bc20da4..aaa312e20 100644 --- a/src/tools/AgentTool/UI.tsx +++ b/src/tools/AgentTool/UI.tsx @@ -8,7 +8,8 @@ import { CtrlOToExpand, SubAgentProvider, } from 'src/components/CtrlOToExpand.js' -import { Byline, KeyboardShortcutHint } from '@anthropic/ink' +import { Byline } from 'src/components/design-system/Byline.js' +import { KeyboardShortcutHint } from 'src/components/design-system/KeyboardShortcutHint.js' import type { z } from 'zod/v4' import { AgentProgressLine } from '../../components/AgentProgressLine.js' import { FallbackToolUseErrorMessage } from '../../components/FallbackToolUseErrorMessage.js' @@ -17,7 +18,7 @@ import { Markdown } from '../../components/Markdown.js' import { Message as MessageComponent } from '../../components/Message.js' import { MessageResponse } from '../../components/MessageResponse.js' import { ToolUseLoader } from '../../components/ToolUseLoader.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { getDumpPromptsPath } from '../../services/api/dumpPrompts.js' import { findToolByName, type Tools } from '../../Tool.js' import type { Message, ProgressMessage } from '../../types/message.js' diff --git a/src/tools/AskUserQuestionTool/AskUserQuestionTool.tsx b/src/tools/AskUserQuestionTool/AskUserQuestionTool.tsx index 4c77e7da6..e71a5c665 100644 --- a/src/tools/AskUserQuestionTool/AskUserQuestionTool.tsx +++ b/src/tools/AskUserQuestionTool/AskUserQuestionTool.tsx @@ -8,7 +8,7 @@ import { MessageResponse } from 'src/components/MessageResponse.js' import { BLACK_CIRCLE } from 'src/constants/figures.js' import { getModeColor } from 'src/utils/permissions/PermissionMode.js' import { z } from 'zod/v4' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import type { Tool } from '../../Tool.js' import { buildTool, type ToolDef } from '../../Tool.js' import { lazySchema } from '../../utils/lazySchema.js' diff --git a/src/tools/BashTool/BashToolResultMessage.tsx b/src/tools/BashTool/BashToolResultMessage.tsx index 6b1534bfb..4b5c0b613 100644 --- a/src/tools/BashTool/BashToolResultMessage.tsx +++ b/src/tools/BashTool/BashToolResultMessage.tsx @@ -1,10 +1,10 @@ import React from 'react' import { removeSandboxViolationTags } from 'src/utils/sandbox/sandbox-ui-utils.js' -import { KeyboardShortcutHint } from '@anthropic/ink' +import { KeyboardShortcutHint } from '../../components/design-system/KeyboardShortcutHint.js' import { MessageResponse } from '../../components/MessageResponse.js' import { OutputLine } from '../../components/shell/OutputLine.js' import { ShellTimeDisplay } from '../../components/shell/ShellTimeDisplay.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import type { Out as BashOut } from './BashTool.js' type Props = { diff --git a/src/tools/BashTool/UI.tsx b/src/tools/BashTool/UI.tsx index 2b0b3bfd3..1d5c3f5aa 100644 --- a/src/tools/BashTool/UI.tsx +++ b/src/tools/BashTool/UI.tsx @@ -1,10 +1,10 @@ import type { ToolResultBlockParam } from '@anthropic-ai/sdk/resources/index.mjs' import * as React from 'react' -import { KeyboardShortcutHint } from '@anthropic/ink' +import { KeyboardShortcutHint } from '../../components/design-system/KeyboardShortcutHint.js' import { FallbackToolUseErrorMessage } from '../../components/FallbackToolUseErrorMessage.js' import { MessageResponse } from '../../components/MessageResponse.js' import { ShellProgressMessage } from '../../components/shell/ShellProgressMessage.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { useKeybinding } from '../../keybindings/useKeybinding.js' import { useShortcutDisplay } from '../../keybindings/useShortcutDisplay.js' import { useAppStateStore, useSetAppState } from '../../state/AppState.js' diff --git a/src/tools/BriefTool/UI.tsx b/src/tools/BriefTool/UI.tsx index ea3a28f9f..e68adccf6 100644 --- a/src/tools/BriefTool/UI.tsx +++ b/src/tools/BriefTool/UI.tsx @@ -2,7 +2,7 @@ import figures from 'figures' import React from 'react' import { Markdown } from '../../components/Markdown.js' import { BLACK_CIRCLE } from '../../constants/figures.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import type { ProgressMessage } from '../../types/message.js' import { getDisplayPath } from '../../utils/file.js' import { formatFileSize } from '../../utils/format.js' diff --git a/src/tools/ConfigTool/UI.tsx b/src/tools/ConfigTool/UI.tsx index da8ced8a1..36ce77e62 100644 --- a/src/tools/ConfigTool/UI.tsx +++ b/src/tools/ConfigTool/UI.tsx @@ -1,6 +1,6 @@ import React from 'react' import { MessageResponse } from '../../components/MessageResponse.js' -import { Text } from '@anthropic/ink' +import { Text } from '../../ink.js' import { jsonStringify } from '../../utils/slowOperations.js' import type { Input, Output } from './ConfigTool.js' diff --git a/src/tools/EnterPlanModeTool/UI.tsx b/src/tools/EnterPlanModeTool/UI.tsx index a1bc5d6c8..830708309 100644 --- a/src/tools/EnterPlanModeTool/UI.tsx +++ b/src/tools/EnterPlanModeTool/UI.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import { BLACK_CIRCLE } from 'src/constants/figures.js' import { getModeColor } from 'src/utils/permissions/PermissionMode.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import type { ToolProgressData } from '../../Tool.js' import type { ProgressMessage } from '../../types/message.js' import type { ThemeName } from '../../utils/theme.js' diff --git a/src/tools/EnterWorktreeTool/UI.tsx b/src/tools/EnterWorktreeTool/UI.tsx index 985186d34..5a859fd72 100644 --- a/src/tools/EnterWorktreeTool/UI.tsx +++ b/src/tools/EnterWorktreeTool/UI.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import type { ToolProgressData } from '../../Tool.js' import type { ProgressMessage } from '../../types/message.js' import type { ThemeName } from '../../utils/theme.js' diff --git a/src/tools/ExitPlanModeTool/UI.tsx b/src/tools/ExitPlanModeTool/UI.tsx index 789ea4ccf..b73c4e53f 100644 --- a/src/tools/ExitPlanModeTool/UI.tsx +++ b/src/tools/ExitPlanModeTool/UI.tsx @@ -4,7 +4,7 @@ import { MessageResponse } from 'src/components/MessageResponse.js' import { RejectedPlanMessage } from 'src/components/messages/UserToolResultMessage/RejectedPlanMessage.js' import { BLACK_CIRCLE } from 'src/constants/figures.js' import { getModeColor } from 'src/utils/permissions/PermissionMode.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import type { ToolProgressData } from '../../Tool.js' import type { ProgressMessage } from '../../types/message.js' import { getDisplayPath } from '../../utils/file.js' diff --git a/src/tools/ExitWorktreeTool/UI.tsx b/src/tools/ExitWorktreeTool/UI.tsx index a3eccf4ba..d624f9720 100644 --- a/src/tools/ExitWorktreeTool/UI.tsx +++ b/src/tools/ExitWorktreeTool/UI.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import type { ToolProgressData } from '../../Tool.js' import type { ProgressMessage } from '../../types/message.js' import type { ThemeName } from '../../utils/theme.js' diff --git a/src/tools/FileEditTool/UI.tsx b/src/tools/FileEditTool/UI.tsx index 57223d000..b43f520ee 100644 --- a/src/tools/FileEditTool/UI.tsx +++ b/src/tools/FileEditTool/UI.tsx @@ -7,9 +7,8 @@ import { MessageResponse } from 'src/components/MessageResponse.js' import { extractTag } from 'src/utils/messages.js' import { FallbackToolUseErrorMessage } from '../../components/FallbackToolUseErrorMessage.js' import { FileEditToolUpdatedMessage } from '../../components/FileEditToolUpdatedMessage.js' - -import { Text } from '@anthropic/ink' import { FilePathLink } from '../../components/FilePathLink.js' +import { Text } from '../../ink.js' import type { Tools } from '../../Tool.js' import type { Message, ProgressMessage } from '../../types/message.js' import { adjustHunkLineNumbers, CONTEXT_LINES } from '../../utils/diff.js' diff --git a/src/tools/FileReadTool/UI.tsx b/src/tools/FileReadTool/UI.tsx index 017d55e86..f7849957e 100644 --- a/src/tools/FileReadTool/UI.tsx +++ b/src/tools/FileReadTool/UI.tsx @@ -2,10 +2,9 @@ import type { ToolResultBlockParam } from '@anthropic-ai/sdk/resources/index.mjs import * as React from 'react' import { extractTag } from 'src/utils/messages.js' import { FallbackToolUseErrorMessage } from '../../components/FallbackToolUseErrorMessage.js' - -import { MessageResponse } from '../../components/MessageResponse.js' -import { Text } from '@anthropic/ink' import { FilePathLink } from '../../components/FilePathLink.js' +import { MessageResponse } from '../../components/MessageResponse.js' +import { Text } from '../../ink.js' import { FILE_NOT_FOUND_CWD_NOTE, getDisplayPath } from '../../utils/file.js' import { formatFileSize } from '../../utils/format.js' import { getPlansDirectory } from '../../utils/plans.js' diff --git a/src/tools/FileWriteTool/UI.tsx b/src/tools/FileWriteTool/UI.tsx index 8ea218fb7..183edc811 100644 --- a/src/tools/FileWriteTool/UI.tsx +++ b/src/tools/FileWriteTool/UI.tsx @@ -9,11 +9,10 @@ import { CtrlOToExpand } from '../../components/CtrlOToExpand.js' import { FallbackToolUseErrorMessage } from '../../components/FallbackToolUseErrorMessage.js' import { FileEditToolUpdatedMessage } from '../../components/FileEditToolUpdatedMessage.js' import { FileEditToolUseRejectedMessage } from '../../components/FileEditToolUseRejectedMessage.js' - +import { FilePathLink } from '../../components/FilePathLink.js' import { HighlightedCode } from '../../components/HighlightedCode.js' import { useTerminalSize } from '../../hooks/useTerminalSize.js' -import { Box, Text } from '@anthropic/ink' -import { FilePathLink } from '../../components/FilePathLink.js' +import { Box, Text } from '../../ink.js' import type { ToolProgressData } from '../../Tool.js' import type { ProgressMessage } from '../../types/message.js' import { getCwd } from '../../utils/cwd.js' diff --git a/src/tools/GlobTool/UI.tsx b/src/tools/GlobTool/UI.tsx index b70934fa4..03a76237b 100644 --- a/src/tools/GlobTool/UI.tsx +++ b/src/tools/GlobTool/UI.tsx @@ -4,7 +4,7 @@ import { MessageResponse } from 'src/components/MessageResponse.js' import { extractTag } from 'src/utils/messages.js' import { FallbackToolUseErrorMessage } from '../../components/FallbackToolUseErrorMessage.js' import { TOOL_SUMMARY_MAX_LENGTH } from '../../constants/toolLimits.js' -import { Text } from '@anthropic/ink' +import { Text } from '../../ink.js' import { FILE_NOT_FOUND_CWD_NOTE, getDisplayPath } from '../../utils/file.js' import { truncate } from '../../utils/format.js' import { GrepTool } from '../GrepTool/GrepTool.js' diff --git a/src/tools/GrepTool/UI.tsx b/src/tools/GrepTool/UI.tsx index 4bc319175..dd26d0cb7 100644 --- a/src/tools/GrepTool/UI.tsx +++ b/src/tools/GrepTool/UI.tsx @@ -4,7 +4,7 @@ import { CtrlOToExpand } from '../../components/CtrlOToExpand.js' import { FallbackToolUseErrorMessage } from '../../components/FallbackToolUseErrorMessage.js' import { MessageResponse } from '../../components/MessageResponse.js' import { TOOL_SUMMARY_MAX_LENGTH } from '../../constants/toolLimits.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import type { ToolProgressData } from '../../Tool.js' import type { ProgressMessage } from '../../types/message.js' import { FILE_NOT_FOUND_CWD_NOTE, getDisplayPath } from '../../utils/file.js' diff --git a/src/tools/LSPTool/UI.tsx b/src/tools/LSPTool/UI.tsx index 2a5403b12..2ca53e9a3 100644 --- a/src/tools/LSPTool/UI.tsx +++ b/src/tools/LSPTool/UI.tsx @@ -3,7 +3,7 @@ import React from 'react' import { CtrlOToExpand } from '../../components/CtrlOToExpand.js' import { FallbackToolUseErrorMessage } from '../../components/FallbackToolUseErrorMessage.js' import { MessageResponse } from '../../components/MessageResponse.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { getDisplayPath } from '../../utils/file.js' import { extractTag } from '../../utils/messages.js' import type { Input, Output } from './LSPTool.js' diff --git a/src/tools/ListMcpResourcesTool/UI.tsx b/src/tools/ListMcpResourcesTool/UI.tsx index 6f9b18559..4343123c0 100644 --- a/src/tools/ListMcpResourcesTool/UI.tsx +++ b/src/tools/ListMcpResourcesTool/UI.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import { MessageResponse } from '../../components/MessageResponse.js' import { OutputLine } from '../../components/shell/OutputLine.js' -import { Text } from '@anthropic/ink' +import { Text } from '../../ink.js' import type { ToolProgressData } from '../../Tool.js' import type { ProgressMessage } from '../../types/message.js' import { jsonStringify } from '../../utils/slowOperations.js' diff --git a/src/tools/MCPTool/UI.tsx b/src/tools/MCPTool/UI.tsx index 5fa55c7a2..bdfb3e4ab 100644 --- a/src/tools/MCPTool/UI.tsx +++ b/src/tools/MCPTool/UI.tsx @@ -2,19 +2,19 @@ import { feature } from 'bun:bundle' import figures from 'figures' import * as React from 'react' import type { z } from 'zod/v4' -import { ProgressBar } from '@anthropic/ink' +import { ProgressBar } from '../../components/design-system/ProgressBar.js' import { MessageResponse } from '../../components/MessageResponse.js' import { linkifyUrlsInText, OutputLine, } from '../../components/shell/OutputLine.js' -import { Ansi, Box, Text, stringWidth } from '@anthropic/ink' -import { createHyperlink } from '../../utils/hyperlink.js' +import { stringWidth } from '../../ink/stringWidth.js' +import { Ansi, Box, Text } from '../../ink.js' import type { ToolProgressData } from '../../Tool.js' import type { ProgressMessage } from '../../types/message.js' import type { MCPProgress } from '../../types/tools.js' import { formatNumber } from '../../utils/format.js' - +import { createHyperlink } from '../../utils/hyperlink.js' import { getContentSizeEstimate, type MCPToolResult, diff --git a/src/tools/NotebookEditTool/UI.tsx b/src/tools/NotebookEditTool/UI.tsx index e209aaba7..3d777308a 100644 --- a/src/tools/NotebookEditTool/UI.tsx +++ b/src/tools/NotebookEditTool/UI.tsx @@ -5,12 +5,11 @@ import { extractTag } from 'src/utils/messages.js' import type { ThemeName } from 'src/utils/theme.js' import type { z } from 'zod/v4' import { FallbackToolUseErrorMessage } from '../../components/FallbackToolUseErrorMessage.js' - +import { FilePathLink } from '../../components/FilePathLink.js' import { HighlightedCode } from '../../components/HighlightedCode.js' import { MessageResponse } from '../../components/MessageResponse.js' import { NotebookEditToolUseRejectedMessage } from '../../components/NotebookEditToolUseRejectedMessage.js' -import { Box, Text } from '@anthropic/ink' -import { FilePathLink } from '../../components/FilePathLink.js' +import { Box, Text } from '../../ink.js' import type { Tools } from '../../Tool.js' import { getDisplayPath } from '../../utils/file.js' import type { inputSchema, Output } from './NotebookEditTool.js' diff --git a/src/tools/PowerShellTool/UI.tsx b/src/tools/PowerShellTool/UI.tsx index 8b27bfa12..0f9438510 100644 --- a/src/tools/PowerShellTool/UI.tsx +++ b/src/tools/PowerShellTool/UI.tsx @@ -1,12 +1,12 @@ import type { ToolResultBlockParam } from '@anthropic-ai/sdk/resources/index.mjs' import * as React from 'react' -import { KeyboardShortcutHint } from '@anthropic/ink' +import { KeyboardShortcutHint } from '../../components/design-system/KeyboardShortcutHint.js' import { FallbackToolUseErrorMessage } from '../../components/FallbackToolUseErrorMessage.js' import { MessageResponse } from '../../components/MessageResponse.js' import { OutputLine } from '../../components/shell/OutputLine.js' import { ShellProgressMessage } from '../../components/shell/ShellProgressMessage.js' import { ShellTimeDisplay } from '../../components/shell/ShellTimeDisplay.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import type { Tool } from '../../Tool.js' import type { ProgressMessage } from '../../types/message.js' import type { PowerShellProgress } from '../../types/tools.js' diff --git a/src/tools/ReadMcpResourceTool/UI.tsx b/src/tools/ReadMcpResourceTool/UI.tsx index d2641adc2..7459608a3 100644 --- a/src/tools/ReadMcpResourceTool/UI.tsx +++ b/src/tools/ReadMcpResourceTool/UI.tsx @@ -2,7 +2,7 @@ import * as React from 'react' import type { z } from 'zod/v4' import { MessageResponse } from '../../components/MessageResponse.js' import { OutputLine } from '../../components/shell/OutputLine.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import type { ToolProgressData } from '../../Tool.js' import type { ProgressMessage } from '../../types/message.js' import { jsonStringify } from '../../utils/slowOperations.js' diff --git a/src/tools/RemoteTriggerTool/UI.tsx b/src/tools/RemoteTriggerTool/UI.tsx index 5aae25157..3b44a786e 100644 --- a/src/tools/RemoteTriggerTool/UI.tsx +++ b/src/tools/RemoteTriggerTool/UI.tsx @@ -1,6 +1,6 @@ import React from 'react' import { MessageResponse } from '../../components/MessageResponse.js' -import { Text } from '@anthropic/ink' +import { Text } from '../../ink.js' import { countCharInString } from '../../utils/stringUtils.js' import type { Input, Output } from './RemoteTriggerTool.js' diff --git a/src/tools/ScheduleCronTool/UI.tsx b/src/tools/ScheduleCronTool/UI.tsx index 7e4928a81..439f7048f 100644 --- a/src/tools/ScheduleCronTool/UI.tsx +++ b/src/tools/ScheduleCronTool/UI.tsx @@ -1,6 +1,6 @@ import React from 'react' import { MessageResponse } from '../../components/MessageResponse.js' -import { Text } from '@anthropic/ink' +import { Text } from '../../ink.js' import { truncate } from '../../utils/format.js' import type { CreateOutput } from './CronCreateTool.js' import type { DeleteOutput } from './CronDeleteTool.js' diff --git a/src/tools/SendMessageTool/UI.tsx b/src/tools/SendMessageTool/UI.tsx index 38d17a8d4..2cd05a935 100644 --- a/src/tools/SendMessageTool/UI.tsx +++ b/src/tools/SendMessageTool/UI.tsx @@ -1,6 +1,6 @@ import React from 'react' import { MessageResponse } from '../../components/MessageResponse.js' -import { Text } from '@anthropic/ink' +import { Text } from '../../ink.js' import { jsonParse } from '../../utils/slowOperations.js' import type { Input, SendMessageToolOutput } from './SendMessageTool.js' diff --git a/src/tools/SkillTool/UI.tsx b/src/tools/SkillTool/UI.tsx index 7898ee348..558ef9c57 100644 --- a/src/tools/SkillTool/UI.tsx +++ b/src/tools/SkillTool/UI.tsx @@ -5,10 +5,10 @@ import { FallbackToolUseErrorMessage } from 'src/components/FallbackToolUseError import { FallbackToolUseRejectedMessage } from 'src/components/FallbackToolUseRejectedMessage.js' import type { z } from 'zod/v4' import type { Command } from '../../commands.js' -import { Byline } from '@anthropic/ink' +import { Byline } from '../../components/design-system/Byline.js' import { Message as MessageComponent } from '../../components/Message.js' import { MessageResponse } from '../../components/MessageResponse.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import type { Tools } from '../../Tool.js' import type { ProgressMessage } from '../../types/message.js' import { buildSubagentLookups, EMPTY_LOOKUPS } from '../../utils/messages.js' diff --git a/src/tools/SkillTool/prompt.ts b/src/tools/SkillTool/prompt.ts index ef8267a78..b05a58f3d 100644 --- a/src/tools/SkillTool/prompt.ts +++ b/src/tools/SkillTool/prompt.ts @@ -6,7 +6,7 @@ import { getSlashCommandToolSkills, } from 'src/commands.js' import { COMMAND_NAME_TAG } from '../../constants/xml.js' -import { stringWidth } from '@anthropic/ink' +import { stringWidth } from '../../ink/stringWidth.js' import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent, diff --git a/src/tools/TaskOutputTool/TaskOutputTool.tsx b/src/tools/TaskOutputTool/TaskOutputTool.tsx index 4da47ffbc..9d8897411 100644 --- a/src/tools/TaskOutputTool/TaskOutputTool.tsx +++ b/src/tools/TaskOutputTool/TaskOutputTool.tsx @@ -3,7 +3,7 @@ import { z } from 'zod/v4' import { FallbackToolUseErrorMessage } from '../../components/FallbackToolUseErrorMessage.js' import { FallbackToolUseRejectedMessage } from '../../components/FallbackToolUseRejectedMessage.js' import { MessageResponse } from '../../components/MessageResponse.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import { useShortcutDisplay } from '../../keybindings/useShortcutDisplay.js' import type { TaskType } from '../../Task.js' import type { Tool } from '../../Tool.js' diff --git a/src/tools/TaskStopTool/UI.tsx b/src/tools/TaskStopTool/UI.tsx index e4b2e8501..6d25894bf 100644 --- a/src/tools/TaskStopTool/UI.tsx +++ b/src/tools/TaskStopTool/UI.tsx @@ -1,6 +1,7 @@ import React from 'react' import { MessageResponse } from '../../components/MessageResponse.js' -import { Text, stringWidth } from '@anthropic/ink' +import { stringWidth } from '../../ink/stringWidth.js' +import { Text } from '../../ink.js' import { truncateToWidthNoEllipsis } from '../../utils/format.js' import type { Output } from './TaskStopTool.js' diff --git a/src/tools/WebFetchTool/UI.tsx b/src/tools/WebFetchTool/UI.tsx index 546bf727e..a32c95463 100644 --- a/src/tools/WebFetchTool/UI.tsx +++ b/src/tools/WebFetchTool/UI.tsx @@ -1,7 +1,7 @@ import React from 'react' import { MessageResponse } from '../../components/MessageResponse.js' import { TOOL_SUMMARY_MAX_LENGTH } from '../../constants/toolLimits.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import type { ToolProgressData } from '../../Tool.js' import type { ProgressMessage } from '../../types/message.js' import { formatFileSize, truncate } from '../../utils/format.js' diff --git a/src/tools/WebSearchTool/UI.tsx b/src/tools/WebSearchTool/UI.tsx index 005f95db9..af8584b29 100644 --- a/src/tools/WebSearchTool/UI.tsx +++ b/src/tools/WebSearchTool/UI.tsx @@ -1,7 +1,7 @@ import React from 'react' import { MessageResponse } from '../../components/MessageResponse.js' import { TOOL_SUMMARY_MAX_LENGTH } from '../../constants/toolLimits.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../../ink.js' import type { ProgressMessage } from '../../types/message.js' import { truncate } from '../../utils/format.js' import type { diff --git a/src/types/ink-elements.d.ts b/src/types/ink-elements.d.ts index f98f08e53..d5053283e 100644 --- a/src/types/ink-elements.d.ts +++ b/src/types/ink-elements.d.ts @@ -2,7 +2,11 @@ // Note: The detailed prop types are defined in ink-jsx.d.ts via React module augmentation. // This file provides the global JSX namespace fallback declarations. import type { ReactNode, Ref } from 'react'; -import type { ClickEvent, FocusEvent, KeyboardEvent, Styles, TextStyles, DOMElement } from '@anthropic/ink'; +import type { ClickEvent } from '../ink/events/click-event.js'; +import type { FocusEvent } from '../ink/events/focus-event.js'; +import type { KeyboardEvent } from '../ink/events/keyboard-event.js'; +import type { Styles, TextStyles } from '../ink/styles.js'; +import type { DOMElement } from '../ink/dom.js'; declare global { namespace JSX { diff --git a/src/types/ink-jsx.d.ts b/src/types/ink-jsx.d.ts index 2c8911c71..8c3704959 100644 --- a/src/types/ink-jsx.d.ts +++ b/src/types/ink-jsx.d.ts @@ -9,7 +9,11 @@ * augmentation to work correctly. */ import type { ReactNode, Ref } from 'react'; -import type { ClickEvent, FocusEvent, KeyboardEvent, Styles, TextStyles, DOMElement } from '@anthropic/ink'; +import type { ClickEvent } from '../ink/events/click-event.js'; +import type { FocusEvent } from '../ink/events/focus-event.js'; +import type { KeyboardEvent } from '../ink/events/keyboard-event.js'; +import type { Styles, TextStyles } from '../ink/styles.js'; +import type { DOMElement } from '../ink/dom.js'; declare module 'react' { namespace JSX { diff --git a/src/types/textInputTypes.ts b/src/types/textInputTypes.ts index 7e5cb047e..1e77b56fe 100644 --- a/src/types/textInputTypes.ts +++ b/src/types/textInputTypes.ts @@ -2,7 +2,7 @@ import type { ContentBlockParam } from '@anthropic-ai/sdk/resources/messages.mjs import type { UUID } from 'crypto' import type React from 'react' import type { PermissionResult } from '../entrypoints/agentSdkTypes.js' -import type { Key } from '@anthropic/ink' +import type { Key } from '../ink.js' import type { PastedContent } from '../utils/config.js' import type { ImageDimensions } from '../utils/imageResizer.js' import type { TextHighlight } from '../utils/textHighlighting.js' diff --git a/src/utils/Cursor.ts b/src/utils/Cursor.ts index 6968ff473..0317ece77 100644 --- a/src/utils/Cursor.ts +++ b/src/utils/Cursor.ts @@ -1,4 +1,5 @@ -import { stringWidth, wrapAnsi } from '@anthropic/ink' +import { stringWidth } from '../ink/stringWidth.js' +import { wrapAnsi } from '../ink/wrapAnsi.js' import { firstGrapheme, getGraphemeSegmenter, diff --git a/src/utils/__tests__/treeify.test.ts b/src/utils/__tests__/treeify.test.ts index 6e60c66bc..e9f553755 100644 --- a/src/utils/__tests__/treeify.test.ts +++ b/src/utils/__tests__/treeify.test.ts @@ -8,7 +8,7 @@ mock.module("figures", () => ({ }, })); -mock.module("src/ink.js", () => ({ +mock.module("src/components/design-system/color.js", () => ({ color: (colorKey: string, themeName: string) => (text: string) => text, })); diff --git a/src/utils/ansiToPng.ts b/src/utils/ansiToPng.ts index 499ecedc6..f823fbbf8 100644 --- a/src/utils/ansiToPng.ts +++ b/src/utils/ansiToPng.ts @@ -19,7 +19,7 @@ */ import { deflateSync } from 'zlib' -import { stringWidth } from '@anthropic/ink' +import { stringWidth } from '../ink/stringWidth.js' import { type AnsiColor, DEFAULT_BG, diff --git a/src/utils/autoRunIssue.tsx b/src/utils/autoRunIssue.tsx index 3a6275bb5..971f25635 100644 --- a/src/utils/autoRunIssue.tsx +++ b/src/utils/autoRunIssue.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import { useEffect, useRef } from 'react' -import { KeyboardShortcutHint } from '@anthropic/ink' -import { Box, Text } from '@anthropic/ink' +import { KeyboardShortcutHint } from '../components/design-system/KeyboardShortcutHint.js' +import { Box, Text } from '../ink.js' import { useKeybinding } from '../keybindings/useKeybinding.js' type Props = { diff --git a/src/utils/claudeInChrome/mcpServer.ts b/src/utils/claudeInChrome/mcpServer.ts index 02f0d629c..4195d2c4f 100644 --- a/src/utils/claudeInChrome/mcpServer.ts +++ b/src/utils/claudeInChrome/mcpServer.ts @@ -4,7 +4,6 @@ import { type Logger, type PermissionMode, } from '@ant/claude-for-chrome-mcp' -import { initializeAnalyticsSink } from '../../services/analytics/sink.js' import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js' import { format } from 'util' import { shutdownDatadog } from '../../services/analytics/datadog.js' @@ -14,7 +13,7 @@ import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent, } from '../../services/analytics/index.js' - +import { initializeAnalyticsSink } from '../../services/analytics/sink.js' import { getClaudeAIOAuthTokens } from '../auth.js' import { enableConfigs, getGlobalConfig, saveGlobalConfig } from '../config.js' import { logForDebugging } from '../debug.js' diff --git a/src/utils/claudeInChrome/toolRendering.tsx b/src/utils/claudeInChrome/toolRendering.tsx index 6f77bcf0b..d760085fb 100644 --- a/src/utils/claudeInChrome/toolRendering.tsx +++ b/src/utils/claudeInChrome/toolRendering.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import { MessageResponse } from '../../components/MessageResponse.js' -import { supportsHyperlinks } from '@anthropic/ink' -import { Link, Text } from '@anthropic/ink' +import { supportsHyperlinks } from '../../ink/supports-hyperlinks.js' +import { Link, Text } from '../../ink.js' import { renderToolResultMessage as renderDefaultMCPToolResultMessage } from '../../tools/MCPTool/UI.js' import type { MCPToolResult } from '../../utils/mcpValidation.js' import { truncateToWidth } from '../format.js' diff --git a/src/utils/completionCache.ts b/src/utils/completionCache.ts index ec288c3e6..3f0c9d2f7 100644 --- a/src/utils/completionCache.ts +++ b/src/utils/completionCache.ts @@ -3,8 +3,8 @@ import { mkdir, readFile, writeFile } from 'fs/promises' import { homedir } from 'os' import { dirname, join } from 'path' import { pathToFileURL } from 'url' -import { color } from '@anthropic/ink' -import { supportsHyperlinks } from '@anthropic/ink' +import { color } from '../components/design-system/color.js' +import { supportsHyperlinks } from '../ink/supports-hyperlinks.js' import { logForDebugging } from './debug.js' import { isENOENT } from './errors.js' import { execFileNoThrow } from './execFileNoThrow.js' diff --git a/src/utils/computerUse/mcpServer.ts b/src/utils/computerUse/mcpServer.ts index f759335c5..d51d80ab2 100644 --- a/src/utils/computerUse/mcpServer.ts +++ b/src/utils/computerUse/mcpServer.ts @@ -2,14 +2,13 @@ import { buildComputerUseTools, createComputerUseMcpServer, } from '@ant/computer-use-mcp' -import { initializeAnalyticsSink } from '../../services/analytics/sink.js' import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js' import { ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js' import { homedir } from 'os' import { shutdownDatadog } from '../../services/analytics/datadog.js' import { shutdown1PEventLogging } from '../../services/analytics/firstPartyEventLogger.js' - +import { initializeAnalyticsSink } from '../../services/analytics/sink.js' import { enableConfigs } from '../config.js' import { logForDebugging } from '../debug.js' import { filterAppsForDescription } from './appNames.js' diff --git a/src/utils/computerUse/toolRendering.tsx b/src/utils/computerUse/toolRendering.tsx index 0295e19e3..4b5da230e 100644 --- a/src/utils/computerUse/toolRendering.tsx +++ b/src/utils/computerUse/toolRendering.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import { MessageResponse } from '../../components/MessageResponse.js' -import { Text } from '@anthropic/ink' +import { Text } from '../../ink.js' import { truncateToWidth } from '../format.js' import type { MCPToolResult } from '../mcpValidation.js' diff --git a/src/utils/deepLink/protocolHandler.ts b/src/utils/deepLink/protocolHandler.ts index 511754dc7..0134aef51 100644 --- a/src/utils/deepLink/protocolHandler.ts +++ b/src/utils/deepLink/protocolHandler.ts @@ -11,7 +11,6 @@ * directly — there is no terminal attached. */ -import { parseDeepLink } from './parseDeepLink.js' import { homedir } from 'os' import { logForDebugging } from '../debug.js' import { @@ -20,7 +19,7 @@ import { } from '../githubRepoPathMapping.js' import { jsonStringify } from '../slowOperations.js' import { readLastFetchTime } from './banner.js' - +import { parseDeepLink } from './parseDeepLink.js' import { MACOS_BUNDLE_ID } from './registerProtocol.js' import { launchInTerminal } from './terminalLauncher.js' diff --git a/src/utils/deepLink/registerProtocol.ts b/src/utils/deepLink/registerProtocol.ts index a877cbaaa..0e630ee61 100644 --- a/src/utils/deepLink/registerProtocol.ts +++ b/src/utils/deepLink/registerProtocol.ts @@ -13,7 +13,6 @@ * Windows — Writes registry keys under HKEY_CURRENT_USER\Software\Classes */ -import { DEEP_LINK_PROTOCOL } from './parseDeepLink.js' import { promises as fs } from 'fs' import * as os from 'os' import * as path from 'path' @@ -29,6 +28,7 @@ import { execFileNoThrow } from '../execFileNoThrow.js' import { getInitialSettings } from '../settings/settings.js' import { which } from '../which.js' import { getUserBinDir, getXDGDataHome } from '../xdg.js' +import { DEEP_LINK_PROTOCOL } from './parseDeepLink.js' export const MACOS_BUNDLE_ID = 'com.anthropic.claude-code-url-handler' const APP_NAME = 'Claude Code URL Handler' diff --git a/src/utils/editor.ts b/src/utils/editor.ts index 2cd6aa86e..2924b77b9 100644 --- a/src/utils/editor.ts +++ b/src/utils/editor.ts @@ -6,7 +6,7 @@ import { } from 'child_process' import memoize from 'lodash-es/memoize.js' import { basename } from 'path' -import { instances } from '@anthropic/ink' +import instances from '../ink/instances.js' import { logForDebugging } from './debug.js' import { whichSync } from './which.js' diff --git a/src/utils/gracefulShutdown.ts b/src/utils/gracefulShutdown.ts index 8b8a5b9c7..391dce288 100644 --- a/src/utils/gracefulShutdown.ts +++ b/src/utils/gracefulShutdown.ts @@ -10,7 +10,25 @@ import { getSessionId, isSessionPersistenceDisabled, } from '../bootstrap/state.js' -import { DISABLE_KITTY_KEYBOARD, DISABLE_MODIFY_OTHER_KEYS, DBP, DFE, DISABLE_MOUSE_TRACKING, EXIT_ALT_SCREEN, SHOW_CURSOR, CLEAR_ITERM2_PROGRESS, CLEAR_TAB_STATUS, CLEAR_TERMINAL_TITLE, instances, supportsTabStatus, wrapForMultiplexer } from '@anthropic/ink' +import instances from '../ink/instances.js' +import { + DISABLE_KITTY_KEYBOARD, + DISABLE_MODIFY_OTHER_KEYS, +} from '../ink/termio/csi.js' +import { + DBP, + DFE, + DISABLE_MOUSE_TRACKING, + EXIT_ALT_SCREEN, + SHOW_CURSOR, +} from '../ink/termio/dec.js' +import { + CLEAR_ITERM2_PROGRESS, + CLEAR_TAB_STATUS, + CLEAR_TERMINAL_TITLE, + supportsTabStatus, + wrapForMultiplexer, +} from '../ink/termio/osc.js' import { shutdownDatadog } from '../services/analytics/datadog.js' import { shutdown1PEventLogging } from '../services/analytics/firstPartyEventLogger.js' import { diff --git a/src/utils/highlightMatch.tsx b/src/utils/highlightMatch.tsx index 23945f976..31ed36045 100644 --- a/src/utils/highlightMatch.tsx +++ b/src/utils/highlightMatch.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { Text } from '@anthropic/ink' +import { Text } from '../ink.js' /** * Inverse-highlight every occurrence of `query` in `text` (case-insensitive). diff --git a/src/utils/hyperlink.ts b/src/utils/hyperlink.ts index 732527380..306dd93ad 100644 --- a/src/utils/hyperlink.ts +++ b/src/utils/hyperlink.ts @@ -1,5 +1,5 @@ import chalk from 'chalk' -import { supportsHyperlinks } from '@anthropic/ink' +import { supportsHyperlinks } from '../ink/supports-hyperlinks.js' // OSC 8 hyperlink escape sequences // Format: \e]8;;URL\e\\TEXT\e]8;;\e\\ diff --git a/src/utils/ink.ts b/src/utils/ink.ts index 915837bc9..ea2669aca 100644 --- a/src/utils/ink.ts +++ b/src/utils/ink.ts @@ -1,4 +1,4 @@ -import type { TextProps } from '@anthropic/ink' +import type { TextProps } from '../ink.js' import { AGENT_COLOR_TO_THEME_COLOR, type AgentColorName, diff --git a/src/utils/logoV2Utils.ts b/src/utils/logoV2Utils.ts index 4357119d7..f7f9720b4 100644 --- a/src/utils/logoV2Utils.ts +++ b/src/utils/logoV2Utils.ts @@ -1,5 +1,5 @@ import { getDirectConnectServerUrl, getSessionId } from '../bootstrap/state.js' -import { stringWidth } from '@anthropic/ink' +import { stringWidth } from '../ink/stringWidth.js' import type { LogOption } from '../types/logs.js' import { getSubscriptionName, isClaudeAISubscriber } from './auth.js' import { getCwd } from './cwd.js' diff --git a/src/utils/markdown.ts b/src/utils/markdown.ts index ae1e3902e..b2c673e6d 100644 --- a/src/utils/markdown.ts +++ b/src/utils/markdown.ts @@ -1,13 +1,13 @@ import chalk from 'chalk' import { marked, type Token, type Tokens } from 'marked' import stripAnsi from 'strip-ansi' -import { color } from '@anthropic/ink' +import { color } from '../components/design-system/color.js' import { BLOCKQUOTE_BAR } from '../constants/figures.js' -import { stringWidth, supportsHyperlinks } from '@anthropic/ink' -import { createHyperlink } from '../utils/hyperlink.js' +import { stringWidth } from '../ink/stringWidth.js' +import { supportsHyperlinks } from '../ink/supports-hyperlinks.js' import type { CliHighlight } from './cliHighlight.js' import { logForDebugging } from './debug.js' - +import { createHyperlink } from './hyperlink.js' import { stripPromptXMLTags } from './messages.js' import type { ThemeName } from './theme.js' diff --git a/src/utils/preflightChecks.tsx b/src/utils/preflightChecks.tsx index fed5b1236..445cd12a8 100644 --- a/src/utils/preflightChecks.tsx +++ b/src/utils/preflightChecks.tsx @@ -4,7 +4,7 @@ import { logEvent } from 'src/services/analytics/index.js' import { Spinner } from '../components/Spinner.js' import { getOauthConfig } from '../constants/oauth.js' import { useTimeout } from '../hooks/useTimeout.js' -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import { getSSLErrorHint } from '../services/api/errorUtils.js' import { getUserAgent } from './http.js' import { logError } from './log.js' diff --git a/src/utils/promptEditor.ts b/src/utils/promptEditor.ts index 0b0a65241..36a2b7bcd 100644 --- a/src/utils/promptEditor.ts +++ b/src/utils/promptEditor.ts @@ -3,7 +3,7 @@ import { formatPastedTextRef, getPastedTextRefNumLines, } from '../history.js' -import { instances } from '@anthropic/ink' +import instances from '../ink/instances.js' import type { PastedContent } from './config.js' import { classifyGuiEditor, getExternalEditor } from './editor.js' import { execSync_DEPRECATED } from './execSyncWrapper.js' diff --git a/src/utils/renderOptions.ts b/src/utils/renderOptions.ts index c601b36a3..37bb65050 100644 --- a/src/utils/renderOptions.ts +++ b/src/utils/renderOptions.ts @@ -1,6 +1,6 @@ import { openSync } from 'fs' import { ReadStream } from 'tty' -import type { RenderOptions } from '@anthropic/ink' +import type { RenderOptions } from '../ink.js' import { isEnvTruthy } from './envUtils.js' import { logError } from './log.js' diff --git a/src/utils/sinks.ts b/src/utils/sinks.ts index ba3999587..386ce6ae4 100644 --- a/src/utils/sinks.ts +++ b/src/utils/sinks.ts @@ -1,6 +1,5 @@ - -import { initializeErrorLogSink } from './errorLogSink.js' import { initializeAnalyticsSink } from '../services/analytics/sink.js' +import { initializeErrorLogSink } from './errorLogSink.js' /** * Attach error log and analytics sinks, draining any events queued before diff --git a/src/utils/sliceAnsi.ts b/src/utils/sliceAnsi.ts index 565a5f8cd..8e1a0a07f 100644 --- a/src/utils/sliceAnsi.ts +++ b/src/utils/sliceAnsi.ts @@ -5,7 +5,7 @@ import { tokenize, undoAnsiCodes, } from '@alcalzone/ansi-tokenize' -import { stringWidth } from '@anthropic/ink' +import { stringWidth } from '../ink/stringWidth.js' // A code is an "end code" if its code equals its endCode (e.g., hyperlink close) function isEndCode(code: AnsiCode): boolean { diff --git a/src/utils/staticRender.tsx b/src/utils/staticRender.tsx index 2f066fe96..66a809c19 100644 --- a/src/utils/staticRender.tsx +++ b/src/utils/staticRender.tsx @@ -2,7 +2,7 @@ import * as React from 'react' import { useLayoutEffect } from 'react' import { PassThrough } from 'stream' import stripAnsi from 'strip-ansi' -import { wrappedRender as render, useApp } from '@anthropic/ink' +import { render, useApp } from '../ink.js' // This is a workaround for the fact that Ink doesn't support multiple // components in the same render tree. Instead of using a we just render diff --git a/src/utils/status.tsx b/src/utils/status.tsx index 5b4012fff..6d66bc83f 100644 --- a/src/utils/status.tsx +++ b/src/utils/status.tsx @@ -1,7 +1,7 @@ import chalk from 'chalk' import figures from 'figures' import * as React from 'react' -import { color, Text } from '@anthropic/ink' +import { color, Text } from '../ink.js' import type { MCPServerConnection } from '../services/mcp/types.js' import { getAccountInformation, isClaudeAISubscriber } from './auth.js' import { diff --git a/src/utils/statusNoticeDefinitions.tsx b/src/utils/statusNoticeDefinitions.tsx index 9ad173fd0..e04f9e3d3 100644 --- a/src/utils/statusNoticeDefinitions.tsx +++ b/src/utils/statusNoticeDefinitions.tsx @@ -1,5 +1,5 @@ // biome-ignore-all assist/source/organizeImports: ANT-ONLY import markers must not be reordered -import { Box, Text } from '@anthropic/ink' +import { Box, Text } from '../ink.js' import * as React from 'react' import { getLargeMemoryFiles, diff --git a/src/utils/swarm/It2SetupPrompt.tsx b/src/utils/swarm/It2SetupPrompt.tsx index 166c984c7..688e2c8f4 100644 --- a/src/utils/swarm/It2SetupPrompt.tsx +++ b/src/utils/swarm/It2SetupPrompt.tsx @@ -3,11 +3,11 @@ import { type OptionWithDescription, Select, } from '../../components/CustomSelect/index.js' -import { Pane } from '@anthropic/ink' +import { Pane } from '../../components/design-system/Pane.js' import { Spinner } from '../../components/Spinner.js' import { useExitOnCtrlCDWithKeybindings } from '../../hooks/useExitOnCtrlCDWithKeybindings.js' // eslint-disable-next-line custom-rules/prefer-use-keybindings -- enter to proceed through setup steps -import { Box, Text, useInput } from '@anthropic/ink' +import { Box, Text, useInput } from '../../ink.js' import { useKeybinding } from '../../keybindings/useKeybinding.js' import { detectPythonPackageManager, diff --git a/src/utils/teleport.tsx b/src/utils/teleport.tsx index 513196fe4..04202dbc7 100644 --- a/src/utils/teleport.tsx +++ b/src/utils/teleport.tsx @@ -17,7 +17,7 @@ import { } from '../components/TeleportError.js' import { getOauthConfig } from '../constants/oauth.js' import type { SDKMessage } from '../entrypoints/agentSdkTypes.js' -import type { Root } from '@anthropic/ink' +import type { Root } from '../ink.js' import { KeybindingSetup } from '../keybindings/KeybindingProviderSetup.js' import { queryHaiku } from '../services/api/claude.js' import { diff --git a/src/utils/terminal.ts b/src/utils/terminal.ts index a935f700c..a87f020c8 100644 --- a/src/utils/terminal.ts +++ b/src/utils/terminal.ts @@ -1,6 +1,6 @@ import chalk from 'chalk' import { ctrlOToExpand } from '../components/CtrlOToExpand.js' -import { stringWidth } from '@anthropic/ink' +import { stringWidth } from '../ink/stringWidth.js' import sliceAnsi from './sliceAnsi.js' // Text rendering utilities for terminal display diff --git a/src/utils/terminalPanel.ts b/src/utils/terminalPanel.ts index 9cf56c554..034d22bed 100644 --- a/src/utils/terminalPanel.ts +++ b/src/utils/terminalPanel.ts @@ -17,7 +17,7 @@ import { spawn, spawnSync } from 'child_process' import { getSessionId } from '../bootstrap/state.js' -import { instances } from '@anthropic/ink' +import instances from '../ink/instances.js' import { registerCleanup } from './cleanupRegistry.js' import { pwd } from './cwd.js' import { logForDebugging } from './debug.js' diff --git a/src/utils/treeify.ts b/src/utils/treeify.ts index 3b270ad4d..28dc9d83b 100644 --- a/src/utils/treeify.ts +++ b/src/utils/treeify.ts @@ -1,5 +1,5 @@ import figures from 'figures' -import { color } from '@anthropic/ink' +import { color } from '../components/design-system/color.js' import type { Theme, ThemeName } from './theme.js' export type TreeNode = { diff --git a/src/utils/truncate.ts b/src/utils/truncate.ts index aff5c72ef..fa35c10e4 100644 --- a/src/utils/truncate.ts +++ b/src/utils/truncate.ts @@ -1,6 +1,6 @@ // Width-aware truncation/wrapping — needs ink/stringWidth (not leaf-safe). -import { stringWidth } from '@anthropic/ink' +import { stringWidth } from '../ink/stringWidth.js' import { getGraphemeSegmenter } from './intl.js' /**