Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

- Fix patching for CC 2.1.9 (#380) - @basekevin
- Add input pattern highlighters (#387) - @bl-ue

## [v3.3.0](https://github.com/Piebald-AI/tweakcc/releases/tag/v3.3.0) - 2026-01-18
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,15 +115,15 @@ Here's one where various common patterns like environment variables, file paths,

![Input box highlighting environment variables, file paths, numbers, and markdown constructs](./assets/input_pattern_highlight_2_common_patterns.png)

Finally, here's one showing how you can render extra characters that aren't really part of the prompt by customizing the **format string**. The first line shows a copy of what I've actually got typed into the prompt, and in the prompt itself you can see that `cluade` was _visually_ (but not _in reality_) replaced with `Claude Code, ...`, etc.
Finally, here's one showing how you can render extra characters that aren't really part of the prompt by customizing the **format string**. The first line shows a copy of what I've actually got typed into the prompt, and in the prompt itself you can see that `cluade` was _visually_ (but not _in reality_) replaced with `Claude Code, ...`, etc.

![Input box demonstrating format strings rendering extra characters not in the actual prompt](./assets/input_pattern_highlight_3_with_format_string.png)
To add some patterns, you can use the tweakcc UI or edit [`~/.tweakcc/config.json`](#configuration-directory) manually.

**Via the UI:**

| Listing | Edit |
| ---------------------------------------------------- | ------------------------------------------------- |
| Listing | Edit |
| ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| ![Input pattern highlighters listing view showing configured patterns](./assets/input_pattern_highlighters_listing.png) | ![Input pattern highlighter edit view with fields for name, regex, colors, and styling](./assets/input_pattern_highlighters_edit.png) |

**Via `config.json`:**
Expand Down
59 changes: 42 additions & 17 deletions src/patches/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -423,31 +423,56 @@ export const findTextComponent = (fileContents: string): string | undefined => {
* Find the Box component variable name
*/
export const findBoxComponent = (fileContents: string): string | undefined => {
// 1. Search for Box displayName
// Method 1: Search for Box displayName (older CC versions)
const boxDisplayNamePattern = /\b([$\w]+)\.displayName="Box"/;
const boxDisplayNameMatch = fileContents.match(boxDisplayNamePattern);
if (!boxDisplayNameMatch) {
console.error('patch: findBoxComponent: failed to find Box displayName');
return undefined;
if (boxDisplayNameMatch) {
const boxOrigCompName = boxDisplayNameMatch[1];

// Search for the variable that equals the original Box component
const boxVarPattern = new RegExp(
// /[^$\w]/ = /\b/ but considering dollar signs word characters.
// Normally /$\b/ does NOT match "$}" but this does.
// Because once boxOrigCompName was `LK$` so `_=LK$}` wasn't matching.
`\\b([$\\w]+)=${escapeIdent(boxOrigCompName)}[^$\\w]`
);
const boxVarMatch = fileContents.match(boxVarPattern);
if (boxVarMatch) {
return boxVarMatch[1];
}
console.error(
`patch: findBoxComponent: found Box displayName but failed to find Box component variable (boxOrigCompName=${boxOrigCompName})`
);
}
const boxOrigCompName = boxDisplayNameMatch[1];

// 2. Search for the variable that equals the original Box component
const boxVarPattern = new RegExp(
// /[^$\w]/ = /\b/ but considering dollar signs word characters.
// Normally /$\b/ does NOT match "$}" but this does.
// Because once boxOrigCompName was `LK$` so `_=LK$}` wasn't matching.
`\\b([$\\w]+)=${escapeIdent(boxOrigCompName)}[^$\\w]`
);
const boxVarMatch = fileContents.match(boxVarPattern);
if (!boxVarMatch) {
// Method 2: Find Box component by its unique function signature (newer CLI versions 2.1.8+)
// The internal Box function has a unique signature: function X({children:Y,flexWrap:Z="nowrap",flexDirection:W="row",...
const internalBoxPattern =
/\bfunction ([$\w]+)\(\{children:[$\w]+,flexWrap:[$\w]+="nowrap",flexDirection:[$\w]+="row",flexGrow:[$\w]+=[0-9]+,flexShrink:[$\w]+=[0-9]+/;
const internalBoxMatch = fileContents.match(internalBoxPattern);
if (internalBoxMatch) {
const internalBoxFn = internalBoxMatch[1];

// Find the variable that's assigned the internal Box function: boxVar=internalBoxFn}
// Pattern like: eq=uW8}
const boxVarAssignPattern = new RegExp(
`\\b([$\\w]+)=${escapeIdent(internalBoxFn)}\\}`
);
const boxVarAssignMatch = fileContents.match(boxVarAssignPattern);
if (boxVarAssignMatch) {
return boxVarAssignMatch[1];
}
// If no assignment found, the internal function itself might be used directly
console.error(
`patch: findBoxComponent: failed to find Box component variable (boxOrigCompName=${boxOrigCompName})`
`patch: findBoxComponent: found internal Box function (${internalBoxFn}) but no assignment found`
);
return undefined;
return internalBoxFn;
}

return boxVarMatch[1];
console.error(
'patch: findBoxComponent: failed to find Box component (neither displayName nor function signature found)'
);
return undefined;
};

export const applyCustomization = async (
Expand Down
3 changes: 2 additions & 1 deletion src/patches/toolsets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,9 +172,10 @@ export const getToolFetchingUseMemoLocation = (
const chunk = fileContents.slice(bodyStart, bodyStart + 2000);

// Pattern to match: outputVar=reactVar.useMemo(()=>filterFunc(contextVar),[contextVar])
// Or (CC 2.1.9+): outputVar=reactVar.useMemo(()=>filterFunc(contextVar),[contextVar,extraDep])
// Note: may be comma-separated (,v=...) or let-prefixed (let v=...)
const useMemoPattern =
/(?:let |,)([$\w]+)=([$\w]+)\.useMemo\(\(\)=>([$\w]+)\(([$\w]+)\),\[\4\]\)/;
/(?:let |,)([$\w]+)=([$\w]+)\.useMemo\(\(\)=>([$\w]+)\(([$\w]+)\),\[\4(?:,[$\w]+)?\]\)/;
const match = chunk.match(useMemoPattern);

if (!match || match.index === undefined) {
Expand Down