Fix plotly resize

This commit is contained in:
James Ketr 2025-05-16 14:25:44 -07:00
parent a8b355188c
commit 5cdab85a7a
47 changed files with 158 additions and 88 deletions

View File

@ -8,5 +8,16 @@ module.exports = {
// key: '/path/to/key.pem',
// }
}
}
};
},
webpack: {
configure: (webpackConfig) => {
// Add .ts and .tsx to resolve.extensions
webpackConfig.resolve.extensions = [
...webpackConfig.resolve.extensions,
'.ts',
'.tsx',
];
return webpackConfig;
},
},
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View File

@ -1,8 +1,8 @@
import React, { useRef, useCallback } from 'react';
import { BrowserRouter as Router, Routes, Route, useLocation } from "react-router-dom";
import { SessionWrapper } from "./SessionWrapper";
import { Main } from "./Main";
import { Snack, SeverityType } from './Snack';
import { SessionWrapper } from "./App/SessionWrapper";
import { Main } from "./App/Main";
import { Snack, SeverityType } from './Components/Snack';
export function PathRouter({ setSnack }: { setSnack: any }) {
const location = useLocation();

View File

@ -16,21 +16,20 @@ import Box from '@mui/material/Box';
import CssBaseline from '@mui/material/CssBaseline';
import MenuIcon from '@mui/icons-material/Menu';
import { ConversationHandle } from './Conversation';
import { Query } from './ChatQuery';
import { Scrollable } from './Scrollable';
import { BackstoryPage, BackstoryTabProps } from './BackstoryTab';
import { ConversationHandle } from '../Components/Conversation';
import { Query } from '../Components/ChatQuery';
import { Scrollable } from '../Components/Scrollable';
import { BackstoryPage, BackstoryTabProps } from '../Components/BackstoryTab';
import { HomePage } from './HomePage';
import { LoadingPage } from './LoadingPage';
import { ResumeBuilderPage } from './ResumeBuilderPage';
import { VectorVisualizerPage } from './VectorVisualizer';
import { AboutPage } from './AboutPage';
import { ControlsPage } from './ControlsPage';
import { SetSnackType } from './Snack';
import { HomePage } from '../Pages/HomePage';
import { LoadingPage } from '../Pages/LoadingPage';
import { ResumeBuilderPage } from '../Pages/ResumeBuilderPage';
import { VectorVisualizerPage } from '../Pages/VectorVisualizerPage';
import { AboutPage } from '../Pages/AboutPage';
import { ControlsPage } from '../Pages/ControlsPage';
import { SetSnackType } from '../Components/Snack';
import './Main.css';
import './Conversation.css';
import '@fontsource/roboto/300.css';
import '@fontsource/roboto/400.css';

View File

@ -1,7 +1,7 @@
import { useEffect, useState, useRef } from "react";
import { useNavigate, useLocation } from "react-router-dom";
import { connectionBase } from './Global';
import { SetSnackType } from './Snack';
import { connectionBase } from '../Global';
import { SetSnackType } from '../Components/Snack';
const getSessionId = async () => {
const response = await fetch(connectionBase + `/api/context`, {

View File

@ -17,7 +17,7 @@ import { Query } from './ChatQuery';
import './Conversation.css';
import { BackstoryTextField, BackstoryTextFieldRef } from './BackstoryTextField';
import { BackstoryElementProps } from './BackstoryTab';
import { connectionBase } from './Global';
import { connectionBase } from '../Global';
const loadingMessage: BackstoryMessage = { "role": "status", "content": "Establishing connection with server..." };

View File

@ -17,7 +17,7 @@ import TableContainer from '@mui/material/TableContainer';
import TableRow from '@mui/material/TableRow';
import { Scrollable } from './Scrollable';
import { connectionBase } from './Global';
import { connectionBase } from '../Global';
import './VectorVisualizer.css';
import { BackstoryPageProps } from './BackstoryTab';
@ -66,14 +66,10 @@ interface PlotData {
const config: Partial<Plotly.Config> = {
responsive: true,
autosizable: true,
// displayModeBar: false,
displaylogo: false,
showSendToCloud: false,
staticPlot: false,
//fillFrame: true,
/** if we DO autosize, set the frame margins in percents of plot size */
frameMargins: 0,
/** mousewheel or two-finger scroll zooms the plot */
scrollZoom: true,
doubleClick: "reset+autosize",
// | "lasso2d"
@ -119,7 +115,7 @@ const config: Partial<Plotly.Config> = {
};
const layout: Partial<Plotly.Layout> = {
autosize: true,
autosize: false,
paper_bgcolor: '#FFFFFF', // white
plot_bgcolor: '#FFFFFF', // white plot background
font: {
@ -200,6 +196,22 @@ const VectorVisualizer: React.FC<VectorVisualizerProps> = (props: VectorVisualiz
const [node, setNode] = useState<Node | null>(null);
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
const [plotDimensions, setPlotDimensions] = useState({ width: 0, height: 0 });
/* Force resize of Plotly as it tends to not be the correct size if it is initially rendered
* off screen (eg., the VectorVisualizer is not on the tab the app loads to) */
useEffect(() => {
requestAnimationFrame(() => {
const plotContainer = document.querySelector('.plot-container') as HTMLElement;
const svgContainer = document?.querySelector('.svg-container') as HTMLElement;
if ( plotContainer && svgContainer) {
const plotContainerRect = plotContainer.getBoundingClientRect();
svgContainer.style.width = `${plotContainerRect.width}px`;
svgContainer.style.height = `${plotContainerRect.height}px`;
setPlotDimensions({ width: plotContainerRect.width, height: plotContainerRect.height});
}
});
});
// Get the collection to visualize
useEffect(() => {
@ -509,7 +521,7 @@ The scatter graph shows the query in N-dimensional space, mapped to ${view2D ? '
height: "100%",
overflow: "hidden",
}}
layout={layout}
layout={{...layout, width: plotDimensions.width, height: plotDimensions.height }}
/>
</Paper>
@ -611,21 +623,8 @@ The scatter graph shows the query in N-dimensional space, mapped to ${view2D ? '
);
};
const VectorVisualizerPage: React.FC<VectorVisualizerProps> = (props: VectorVisualizerProps) => {
return <Scrollable
autoscroll={false}
sx={{
maxWidth: "1024px",
height: "calc(100vh - 72px)",
}}
>
<VectorVisualizer {...props} />
</Scrollable>;
};
export type { VectorVisualizerProps };
export {
VectorVisualizer,
VectorVisualizerPage
};

View File

@ -1,8 +1,8 @@
import React, { useState, useEffect } from 'react';
import { Scrollable } from './Scrollable';
import { BackstoryPageProps } from './BackstoryTab';
import { Document } from './Document';
import { Scrollable } from '../Components/Scrollable';
import { BackstoryPageProps } from '../Components/BackstoryTab';
import { Document } from '../Components/Document';
const AboutPage = (props: BackstoryPageProps) => {
const { sessionId, submitQuery, setSnack, route, setRoute } = props;

View File

@ -14,9 +14,8 @@ import Typography from '@mui/material/Typography';
// import ResetIcon from '@mui/icons-material/History';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import { connectionBase } from './Global';
import { BackstoryPageProps } from './BackstoryTab';
import { restyle } from 'plotly.js';
import { connectionBase } from '../Global';
import { BackstoryPageProps } from '../Components/BackstoryTab';
interface ServerTunables {
system_prompt: string,

View File

@ -4,10 +4,10 @@ import Box from '@mui/material/Box';
import { useTheme } from '@mui/material/styles';
import MuiMarkdown from 'mui-markdown';
import { BackstoryPageProps } from './BackstoryTab';
import { Conversation, ConversationHandle } from './Conversation';
import { ChatQuery } from './ChatQuery';
import { MessageList } from './Message';
import { BackstoryPageProps } from '../Components//BackstoryTab';
import { Conversation, ConversationHandle } from '../Components/Conversation';
import { ChatQuery } from '../Components/ChatQuery';
import { MessageList } from '../Components/Message';
const HomePage = forwardRef<ConversationHandle, BackstoryPageProps>((props: BackstoryPageProps, ref) => {
const { sessionId, setSnack, submitQuery } = props;

View File

@ -1,6 +1,6 @@
import Box from '@mui/material/Box';
import { BackstoryPageProps } from './BackstoryTab';
import { BackstoryMessage, Message } from './Message';
import { BackstoryPageProps } from '../Components/BackstoryTab';
import { BackstoryMessage, Message } from '../Components/Message';
const LoadingPage = (props: BackstoryPageProps) => {
const backstoryPreamble: BackstoryMessage = {

View File

@ -6,10 +6,10 @@ import {
} from '@mui/material';
import { SxProps } from '@mui/material';
import { ChatQuery, Query } from './ChatQuery';
import { MessageList, BackstoryMessage } from './Message';
import { Conversation } from './Conversation';
import { BackstoryPageProps } from './BackstoryTab';
import { ChatQuery, Query } from '../Components/ChatQuery';
import { MessageList, BackstoryMessage } from '../Components/Message';
import { Conversation } from '../Components/Conversation';
import { BackstoryPageProps } from '../Components/BackstoryTab';
import './ResumeBuilderPage.css';

View File

@ -0,0 +1,28 @@
import React, { useEffect, useState, useRef } from 'react';
import { Scrollable } from '../Components/Scrollable';
import { VectorVisualizer } from '../Components/VectorVisualizer';
import { BackstoryPageProps } from '../Components/BackstoryTab';
interface VectorVisualizerProps extends BackstoryPageProps {
inline?: boolean;
rag?: any;
};
const VectorVisualizerPage: React.FC<VectorVisualizerProps> = (props: VectorVisualizerProps) => {
return <Scrollable
autoscroll={false}
sx={{
maxWidth: "1024px",
height: "calc(100vh - 72px)",
}}
>
<VectorVisualizer {...props} />
</Scrollable>;
};
export type { VectorVisualizerProps };
export {
VectorVisualizerPage
};

View File

@ -1,2 +1 @@
declare module 'tsne-js';
declare module 'react-plotly.js';

View File

@ -1,10 +1,9 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import { ThemeProvider } from '@mui/material/styles';
import { backstoryTheme } from './BackstoryTheme'; // Adjust path as needed
import { backstoryTheme } from './BackstoryTheme';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
@ -16,8 +15,3 @@ root.render(
</ThemeProvider>
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

View File

@ -1,15 +0,0 @@
import { ReportHandler } from 'web-vitals';
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;

View File

@ -1,5 +0,0 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';

View File

@ -18,9 +18,10 @@
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
"jsx": "react-jsx",
"baseUrl": "src",
},
"include": [
"src"
]
"include": [
"src/**/*"
]
}

View File

@ -0,0 +1,60 @@
from pydantic import BaseModel, Field # type: ignore
import json
from typing import Any, List, Set
def check_serializable(obj: Any, path: str = "", errors: List[str] = [], visited: Set[int] = set()) -> List[str]:
"""
Recursively check all fields in an object for non-JSON-serializable types, avoiding infinite recursion.
Skips fields in Pydantic models marked with Field(..., exclude=True).
Args:
obj: The object to inspect (Pydantic model, dict, list, or other).
path: The current field path (e.g., 'field1.nested_field').
errors: List to collect error messages.
visited: Set of object IDs to track visited objects and prevent infinite recursion.
Returns:
List of error messages for non-serializable fields.
"""
# Check for circular reference by object ID
obj_id = id(obj)
if obj_id in visited:
errors.append(f"Field '{path}' contains a circular reference, skipping further inspection")
return errors
# Add current object to visited set
visited.add(obj_id)
try:
# Handle Pydantic models
if isinstance(obj, BaseModel):
for field_name, field_info in obj.model_fields.items():
# Skip fields marked with exclude=True
if field_info.exclude:
continue
value = getattr(obj, field_name)
new_path = f"{path}.{field_name}" if path else field_name
check_serializable(value, new_path, errors, visited)
# Handle dictionaries
elif isinstance(obj, dict):
for key, value in obj.items():
new_path = f"{path}[{key}]" if path else str(key)
check_serializable(value, new_path, errors, visited)
# Handle lists, tuples, or other iterables
elif isinstance(obj, (list, tuple)):
for i, value in enumerate(obj):
new_path = f"{path}[{i}]" if path else str(i)
check_serializable(value, new_path, errors, visited)
# Handle other types (check for JSON serializability)
else:
try:
json.dumps(obj)
except (TypeError, OverflowError, ValueError) as e:
errors.append(f"Field '{path}' contains non-serializable type: {type(obj)} ({str(e)})")
finally:
# Remove the current object from visited to allow processing in other branches
visited.discard(obj_id)
return errors