375dd1c3d6
* feat: Add Context Injection Settings modal with terminal preview Adds a new settings modal accessible from the viewer UI header that allows users to configure context injection parameters with a live terminal preview showing how observations will appear. Changes: - New ContextSettingsModal component with auto-saving settings - TerminalPreview component for live context visualization - useContextPreview hook for fetching preview data - Modal positioned to left of color mode button - Settings sync with backend via worker service API 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat: Add demo data and modify contextHook for cm_demo_content project - Introduced DEMO_OBSERVATIONS and DEMO_SUMMARIES for the cm_demo_content project to provide mock data for testing and demonstration purposes. - Updated contextHook to utilize demo data when the project is cm_demo_content, filtering observations based on configured types and concepts. - Adjusted the worker service to use the contextHook with demo data, ensuring ANSI rendering for terminal output. - Enhanced error handling and ensured proper closure of database connections. * feat: add GitHub stars button with dynamic star count - Implemented a new GitHubStarsButton component that fetches and displays the star count for a specified GitHub repository. - Added useGitHubStars hook to handle API requests and state management for star count. - Created formatStarCount utility function to format the star count into compact notation (k/M suffixes). - Styled the GitHub stars button to match existing UI components, including hover and active states. - Updated Header component to include the new GitHubStarsButton, replacing the static GitHub link. - Added responsive styles to hide the GitHub stars button on mobile devices. * feat: add API endpoint to fetch distinct projects and update context settings modal - Implemented a new API endpoint `/api/projects` in `worker-service.ts` to retrieve a list of distinct projects from the observations. - Modified `ContextSettingsModal.tsx` to replace the current project display with a dropdown for selecting projects, utilizing the fetched project list. - Updated `useContextPreview.ts` to fetch projects on mount and manage the selected project state. - Removed the `currentProject` prop from `ContextSettingsModal` and `App` components as it is now managed internally within the modal. * Enhance Context Settings Modal and Terminal Preview - Updated the styling of the Context Settings Modal for a modern clean design, including improved backdrop, header, and body layout. - Introduced responsive design adjustments for smaller screens. - Added custom scrollbar styles for better user experience. - Refactored the TerminalPreview component to utilize `ansi-to-html` for rendering ANSI content, improving text display. - Implemented new font variables for terminal styling across the application. - Enhanced checkbox and input styles in the settings panel for better usability and aesthetics. - Improved the layout and structure of settings groups and chips for a more organized appearance. * Refactor UI components for compact design and enhance MCP toggle functionality - Updated grid layout in viewer.html and viewer-template.html for better space utilization. - Reduced padding and font sizes in settings groups, filter chips, and form controls for a more compact appearance. - Implemented MCP toggle state management in ContextSettingsModal with API integration for status fetching and toggling. - Reorganized settings groups for clarity, renaming and consolidating sections for improved user experience. - Added feedback mechanism for MCP toggle status to inform users of changes and errors. * feat: add collapsible sections, chip groups, form fields with tooltips, and toggle switches in settings modal - Implemented collapsible sections for better organization of settings. - Added chip groups with select all/none functionality for observation types and concepts. - Enhanced form fields with optional tooltips for better user guidance. - Introduced toggle switches for various settings, improving user interaction. - Updated styles for new components to ensure consistency and responsiveness. - Refactored ContextSettingsModal to utilize new components and improve readability. - Improved TerminalPreview component styling for better layout and usability. * Refactor modal header and preview selector styles; enhance terminal preview functionality - Updated modal header padding and added gap for better spacing. - Introduced a new header-controls section to include a project preview selector. - Enhanced the preview selector styles for improved usability and aesthetics. - Adjusted the preview column styles for a cleaner look. - Implemented word wrap toggle functionality in the TerminalPreview component, allowing users to switch between wrapped and scrollable text. - Improved scroll position handling in TerminalPreview to maintain user experience during content updates. * feat: enhance modal settings with new icon links and update header controls - Added new modal icon links for documentation and social media in ContextSettingsModal. - Updated the header to remove sidebar toggle functionality and replaced it with context preview toggle. - Refactored styles for modal icon links to improve UI/UX. - Removed sidebar component from App and adjusted related state management. * chore: remove abandoned cm_demo_content demo data approach The demo data feature was prototyped but didn't work out. Removes: - DEMO_OBSERVATIONS and DEMO_SUMMARIES arrays - Conditional logic that bypassed DB for demo project - Demo mode check in prior message extraction 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
137 lines
4.2 KiB
TypeScript
137 lines
4.2 KiB
TypeScript
import React, { useMemo, useRef, useLayoutEffect, useState } from 'react';
|
|
import AnsiToHtml from 'ansi-to-html';
|
|
|
|
interface TerminalPreviewProps {
|
|
content: string;
|
|
isLoading?: boolean;
|
|
className?: string;
|
|
}
|
|
|
|
const ansiConverter = new AnsiToHtml({
|
|
fg: '#dcd6cc',
|
|
bg: '#252320',
|
|
newline: false,
|
|
escapeXML: true,
|
|
stream: false
|
|
});
|
|
|
|
export function TerminalPreview({ content, isLoading = false, className = '' }: TerminalPreviewProps) {
|
|
const preRef = useRef<HTMLPreElement>(null);
|
|
const scrollTopRef = useRef(0);
|
|
const [wordWrap, setWordWrap] = useState(true);
|
|
|
|
const html = useMemo(() => {
|
|
// Save scroll position before content changes
|
|
if (preRef.current) {
|
|
scrollTopRef.current = preRef.current.scrollTop;
|
|
}
|
|
if (!content) return '';
|
|
return ansiConverter.toHtml(content);
|
|
}, [content]);
|
|
|
|
// Restore scroll position after render
|
|
useLayoutEffect(() => {
|
|
if (preRef.current && scrollTopRef.current > 0) {
|
|
preRef.current.scrollTop = scrollTopRef.current;
|
|
}
|
|
}, [html]);
|
|
|
|
const preStyle: React.CSSProperties = {
|
|
padding: '16px',
|
|
margin: 0,
|
|
fontFamily: 'var(--font-terminal)',
|
|
fontSize: '12px',
|
|
lineHeight: '1.6',
|
|
overflow: 'auto',
|
|
color: 'var(--color-text-primary)',
|
|
backgroundColor: 'var(--color-bg-card)',
|
|
whiteSpace: wordWrap ? 'pre-wrap' : 'pre',
|
|
wordBreak: wordWrap ? 'break-word' : 'normal',
|
|
position: 'absolute',
|
|
inset: 0,
|
|
};
|
|
|
|
return (
|
|
<div
|
|
className={className}
|
|
style={{
|
|
backgroundColor: 'var(--color-bg-card)',
|
|
border: '1px solid var(--color-border-primary)',
|
|
borderRadius: '8px',
|
|
overflow: 'hidden',
|
|
height: '100%',
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
boxShadow: '0 10px 40px rgba(0, 0, 0, 0.4), 0 4px 12px rgba(0, 0, 0, 0.3)'
|
|
}}
|
|
>
|
|
{/* Window chrome */}
|
|
<div
|
|
style={{
|
|
padding: '12px',
|
|
borderBottom: '1px solid var(--color-border-primary)',
|
|
display: 'flex',
|
|
gap: '6px',
|
|
alignItems: 'center',
|
|
backgroundColor: 'var(--color-bg-header)'
|
|
}}
|
|
>
|
|
<div style={{ width: '12px', height: '12px', borderRadius: '50%', backgroundColor: '#ff5f57' }} />
|
|
<div style={{ width: '12px', height: '12px', borderRadius: '50%', backgroundColor: '#ffbd2e' }} />
|
|
<div style={{ width: '12px', height: '12px', borderRadius: '50%', backgroundColor: '#28c840' }} />
|
|
|
|
<button
|
|
onClick={() => setWordWrap(!wordWrap)}
|
|
style={{
|
|
marginLeft: 'auto',
|
|
padding: '4px 8px',
|
|
fontSize: '11px',
|
|
fontWeight: 500,
|
|
color: wordWrap ? 'var(--color-text-secondary)' : 'var(--color-accent-primary)',
|
|
backgroundColor: 'transparent',
|
|
border: '1px solid',
|
|
borderColor: wordWrap ? 'var(--color-border-primary)' : 'var(--color-accent-primary)',
|
|
borderRadius: '4px',
|
|
cursor: 'pointer',
|
|
transition: 'all 0.2s',
|
|
whiteSpace: 'nowrap'
|
|
}}
|
|
onMouseEnter={(e) => {
|
|
e.currentTarget.style.borderColor = 'var(--color-accent-primary)';
|
|
e.currentTarget.style.color = 'var(--color-accent-primary)';
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
e.currentTarget.style.borderColor = wordWrap ? 'var(--color-border-primary)' : 'var(--color-accent-primary)';
|
|
e.currentTarget.style.color = wordWrap ? 'var(--color-text-secondary)' : 'var(--color-accent-primary)';
|
|
}}
|
|
title={wordWrap ? 'Disable word wrap (scroll horizontally)' : 'Enable word wrap'}
|
|
>
|
|
{wordWrap ? '⤢ Wrap' : '⇄ Scroll'}
|
|
</button>
|
|
</div>
|
|
|
|
{/* Content area */}
|
|
{isLoading ? (
|
|
<div
|
|
style={{
|
|
padding: '16px',
|
|
fontFamily: 'var(--font-terminal)',
|
|
fontSize: '12px',
|
|
color: 'var(--color-text-secondary)'
|
|
}}
|
|
>
|
|
Loading preview...
|
|
</div>
|
|
) : (
|
|
<div style={{ position: 'relative', flex: 1, overflow: 'hidden' }}>
|
|
<pre
|
|
ref={preRef}
|
|
style={preStyle}
|
|
dangerouslySetInnerHTML={{ __html: html }}
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|