Skip to content

Commit

Permalink
add enable_clipboard_inspector (default off) to facilitate further te…
Browse files Browse the repository at this point in the history
…sting
  • Loading branch information
tahouse committed Feb 17, 2025
1 parent d229a8a commit 54bebd9
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 19 deletions.
3 changes: 2 additions & 1 deletion examples/example.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ def dialog(default_input: str | PromptReturn | None = None, key="default_dialog_
key="chat_prompt",
placeholder="Hi there! What should we chat about?",
main_bottom=True,
log_level="debug",
log_level="info",
enable_clipboard_inspector=False,
)

if prompt_return:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "streamlit-chat-prompt"
version = "0.3.7"
version = "0.3.8"
description = "A streamlit custom component that allows you to create a chat prompt with paste and image attachment support"
readme = { file = "README.md", content-type = "text/markdown" }
authors = [
Expand Down
3 changes: 3 additions & 0 deletions streamlit_chat_prompt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ def prompt(
max_image_size: int = 5 * 1024 * 1024, # 5MB
disabled: bool = False,
log_level: str = "warn",
enable_clipboard_inspector: bool = False,
) -> Optional[PromptReturn]:
"""Create a chat-like prompt input at the bottom of the page.
Expand All @@ -135,6 +136,7 @@ def prompt(
max_image_size (int, optional): Maximum size of uploaded images in bytes. Defaults to 5MB.
disabled (bool, optional): Whether the prompt input is disabled. Defaults to False.
log_level (str, optional): Logging level for the component. Defaults to "warn".
enable_clipboard_inspector (bool, optional): Whether to enable clipboard inspector. Defaults to False.
Returns:
Optional[PromptReturn]: Returns a PromptReturn object containing the text
Expand Down Expand Up @@ -193,6 +195,7 @@ def prompt(
disabled=disabled,
max_image_size=max_image_size,
debug=log_level,
clipboard_inspector_enabled=enable_clipboard_inspector,
)
logger.debug(f"prompt value: {component_value}")

Expand Down
5 changes: 4 additions & 1 deletion streamlit_chat_prompt/frontend/src/components/ChatInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,11 @@ export class ChatInput extends StreamlitComponentBase<State, Props> {
return item.type;
}));

// Check if clipboard inspector is enabled via props
const clipboardInspectorEnabled = this.props.args?.clipboard_inspector_enabled ?? false;

// If more than one type, show the inspector
if (uniqueTypes.size > 1) {
if (uniqueTypes.size > 1 && clipboardInspectorEnabled) {
e.preventDefault(); // Prevent default paste
const clipboardInspectorData = inspectClipboard(e);
this.isShowingDialog = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,32 +188,28 @@ export const ClipboardInspector: React.FC<ClipboardInspectorProps> = ({
})));

let workingHtml = html;
console.log(`before working html: ${workingHtml}`);

// Create temporary placeholders for code blocks
codeBlocks.forEach((block, index) => {
console.log(`raw block html: ${block.html}`);
console.log(`raw block plain: ${block.plainText}`);
// Only replace full code blocks with placeholders
if (!block.isInline) {
if (block.isStandalone) {
const placeholder = `[CODEBLOCK${index}]`;
workingHtml = workingHtml.replace(block.html, placeholder);
}
// Leave inline code in place to be handled by turndown service
});
console.log(`working html: ${workingHtml}`);

// Step 2: Convert remaining HTML to markdown
const markdown = turndownService.turndown(workingHtml);
Logger.info('component', 'After markdown conversion:', {
Logger.debug('component', 'After markdown conversion:', {
markdown: markdown.substring(0, 200) + '...',
containsPlaceholders: codeBlocks.map((_, index) => ({
placeholder: `CODEBLOCK_PLACEHOLDER_${index}_ENDPLACEHOLDER`,
found: markdown.includes(`CODEBLOCK_PLACEHOLDER_${index}_ENDPLACEHOLDER`)
}))
});

Logger.info('component', 'Intermediate markdown:', {
Logger.debug('component', 'Intermediate markdown:', {
markdown,
codeBlocks: codeBlocks.length
});
Expand All @@ -226,7 +222,6 @@ export const ClipboardInspector: React.FC<ClipboardInspectorProps> = ({

// Clean up the code content while preserving line breaks
let codeContent = block.plainText;
console.log(`code content 1: ${codeContent}`);

// Format as markdown code block, ensuring proper line breaks
const formattedCode = block.isInline
Expand All @@ -240,7 +235,7 @@ export const ClipboardInspector: React.FC<ClipboardInspectorProps> = ({
].join('\n');


Logger.info('component', `Processed code block ${index}:`, {
Logger.debug('component', `Processed code block ${index}:`, {
placeholder,
originalContent: codeContent.substring(0, 100) + '...',
formattedBlock: formattedCode.substring(0, 100) + '...',
Expand All @@ -262,10 +257,11 @@ export const ClipboardInspector: React.FC<ClipboardInspectorProps> = ({
finalMarkdown = finalMarkdown.replace(placeholder, formattedCode);

// Also try with escaped placeholder just in case
// eslint-disable-next-line no-useless-escape
const escapedPlaceholder = placeholder.replace(/[\[\]]/g, '\\$&');
finalMarkdown = finalMarkdown.replace(escapedPlaceholder, formattedCode);

Logger.info('component', `After replacement for block ${index}:`, {
Logger.debug('component', `After replacement for block ${index}:`, {
success: !finalMarkdown.includes(placeholder),
markdownPreview: finalMarkdown.substring(0, 100) + '...'
});
Expand All @@ -275,7 +271,7 @@ export const ClipboardInspector: React.FC<ClipboardInspectorProps> = ({

// After all replacements
const remainingPlaceholders = finalMarkdown.match(/\[CODEBLOCK\d+\]/g);
Logger.info('component', 'Final check:', {
Logger.debug('component', 'Final check:', {
remainingPlaceholders,
finalMarkdownPreview: finalMarkdown.substring(0, 200) + '...',
totalLength: finalMarkdown.length
Expand Down
1 change: 1 addition & 0 deletions streamlit_chat_prompt/frontend/src/components/Props.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ export interface Props {
disabled?: boolean;
key?: string;
debug?: string;
clipboard_inspector_enabled?: boolean;
}
36 changes: 31 additions & 5 deletions streamlit_chat_prompt/frontend/src/utils/htmlProcessing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,39 @@ interface CodeBlock {
plainText: string; // Clean text with preserved whitespace
language?: string;
isInline: boolean;
isStandalone: boolean;
}
function isStandaloneBlock(element: Element): boolean {
// Check if element is direct child of body or another block-level container
const isTopLevel = element.parentElement?.tagName === 'BODY';

// Check if it's the only major content in its container
const siblings = Array.from(element.parentElement?.children || []);
const hasOnlyWhitespaceOrEmptySiblings = siblings.every(sibling =>
sibling === element ||
(sibling.textContent || '').trim() === '' ||
sibling.tagName === 'BR'
);

// Check if surrounded by empty lines in text content
const prevSibling = element.previousSibling;
const nextSibling = element.nextSibling;
const hasSurroundingWhitespace =
(!prevSibling || prevSibling.textContent?.trim() === '') &&
(!nextSibling || nextSibling.textContent?.trim() === '');

return isTopLevel || (hasOnlyWhitespaceOrEmptySiblings && hasSurroundingWhitespace);
}
function isInlineCode(element: Element): boolean {
// Check if code is within paragraph text
const isInParagraph = !!element.closest('p');
// Check if there are any line breaks in the content
const hasLineBreaks = element.textContent?.includes('\n');
// Check if it's a single short segment
const isShortSegment = (element.textContent?.length || 0) < 40;
const standalone = isStandaloneBlock(element);

// If it's standalone, it's not inline regardless of other factors
if (standalone) {
return false;
}

return (isInParagraph || isShortSegment) && !hasLineBreaks;
}
Expand Down Expand Up @@ -61,7 +86,7 @@ export function extractCodeBlocks(html: string): CodeBlock[] {
}

if (isCodeBlock(element)) {
Logger.info("component", "Found code block:", {
Logger.debug("component", "Found code block:", {
element: element.tagName,
classes: element.className,
style: element.getAttribute('style')
Expand Down Expand Up @@ -109,7 +134,8 @@ export function extractCodeBlocks(html: string): CodeBlock[] {
html: element.innerHTML, // Original HTML
plainText: decodedPlainText, // Clean text with preserved whitespace
language: language,
isInline: isInline
isInline: isInline,
isStandalone: isStandaloneBlock(element)
});

// Replace with placeholder (unchanged)
Expand Down

0 comments on commit 54bebd9

Please sign in to comment.