refactor(ui-primitives): extract shared head/tail cap and copy-feedback helpers
The tail-header cap fix pushed SearchBlock's head/tail slicing arithmetic and its copy-feedback hook over the duplication gate's threshold against the byte-identical logic in TerminalBlock. Extract both into head-tail-cap.ts (headTailCap) and use-copy-feedback.ts (useCopyFeedback) and consume them from both blocks, deleting the clone rather than nudging it under the limit.
This commit is contained in:
@@ -8,7 +8,8 @@
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
import clsx from 'clsx'
|
||||
import { parseAnsiLines, type AnsiLine } from './ansi.ts'
|
||||
import { writeClipboard } from './clipboard.ts'
|
||||
import { headTailCap } from './head-tail-cap.ts'
|
||||
import { useCopyFeedback } from './use-copy-feedback.ts'
|
||||
import { Pill } from './Pill.tsx'
|
||||
import { StateDot, type StateDotState } from './StateDot.tsx'
|
||||
import css from './TerminalBlock.module.css'
|
||||
@@ -140,18 +141,9 @@ export function TerminalBlock({
|
||||
return terminated ? parsed.slice(0, -1) : parsed
|
||||
}, [text])
|
||||
const [expanded, setExpanded] = useState(false)
|
||||
const [copied, setCopied] = useState(false)
|
||||
|
||||
const onCopy = useCallback(() => {
|
||||
if (copied) return
|
||||
// The raw output, never the rendered tree: the prompt line and the status
|
||||
// pill are chrome the user did not run.
|
||||
void writeClipboard(text).then((ok) => {
|
||||
if (!ok) return
|
||||
setCopied(true)
|
||||
window.setTimeout(() => { setCopied(false) }, 1000)
|
||||
})
|
||||
}, [copied, text])
|
||||
// The raw output, never the rendered tree: the prompt line and the status pill
|
||||
// are chrome the user did not run.
|
||||
const { copied, onCopy } = useCopyFeedback(text)
|
||||
|
||||
const onToggle = useCallback(() => { setExpanded(value => !value) }, [])
|
||||
|
||||
@@ -170,12 +162,7 @@ export function TerminalBlock({
|
||||
// the raw text drew an output box of blank rows plus a copy control for
|
||||
// invisible bytes, and hid the placeholder that belongs there.
|
||||
const empty = lines.every(line => line.every(span => span.text.trim() === ''))
|
||||
const hidden = lines.length - maxLines
|
||||
const capped = hidden > 0 && !expanded
|
||||
// Same split arithmetic as the TUI transcript's collapsed tool card, so a
|
||||
// command's head and tail slices agree between the two front ends.
|
||||
const headLines = Math.ceil(maxLines / 2)
|
||||
const tailLines = maxLines - headLines
|
||||
const { hidden, capped, headLines, tailLines } = headTailCap(lines.length, maxLines, expanded)
|
||||
|
||||
return (
|
||||
<div className={clsx(css.block, className)} data-terminal="" data-running={running ? '' : undefined}>
|
||||
|
||||
Reference in New Issue
Block a user