From 936fde6f1aa4ba00805b698ba892f10d0dac0d22 Mon Sep 17 00:00:00 2001 From: Felix Lu Date: Sat, 30 Nov 2024 19:45:30 -0800 Subject: [PATCH 1/6] refactor: improve feature cleanup and attribute handling - Move attribute cleanup responsibility to individual features - Use consistent data-tc- prefix for custom attributes - Remove centralized attribute cleanup from FeatureManager - Add comprehensive feature-level cleanup in TCThinkingBlock - Update CSS selectors to use data-tc- prefix --- extensions/chrome/public/styles.css | 10 +++++----- .../src/content/v3/features/thinking-block/index.ts | 10 +++++++++- .../features/thinking-block/process-thinking-block.ts | 4 ++-- .../v3/features/thinking-block/setup-controls.ts | 9 ++++++--- .../chrome/src/content/v3/managers/feature-manager.ts | 5 ----- 5 files changed, 22 insertions(+), 16 deletions(-) diff --git a/extensions/chrome/public/styles.css b/extensions/chrome/public/styles.css index 486c051..a0c6780 100644 --- a/extensions/chrome/public/styles.css +++ b/extensions/chrome/public/styles.css @@ -28,7 +28,7 @@ div[data-is-streaming] pre:first-child .code-block__code.collapsed { } /* Collapsed state */ -[data-thinking-block-state="collapsed"] +[data-tc-thinking-block-state="collapsed"] div[data-is-streaming] pre:first-child .code-block__code { @@ -39,7 +39,7 @@ div[data-is-streaming] pre:first-child .code-block__code.collapsed { } /* Expanded state */ -/* [data-thinking-block-state="expanded"] div[data-is-streaming] pre:first-child .code-block__code { +/* [data-tc-thinking-block-state="expanded"] div[data-is-streaming] pre:first-child .code-block__code { height: 50vh !important; padding: 1em !important; visibility: visible !important; @@ -105,7 +105,7 @@ div[data-is-streaming] /* Update the text of the header */ -div[data-thinking-block-state="collapsed"] +div[data-tc-thinking-block-state="collapsed"] pre:first-child .text-text-300.absolute.pl-3.pt-2\.5.text-xs:not(:empty)::after { content: "View Claude's thinking" !important; @@ -127,7 +127,7 @@ div[data-is-streaming] /* Hover state */ /* This implementation is limited. We can consider to disable this hover state. */ -[data-thinking-block-state="expanded"] +[data-tc-thinking-block-state="expanded"] pre:first-child .text-text-300.absolute.pl-3.pt-2\.5.text-xs:not(:empty):hover::after { color: hsl(var(--text-100)); @@ -191,7 +191,7 @@ pre:first-child /* Chevron animation */ -[data-thinking-block-state="collapsed"] +[data-tc-thinking-block-state="collapsed"] pre:first-child .text-text-300.absolute.pl-3.pt-2\.5.text-xs:not(:empty)::before { transform: rotate(180deg); diff --git a/extensions/chrome/src/content/v3/features/thinking-block/index.ts b/extensions/chrome/src/content/v3/features/thinking-block/index.ts index 368b1b8..b65c887 100644 --- a/extensions/chrome/src/content/v3/features/thinking-block/index.ts +++ b/extensions/chrome/src/content/v3/features/thinking-block/index.ts @@ -18,7 +18,7 @@ export class TCThinkingBlock extends BaseFeature { /** * Initialize the thinking block feature * Sets up mutation observer to watch for new thinking blocks - * @returns Cleanup function to unsubscribe from mutation observer + * @returns Cleanup function to unsubscribe from mutation observer and remove custom attributes */ initialize(): void | (() => void) { this.mutationObserver.initialize() @@ -28,6 +28,14 @@ export class TCThinkingBlock extends BaseFeature { return () => { // Unsubscribe from mutation observer unsubscribe() + + // Clean up all feature-specific attributes + document + .querySelectorAll("[data-tc-processed]") + .forEach((el) => el.removeAttribute("data-tc-processed")) + document + .querySelectorAll("[data-tc-thinking-block-state]") + .forEach((el) => el.removeAttribute("data-tc-thinking-block-state")) } } } diff --git a/extensions/chrome/src/content/v3/features/thinking-block/process-thinking-block.ts b/extensions/chrome/src/content/v3/features/thinking-block/process-thinking-block.ts index 775b743..7cd89c0 100644 --- a/extensions/chrome/src/content/v3/features/thinking-block/process-thinking-block.ts +++ b/extensions/chrome/src/content/v3/features/thinking-block/process-thinking-block.ts @@ -20,8 +20,8 @@ function processControl(control: Element) { ?.querySelector(".code-block__code") if (!thinkingBlock) return - if (!resContainer.hasAttribute("data-thinking-block-state")) { - resContainer.setAttribute("data-thinking-block-state", "expanded") + if (!resContainer.hasAttribute("data-tc-thinking-block-state")) { + resContainer.setAttribute("data-tc-thinking-block-state", "expanded") } setupControls(control as HTMLElement, thinkingBlock, resContainer) diff --git a/extensions/chrome/src/content/v3/features/thinking-block/setup-controls.ts b/extensions/chrome/src/content/v3/features/thinking-block/setup-controls.ts index a48e33a..e0ece7f 100644 --- a/extensions/chrome/src/content/v3/features/thinking-block/setup-controls.ts +++ b/extensions/chrome/src/content/v3/features/thinking-block/setup-controls.ts @@ -4,9 +4,10 @@ export function setupControls( resContainer: Element ) { const copyButton = control.querySelector("button") - if (!copyButton) return + copyButton.classList.add("tc-select-none") + copyButton.addEventListener( "click", async (e) => { @@ -17,11 +18,13 @@ export function setupControls( ) control.addEventListener("click", () => { - const currentState = resContainer.getAttribute("data-thinking-block-state") + const currentState = resContainer.getAttribute( + "data-tc-thinking-block-state" + ) // Update the collapse state of thinking block in the response container const newState = currentState === "expanded" ? "collapsed" : "expanded" - resContainer.setAttribute("data-thinking-block-state", newState) + resContainer.setAttribute("data-tc-thinking-block-state", newState) // Toggle the collapse state of the thinking block as fallback thinkingBlock.classList.toggle("collapsed") diff --git a/extensions/chrome/src/content/v3/managers/feature-manager.ts b/extensions/chrome/src/content/v3/managers/feature-manager.ts index 8e99581..762dfee 100644 --- a/extensions/chrome/src/content/v3/managers/feature-manager.ts +++ b/extensions/chrome/src/content/v3/managers/feature-manager.ts @@ -36,11 +36,6 @@ export class FeatureManager { * Clean up all features */ cleanup(): void { - // Remove data-tc-processed attributes from thinking block controls - document.querySelectorAll("[data-tc-processed]").forEach((element) => { - element.removeAttribute("data-tc-processed") - }) - this.cleanupFunctions.forEach((cleanup, id) => { try { cleanup() From e052aa59d661fee580aa54d031f37871dc536aba Mon Sep 17 00:00:00 2001 From: Felix Lu Date: Sat, 30 Nov 2024 19:49:48 -0800 Subject: [PATCH 2/6] refactor: remove unnecessary globals.css import CSS is already properly loaded through manifest.json's content_scripts configuration --- extensions/chrome/src/content/index.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/extensions/chrome/src/content/index.ts b/extensions/chrome/src/content/index.ts index d1df735..6cd953e 100644 --- a/extensions/chrome/src/content/index.ts +++ b/extensions/chrome/src/content/index.ts @@ -1,5 +1,3 @@ -import "@/styles/globals.css" - import { ExtensionManager } from "./v3/managers/extension-manager" // Create a single instance of ExtensionManager From 95a24ba776f7068897e11df303afcbdb6cd5da41 Mon Sep 17 00:00:00 2001 From: Felix Lu Date: Sat, 30 Nov 2024 23:12:45 -0800 Subject: [PATCH 3/6] refactor(styles): reorganize CSS using layer system and improve structure --- extensions/chrome/public/styles.css | 342 +++++++++++++++------------- 1 file changed, 179 insertions(+), 163 deletions(-) diff --git a/extensions/chrome/public/styles.css b/extensions/chrome/public/styles.css index a0c6780..f61e120 100644 --- a/extensions/chrome/public/styles.css +++ b/extensions/chrome/public/styles.css @@ -1,182 +1,198 @@ -/* Overwrite original claude thinking block content styles */ -div[data-is-streaming] pre:first-child .code-block__code { - background: none !important; - white-space: pre-wrap !important; - word-wrap: break-word !important; - overflow-y: auto !important; - overflow-x: hidden !important; - color: var(--text-text-200) !important; - padding: 1em !important; - max-width: 100%; - display: block; - height: auto !important; - min-height: 0vh !important; - max-height: 50vh !important; - visibility: visible !important; - opacity: 1 !important; - transition: all 0.3s ease-out !important; -} +/* Define layer order */ +@layer base, utilities, thinking-block, input-selector; + +/* Base Layer */ +@layer base { + /* Overwrite original claude thinking block content styles */ + div[data-is-streaming] pre:first-child .code-block__code { + background: none !important; + white-space: pre-wrap !important; + word-wrap: break-word !important; + overflow-y: auto !important; + overflow-x: hidden !important; + color: var(--text-text-200) !important; + padding: 1em !important; + max-width: 100%; + display: block; + height: auto !important; + min-height: 0vh !important; + max-height: 50vh !important; + visibility: visible !important; + opacity: 1 !important; + transition: all 0.3s ease-out !important; + } -/* collapsed states */ + /* Collapsed states */ -/* Collapsed state */ -div[data-is-streaming] pre:first-child .code-block__code.collapsed { - height: 0 !important; - padding: 0 !important; - visibility: hidden !important; - opacity: 0 !important; -} + /* Collapsed state */ + div[data-is-streaming] pre:first-child .code-block__code.collapsed { + height: 0 !important; + padding: 0 !important; + visibility: hidden !important; + opacity: 0 !important; + } -/* Collapsed state */ -[data-tc-thinking-block-state="collapsed"] - div[data-is-streaming] - pre:first-child - .code-block__code { - height: 0 !important; - padding: 0 !important; - visibility: hidden !important; - opacity: 0 !important; -} + /* Expanded state */ + /* [data-tc-thinking-block-state="expanded"] div[data-is-streaming] pre:first-child .code-block__code { + height: 50vh !important; + padding: 1em !important; + visibility: visible !important; + opacity: 1 !important; + } */ + + div[data-is-streaming] pre:first-child code { + background: none !important; + white-space: pre-wrap !important; + word-wrap: break-word !important; + text-wrap: balance !important; + color: hsl(var(--text-300)) !important; + font-size: 0.875rem !important; + display: block !important; + max-width: 100% !important; + text-shadow: none !important; + } -/* Expanded state */ -/* [data-tc-thinking-block-state="expanded"] div[data-is-streaming] pre:first-child .code-block__code { - height: 50vh !important; - padding: 1em !important; - visibility: visible !important; - opacity: 1 !important; -} */ - -div[data-is-streaming] pre:first-child code { - background: none !important; - white-space: pre-wrap !important; - word-wrap: break-word !important; - text-wrap: balance !important; - color: hsl(var(--text-300)) !important; - font-size: 0.875rem !important; - display: block !important; - max-width: 100% !important; - text-shadow: none !important; -} + /* Add selection styles */ + code span::selection { + background: hsl(var(--clay) / var(--tw-text-opacity)) !important; + color: hsl(var(--text-100)) !important; + } -/* Add selection styles */ -code span::selection { - background: hsl(var(--clay) / var(--tw-text-opacity)) !important; - color: hsl(var(--text-100)) !important; -} + code span::-moz-selection { + background: hsl(var(--clay) / var(--tw-text-opacity)) !important; + color: hsl(var(--text-100)) !important; + } -code span::-moz-selection { - background: hsl(var(--clay) / var(--tw-text-opacity)) !important; - color: hsl(var(--text-100)) !important; -} + code span:hover { + transition: color 0.4s ease; + color: hsl(var(--text-100)); + } -code span:hover { - transition: color 0.4s ease; - color: hsl(var(--text-100)); -} + /* --------------------------------- */ + + /* Copy button container */ + div[data-is-streaming] pre:first-child .pointer-events-none.sticky { + cursor: pointer !important; + pointer-events: auto !important; + } -/* --------------------------------- */ + /* Copy button container */ + div[data-is-streaming] pre:first-child .from-bg-300\\/90 { + pointer-events: auto !important; + user-select: none !important; + } -/* Copy button container */ -div[data-is-streaming] pre:first-child .pointer-events-none.sticky { - cursor: pointer !important; - pointer-events: auto !important; -} + /* --------------------------------- */ -/* Copy button container */ -div[data-is-streaming] pre:first-child .from-bg-300\\/90 { - pointer-events: auto !important; - user-select: none !important; -} + /* Update the header text */ + /* This is the original header text */ + div[data-is-streaming] + pre:first-child + .text-text-300.absolute.pl-3.pt-2\.5.text-xs:not(:empty) { + font-size: 0 !important; /* Hide original text */ + pointer-events: auto !important; + cursor: pointer !important; + display: inline-flex; + align-items: center; + font-family: var(--font-user-message); + } -/* --------------------------------- */ + div[data-is-streaming] + pre:first-child + .text-text-300.absolute.pl-3.pt-2\.5.text-xs:not(:empty)::after { + content: "Claude's thinking"; + font-size: 0.875rem; /* Restore font size */ + cursor: pointer; + font-family: var(--font-user-message); + transition: color 0.15s ease-in-out; + } -/* Update the header text */ -/* This is the original header text */ -div[data-is-streaming] - pre:first-child - .text-text-300.absolute.pl-3.pt-2\.5.text-xs:not(:empty) { - font-size: 0; /* Hide original text */ - pointer-events: auto !important; /* Make sure it's clickable */ - cursor: pointer !important; - display: inline-flex; - align-items: center; - font-family: var(--font-user-message); -} + /* Hover state */ + /* This implementation is limited. We can consider to disable this hover state. */ + [data-tc-thinking-block-state="expanded"] + pre:first-child + .text-text-300.absolute.pl-3.pt-2\.5.text-xs:not(:empty):hover::after { + color: hsl(var(--text-100)); + content: "Hide Claude's thinking"; + } -/* Update the text of the header */ + /* Streaming state styles */ + div[data-is-streaming="true"] + pre:first-child + .text-text-300.absolute.pl-3.pt-2\.5.text-xs:not(:empty)::after { + content: "Claude is thinking..."; + background: linear-gradient( + 90deg, + rgba(156, 163, 175, 0.7) 0%, + rgba(209, 213, 219, 1) 25%, + rgba(156, 163, 175, 0.7) 50%, + rgba(209, 213, 219, 1) 75%, + rgba(156, 163, 175, 0.7) 100% + ); + background-size: 200% 100%; + animation: gradientWave 3s linear infinite; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + color: transparent; + cursor: pointer; + font-family: var(--font-user-message); + } -div[data-tc-thinking-block-state="collapsed"] + /* Chevron-down icon */ pre:first-child - .text-text-300.absolute.pl-3.pt-2\.5.text-xs:not(:empty)::after { - content: "View Claude's thinking" !important; - font-size: 0.875rem; /* Restore font size */ - cursor: pointer; - font-family: var(--font-user-message); - transition: color 0.15s ease-in-out; + .text-text-300.absolute.pl-3.pt-2\.5.text-xs:not(:empty)::before { + content: ""; + width: 15px; + height: 15px; + margin-right: 0.25rem; + -webkit-mask-image: url('data:image/svg+xml;utf8,'); + mask-image: url('data:image/svg+xml;utf8,'); + -webkit-mask-size: contain; + mask-size: contain; + -webkit-mask-repeat: no-repeat; + mask-repeat: no-repeat; + background-color: hsl(var(--text-500)); + transform: translateY(-1px); + transition: transform 0.25s ease-out; + } } -div[data-is-streaming] - pre:first-child - .text-text-300.absolute.pl-3.pt-2\.5.text-xs:not(:empty)::after { - content: "Claude's thinking"; - font-size: 0.875rem; /* Restore font size */ - cursor: pointer; - font-family: var(--font-user-message); - transition: color 0.15s ease-in-out; +/* Utilities Layer */ +@layer utilities { + .tc-select-none { + user-select: none; + } } -/* Hover state */ -/* This implementation is limited. We can consider to disable this hover state. */ -[data-tc-thinking-block-state="expanded"] - pre:first-child - .text-text-300.absolute.pl-3.pt-2\.5.text-xs:not(:empty):hover::after { - color: hsl(var(--text-100)); - content: "Hide Claude's thinking"; -} +/* Thinking Block Feature Layer */ +@layer thinking-block { + /* Collapsed state */ + [data-tc-thinking-block-state="collapsed"] + div[data-is-streaming] + pre:first-child + .code-block__code { + height: 0 !important; + padding: 0 !important; + visibility: hidden !important; + margin: 0 !important; + } -/* Streaming state styles */ -div[data-is-streaming="true"] - pre:first-child - .text-text-300.absolute.pl-3.pt-2\.5.text-xs:not(:empty)::after { - content: "Claude is thinking..."; - background: linear-gradient( - 90deg, - rgba(156, 163, 175, 0.7) 0%, - rgba(209, 213, 219, 1) 25%, - rgba(156, 163, 175, 0.7) 50%, - rgba(209, 213, 219, 1) 75%, - rgba(156, 163, 175, 0.7) 100% - ); - background-size: 200% 100%; - animation: gradientWave 3s linear infinite; - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; - color: transparent; - cursor: pointer; - font-family: var(--font-user-message); -} + /* Update the text of the header */ + div[data-tc-thinking-block-state="collapsed"] + pre:first-child + .text-text-300.absolute.pl-3.pt-2\.5.text-xs:not(:empty)::after { + content: "View Claude's thinking" !important; + } -/* Chevron-down icon */ -pre:first-child - .text-text-300.absolute.pl-3.pt-2\.5.text-xs:not(:empty)::before { - content: ""; - width: 15px; - height: 15px; - margin-right: 0.25rem; - -webkit-mask-image: url('data:image/svg+xml;utf8,'); - mask-image: url('data:image/svg+xml;utf8,'); - -webkit-mask-size: contain; - mask-size: contain; - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - background-color: hsl(var(--text-500)); - transform: translateY(-1px); - transition: transform 0.25s ease-out; + /* Chevron animation */ + [data-tc-thinking-block-state="collapsed"] + pre:first-child + .text-text-300.absolute.pl-3.pt-2\.5.text-xs:not(:empty)::before { + transform: rotate(180deg); + } } -/* --------------------------------- */ - /* Shimmer animation for streaming state */ @keyframes gradientWave { 0% { @@ -187,12 +203,12 @@ pre:first-child } } -/* --------------------------------- */ - -/* Chevron animation */ +/* ----------------------------------------------------------------- */ -[data-tc-thinking-block-state="collapsed"] - pre:first-child - .text-text-300.absolute.pl-3.pt-2\.5.text-xs:not(:empty)::before { - transform: rotate(180deg); +/* Input Selector Feature Layer */ +@layer input-selector { + /* New feature styles will go here */ + .tc-input-selector { + position: relative; + } } From dc6722b6e0facf4dc8d29fc6d0a205fa96eb591b Mon Sep 17 00:00:00 2001 From: Felix Lu Date: Sun, 1 Dec 2024 00:50:02 -0800 Subject: [PATCH 4/6] feat(styles): implement modular CSS architecture - Establish new CSS architecture to support feature-specific styles - Introduce modular CSS structure for better scalability - Set up base styles directory for shared Tailwind utilities - Separate thinking-block styles into feature-specific module - Configure webpack with MiniCssExtractPlugin for optimized CSS handling - Update manifest.json to reflect new CSS structure This change sets the foundation for a more maintainable and scalable CSS architecture, making it easier to add styles for future features while keeping them modular and isolated. --- extensions/chrome/public/manifest.json | 2 +- extensions/chrome/public/styles.css | 214 ------------------ extensions/chrome/src/content/index.ts | 2 + .../features/thinking-block/setup-controls.ts | 3 +- .../v3/features/thinking-block/styles.css | 195 ++++++++++++++++ extensions/chrome/src/styles/index.css | 4 + extensions/chrome/webpack/webpack.common.js | 17 +- 7 files changed, 212 insertions(+), 225 deletions(-) delete mode 100644 extensions/chrome/public/styles.css create mode 100644 extensions/chrome/src/content/v3/features/thinking-block/styles.css create mode 100644 extensions/chrome/src/styles/index.css diff --git a/extensions/chrome/public/manifest.json b/extensions/chrome/public/manifest.json index 7754eb9..09c53f3 100644 --- a/extensions/chrome/public/manifest.json +++ b/extensions/chrome/public/manifest.json @@ -10,7 +10,7 @@ { "matches": ["https://*.claude.ai/*"], "js": ["content.js"], - "css": ["styles.css"] + "css": ["content.css"] } ], "icons": { diff --git a/extensions/chrome/public/styles.css b/extensions/chrome/public/styles.css deleted file mode 100644 index f61e120..0000000 --- a/extensions/chrome/public/styles.css +++ /dev/null @@ -1,214 +0,0 @@ -/* Define layer order */ -@layer base, utilities, thinking-block, input-selector; - -/* Base Layer */ -@layer base { - /* Overwrite original claude thinking block content styles */ - div[data-is-streaming] pre:first-child .code-block__code { - background: none !important; - white-space: pre-wrap !important; - word-wrap: break-word !important; - overflow-y: auto !important; - overflow-x: hidden !important; - color: var(--text-text-200) !important; - padding: 1em !important; - max-width: 100%; - display: block; - height: auto !important; - min-height: 0vh !important; - max-height: 50vh !important; - visibility: visible !important; - opacity: 1 !important; - transition: all 0.3s ease-out !important; - } - - /* Collapsed states */ - - /* Collapsed state */ - div[data-is-streaming] pre:first-child .code-block__code.collapsed { - height: 0 !important; - padding: 0 !important; - visibility: hidden !important; - opacity: 0 !important; - } - - /* Expanded state */ - /* [data-tc-thinking-block-state="expanded"] div[data-is-streaming] pre:first-child .code-block__code { - height: 50vh !important; - padding: 1em !important; - visibility: visible !important; - opacity: 1 !important; - } */ - - div[data-is-streaming] pre:first-child code { - background: none !important; - white-space: pre-wrap !important; - word-wrap: break-word !important; - text-wrap: balance !important; - color: hsl(var(--text-300)) !important; - font-size: 0.875rem !important; - display: block !important; - max-width: 100% !important; - text-shadow: none !important; - } - - /* Add selection styles */ - code span::selection { - background: hsl(var(--clay) / var(--tw-text-opacity)) !important; - color: hsl(var(--text-100)) !important; - } - - code span::-moz-selection { - background: hsl(var(--clay) / var(--tw-text-opacity)) !important; - color: hsl(var(--text-100)) !important; - } - - code span:hover { - transition: color 0.4s ease; - color: hsl(var(--text-100)); - } - - /* --------------------------------- */ - - /* Copy button container */ - div[data-is-streaming] pre:first-child .pointer-events-none.sticky { - cursor: pointer !important; - pointer-events: auto !important; - } - - /* Copy button container */ - div[data-is-streaming] pre:first-child .from-bg-300\\/90 { - pointer-events: auto !important; - user-select: none !important; - } - - /* --------------------------------- */ - - /* Update the header text */ - /* This is the original header text */ - div[data-is-streaming] - pre:first-child - .text-text-300.absolute.pl-3.pt-2\.5.text-xs:not(:empty) { - font-size: 0 !important; /* Hide original text */ - pointer-events: auto !important; - cursor: pointer !important; - display: inline-flex; - align-items: center; - font-family: var(--font-user-message); - } - - div[data-is-streaming] - pre:first-child - .text-text-300.absolute.pl-3.pt-2\.5.text-xs:not(:empty)::after { - content: "Claude's thinking"; - font-size: 0.875rem; /* Restore font size */ - cursor: pointer; - font-family: var(--font-user-message); - transition: color 0.15s ease-in-out; - } - - /* Hover state */ - /* This implementation is limited. We can consider to disable this hover state. */ - [data-tc-thinking-block-state="expanded"] - pre:first-child - .text-text-300.absolute.pl-3.pt-2\.5.text-xs:not(:empty):hover::after { - color: hsl(var(--text-100)); - content: "Hide Claude's thinking"; - } - - /* Streaming state styles */ - div[data-is-streaming="true"] - pre:first-child - .text-text-300.absolute.pl-3.pt-2\.5.text-xs:not(:empty)::after { - content: "Claude is thinking..."; - background: linear-gradient( - 90deg, - rgba(156, 163, 175, 0.7) 0%, - rgba(209, 213, 219, 1) 25%, - rgba(156, 163, 175, 0.7) 50%, - rgba(209, 213, 219, 1) 75%, - rgba(156, 163, 175, 0.7) 100% - ); - background-size: 200% 100%; - animation: gradientWave 3s linear infinite; - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; - color: transparent; - cursor: pointer; - font-family: var(--font-user-message); - } - - /* Chevron-down icon */ - pre:first-child - .text-text-300.absolute.pl-3.pt-2\.5.text-xs:not(:empty)::before { - content: ""; - width: 15px; - height: 15px; - margin-right: 0.25rem; - -webkit-mask-image: url('data:image/svg+xml;utf8,'); - mask-image: url('data:image/svg+xml;utf8,'); - -webkit-mask-size: contain; - mask-size: contain; - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - background-color: hsl(var(--text-500)); - transform: translateY(-1px); - transition: transform 0.25s ease-out; - } -} - -/* Utilities Layer */ -@layer utilities { - .tc-select-none { - user-select: none; - } -} - -/* Thinking Block Feature Layer */ -@layer thinking-block { - /* Collapsed state */ - [data-tc-thinking-block-state="collapsed"] - div[data-is-streaming] - pre:first-child - .code-block__code { - height: 0 !important; - padding: 0 !important; - visibility: hidden !important; - margin: 0 !important; - } - - /* Update the text of the header */ - div[data-tc-thinking-block-state="collapsed"] - pre:first-child - .text-text-300.absolute.pl-3.pt-2\.5.text-xs:not(:empty)::after { - content: "View Claude's thinking" !important; - } - - /* Chevron animation */ - [data-tc-thinking-block-state="collapsed"] - pre:first-child - .text-text-300.absolute.pl-3.pt-2\.5.text-xs:not(:empty)::before { - transform: rotate(180deg); - } -} - -/* Shimmer animation for streaming state */ -@keyframes gradientWave { - 0% { - background-position: 200% 50%; - } - 100% { - background-position: -200% 50%; - } -} - -/* ----------------------------------------------------------------- */ - -/* Input Selector Feature Layer */ -@layer input-selector { - /* New feature styles will go here */ - .tc-input-selector { - position: relative; - } -} diff --git a/extensions/chrome/src/content/index.ts b/extensions/chrome/src/content/index.ts index 6cd953e..21750ab 100644 --- a/extensions/chrome/src/content/index.ts +++ b/extensions/chrome/src/content/index.ts @@ -1,3 +1,5 @@ +import "@/styles/index.css" + import { ExtensionManager } from "./v3/managers/extension-manager" // Create a single instance of ExtensionManager diff --git a/extensions/chrome/src/content/v3/features/thinking-block/setup-controls.ts b/extensions/chrome/src/content/v3/features/thinking-block/setup-controls.ts index e0ece7f..1c7d765 100644 --- a/extensions/chrome/src/content/v3/features/thinking-block/setup-controls.ts +++ b/extensions/chrome/src/content/v3/features/thinking-block/setup-controls.ts @@ -51,7 +51,8 @@ function updateCopyButtonUI(copyButton: Element) { const originalText = textSpan.textContent const originalSvgPath = svg.innerHTML - textSpan.textContent = "Copied" + // textSpan.textContent = "Copied" + svg.innerHTML = '' diff --git a/extensions/chrome/src/content/v3/features/thinking-block/styles.css b/extensions/chrome/src/content/v3/features/thinking-block/styles.css new file mode 100644 index 0000000..18c2576 --- /dev/null +++ b/extensions/chrome/src/content/v3/features/thinking-block/styles.css @@ -0,0 +1,195 @@ +/* Overwrite original claude thinking block content styles */ +div[data-is-streaming] pre:first-child .code-block__code { + background: none !important; + white-space: pre-wrap !important; + word-wrap: break-word !important; + overflow-y: auto !important; + overflow-x: hidden !important; + color: var(--text-text-200) !important; + padding: 1em !important; + max-width: 100%; + display: block; + height: auto !important; + min-height: 0vh !important; + max-height: 50vh !important; + visibility: visible !important; + opacity: 1 !important; + transition: all 0.3s ease-out !important; +} + +/* Collapsed states */ +div[data-is-streaming] pre:first-child .code-block__code.collapsed { + height: 0 !important; + padding: 0 !important; + visibility: hidden !important; + opacity: 0 !important; +} + +/* Collapsed state */ +[data-tc-thinking-block-state="collapsed"] + div[data-is-streaming] + pre:first-child + .code-block__code { + height: 0 !important; + padding: 0 !important; + visibility: hidden !important; + opacity: 0 !important; +} + +/* Expanded state */ +/* [data-tc-thinking-block-state="expanded"] div[data-is-streaming] pre:first-child .code-block__code { + height: 50vh !important; + padding: 1em !important; + visibility: visible !important; + opacity: 1 !important; +} */ + +div[data-is-streaming] pre:first-child code { + background: none !important; + white-space: pre-wrap !important; + word-wrap: break-word !important; + text-wrap: balance !important; + color: hsl(var(--text-300)) !important; + font-size: 0.875rem !important; + display: block !important; + max-width: 100% !important; + text-shadow: none !important; +} + +/* Add selection styles */ +code span::selection { + background: hsl(var(--clay) / var(--tw-text-opacity)) !important; + color: hsl(var(--text-100)) !important; +} + +code span::-moz-selection { + background: hsl(var(--clay) / var(--tw-text-opacity)) !important; + color: hsl(var(--text-100)) !important; +} + +code span:hover { + transition: color 0.4s ease; + color: hsl(var(--text-100)); +} + +/* --------------------------------- */ + +/* Copy button container */ +div[data-is-streaming] pre:first-child .pointer-events-none.sticky { + cursor: pointer !important; + pointer-events: auto !important; +} + +/* Copy button container */ +div[data-is-streaming] pre:first-child .from-bg-300\\/90 { + pointer-events: auto !important; + user-select: none !important; +} + +/* --------------------------------- */ + +/* Update the header text */ +/* This is the original header text */ +div[data-is-streaming] + pre:first-child + .text-text-300.absolute.pl-3.pt-2\.5.text-xs:not(:empty) { + font-size: 0; /* Hide original text */ + pointer-events: auto !important; /* Make sure it's clickable */ + cursor: pointer !important; + display: inline-flex; + align-items: center; + font-family: var(--font-user-message); +} + +/* Update the text of the header */ + +div[data-tc-thinking-block-state="collapsed"] + pre:first-child + .text-text-300.absolute.pl-3.pt-2\.5.text-xs:not(:empty)::after { + content: "View Claude's thinking" !important; + font-size: 0.875rem; /* Restore font size */ + cursor: pointer; + font-family: var(--font-user-message); + transition: color 0.15s ease-in-out; +} + +div[data-is-streaming] + pre:first-child + .text-text-300.absolute.pl-3.pt-2\.5.text-xs:not(:empty)::after { + content: "Claude's thinking"; + font-size: 0.875rem; /* Restore font size */ + cursor: pointer; + font-family: var(--font-user-message); + transition: color 0.15s ease-in-out; +} + +/* Hover state */ +/* This implementation is limited. We can consider to disable this hover state. */ +[data-tc-thinking-block-state="expanded"] + pre:first-child + .text-text-300.absolute.pl-3.pt-2\.5.text-xs:not(:empty):hover::after { + color: hsl(var(--text-100)); + content: "Hide Claude's thinking"; +} + +/* Streaming state styles */ +div[data-is-streaming="true"] + pre:first-child + .text-text-300.absolute.pl-3.pt-2\.5.text-xs:not(:empty)::after { + content: "Claude is thinking..."; + background: linear-gradient( + 90deg, + rgba(156, 163, 175, 0.7) 0%, + rgba(209, 213, 219, 1) 25%, + rgba(156, 163, 175, 0.7) 50%, + rgba(209, 213, 219, 1) 75%, + rgba(156, 163, 175, 0.7) 100% + ); + background-size: 200% 100%; + animation: gradientWave 3s linear infinite; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + color: transparent; + cursor: pointer; + font-family: var(--font-user-message); +} + +/* Chevron-down icon */ +pre:first-child + .text-text-300.absolute.pl-3.pt-2\.5.text-xs:not(:empty)::before { + content: ""; + width: 15px; + height: 15px; + margin-right: 0.25rem; + -webkit-mask-image: url('data:image/svg+xml;utf8,'); + mask-image: url('data:image/svg+xml;utf8,'); + -webkit-mask-size: contain; + mask-size: contain; + -webkit-mask-repeat: no-repeat; + mask-repeat: no-repeat; + background-color: hsl(var(--text-500)); + transform: translateY(-1px); + transition: transform 0.25s ease-out; +} + +/* Chevron animation */ +[data-tc-thinking-block-state="collapsed"] + pre:first-child + .text-text-300.absolute.pl-3.pt-2\.5.text-xs:not(:empty)::before { + transform: rotate(180deg) !important; +} + +/* --------------------------------- */ + +/* Shimmer animation for streaming state */ +@keyframes gradientWave { + 0% { + background-position: 200% 50%; + } + 100% { + background-position: -200% 50%; + } +} + +/* --------------------------------- */ diff --git a/extensions/chrome/src/styles/index.css b/extensions/chrome/src/styles/index.css new file mode 100644 index 0000000..c1841f5 --- /dev/null +++ b/extensions/chrome/src/styles/index.css @@ -0,0 +1,4 @@ +@import "./globals.css"; + +/* Import feature styles after Tailwind layers to ensure they have highest specificity */ +@import "@/content/v3/features/thinking-block/styles.css"; diff --git a/extensions/chrome/webpack/webpack.common.js b/extensions/chrome/webpack/webpack.common.js index c5a9f6b..6c20301 100644 --- a/extensions/chrome/webpack/webpack.common.js +++ b/extensions/chrome/webpack/webpack.common.js @@ -2,6 +2,7 @@ import path from "path" import { fileURLToPath } from "url" import CopyPlugin from "copy-webpack-plugin" +import MiniCssExtractPlugin from "mini-css-extract-plugin" const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) @@ -30,21 +31,14 @@ export default { { test: /\.css$/, use: [ - "style-loader", + MiniCssExtractPlugin.loader, { loader: "css-loader", options: { importLoaders: 1, }, }, - { - loader: "postcss-loader", - options: { - postcssOptions: { - config: path.resolve(__dirname, "..", "postcss.config.cjs"), - }, - }, - }, + "postcss-loader", ], }, ], @@ -61,11 +55,16 @@ export default { clean: true, }, plugins: [ + new MiniCssExtractPlugin(), new CopyPlugin({ patterns: [ { from: path.resolve(__dirname, "..", "public"), to: path.resolve(__dirname, "..", "dist"), + globOptions: { + ignore: ["**/*.css"], // Ignore CSS files + patterns: ["**/*.{html,json,png,svg,ico}"], // Only copy specific file types + }, }, ], }), From 95ea8f88255c897e87d7119a3981ba0bdd8f5c32 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 1 Dec 2024 08:51:45 +0000 Subject: [PATCH 5/6] chore: bump version to 3.1.5 [skip ci] --- extensions/chrome/package.json | 2 +- extensions/chrome/public/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/chrome/package.json b/extensions/chrome/package.json index bb5026b..8f7b772 100644 --- a/extensions/chrome/package.json +++ b/extensions/chrome/package.json @@ -1,6 +1,6 @@ { "name": "thinking-claude", - "version": "3.1.4", + "version": "3.1.5", "description": "Chrome extension for letting Claude think like a real human", "type": "module", "scripts": { diff --git a/extensions/chrome/public/manifest.json b/extensions/chrome/public/manifest.json index 09c53f3..ff4bad9 100644 --- a/extensions/chrome/public/manifest.json +++ b/extensions/chrome/public/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "Thinking Claude", - "version": "3.1.4", + "version": "3.1.5", "description": "Chrome extension for letting Claude think like a real human", "background": { "service_worker": "background.js" From 2e25459469f2cd436d6539af1ec653a4f6f7c530 Mon Sep 17 00:00:00 2001 From: Felix Lu Date: Sun, 1 Dec 2024 00:54:32 -0800 Subject: [PATCH 6/6] docs(changelog): add CSS architecture improvements - Document modular CSS architecture implementation - Add build system updates for CSS handling - Update changelog format and structure --- extensions/changelog.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/extensions/changelog.md b/extensions/changelog.md index ec99043..5cfb19d 100644 --- a/extensions/changelog.md +++ b/extensions/changelog.md @@ -2,6 +2,23 @@ # Changelog of the extensions +## feat: CSS Architecture - 12/1/2024 - @lumpinif + +### Modular CSS Architecture Implementation + +- Established new modular CSS architecture for better feature isolation + - Introduced feature-specific CSS modules starting with thinking-block + - Set up base styles directory for shared Tailwind utilities + - Improved organization and maintainability of styles + - Added support for future feature-specific styling needs + +### Build System Updates + +- Enhanced webpack configuration for CSS handling + - Integrated MiniCssExtractPlugin for optimized CSS delivery + - Updated manifest.json to reflect new CSS structure + - Removed legacy styles.css in favor of modular approach + ## ci: - 11/30/2024 - @lumpinif ### Chrome Extension CI Improvements