python module structure changes

This commit is contained in:
2025-05-10 10:12:04 +00:00
parent 8c4442fb00
commit 831c6051ea
21 changed files with 3028 additions and 208 deletions

View File

@@ -7,7 +7,7 @@ from hatchling.builders.hooks.plugin.interface import BuildHookInterface
class MarkdownBuildHook(BuildHookInterface):
def initialize(self, version, build_data):
# 1. Compile the UI exactly once per build
ui_dir = pathlib.Path(__file__).parent / "ui"
ui_dir = pathlib.Path(__file__).parent / "markdown_view_plugin" / "ui"
dist_dir = ui_dir / "dist"
# Clean any existing dist directory to ensure fresh build

View File

@@ -0,0 +1 @@
from markdown_view_plugin.markdown_view_plugin import MarkdownViewPlugin

Binary file not shown.

View File

@@ -0,0 +1,88 @@
import React, { useEffect, useState } from 'react';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import { fetchMarkdownContent } from './api';
// import { SunIcon, MoonIcon } from '@chakra-ui/icons';
import { useColorMode } from '@chakra-ui/system';
// Custom renderer to mimic Airflow core UI style
const markdownComponents = {
h1: (props: any) => <h1 style={{ fontSize: '2rem', fontWeight: 700, margin: '1.5rem 0 1rem 0', color: 'var(--chakra-colors-gray-800, #2D3748)' }} {...props} />,
h2: (props: any) => <h2 style={{ fontSize: '1.5rem', fontWeight: 600, margin: '1.25rem 0 0.75rem 0', color: 'var(--chakra-colors-gray-700, #4A5568)' }} {...props} />,
h3: (props: any) => <h3 style={{ fontSize: '1.2rem', fontWeight: 600, margin: '1rem 0 0.5rem 0', color: 'var(--chakra-colors-gray-600, #718096)' }} {...props} />,
p: (props: any) => <p style={{ margin: '0.5em 0', lineHeight: 1.7, color: 'var(--chakra-colors-gray-800, #2D3748)' }} {...props} />,
code: (props: any) => {
const { inline, className, children, ...rest } = props;
return !inline ? (
<pre style={{ padding: '1em', borderRadius: '6px', background: '#F7FAFC', boxShadow: '0 1px 2px #e2e8f0', overflowX: 'auto', margin: '0.5em 0' }} {...rest}>
<code className={className}>{children}</code>
</pre>
) : (
<code style={{ background: '#EDF2F7', borderRadius: '4px', padding: '0.2em 0.4em', fontSize: '0.95em' }} {...rest}>{children}</code>
);
},
a: (props: any) => <a style={{ color: '#3182CE', textDecoration: 'underline' }} target="_blank" rel="noopener noreferrer" {...props} />,
ul: (props: any) => <ul style={{ margin: '1em 0', paddingLeft: '1.5em', color: 'var(--chakra-colors-gray-800, #2D3748)' }} {...props} />,
ol: (props: any) => <ol style={{ margin: '1em 0', paddingLeft: '1.5em', color: 'var(--chakra-colors-gray-800, #2D3748)' }} {...props} />,
li: (props: any) => <li style={{ marginBottom: '0.25em' }} {...props} />,
table: (props: any) => <table style={{ width: '100%', borderCollapse: 'collapse', margin: '1em 0', fontSize: '1em' }} {...props} />,
thead: (props: any) => <thead style={{ background: '#F7FAFC' }} {...props} />,
tbody: (props: any) => <tbody {...props} />,
tr: (props: any) => <tr style={{ borderBottom: '1px solid #E2E8F0' }} {...props} />,
th: (props: any) => <th style={{ padding: '0.5em', textAlign: 'left', fontWeight: 700, borderBottom: '2px solid #E2E8F0', background: '#F7FAFC' }} {...props} />,
td: (props: any) => <td style={{ padding: '0.5em', borderBottom: '1px solid #E2E8F0' }} {...props} />,
hr: (props: any) => <hr style={{ margin: '2em 0', border: 0, borderTop: '1px solid #E2E8F0' }} {...props} />,
};
const View: React.FC = () => {
const [markdown, setMarkdown] = useState<string>('');
const [error, setError] = useState<string | null>(null);
const { colorMode, toggleColorMode } = useColorMode(); // Hook for theme toggle
useEffect(() => {
const loadContent = async () => {
try {
const content = await fetchMarkdownContent();
setMarkdown(content);
} catch (err) {
setError((err as Error).message);
}
};
loadContent();
}, []);
if (error) {
return (
<div style={{ maxWidth: '1200px', padding: '2em', color: '#E53E3E' }}>Error loading content: {error}</div>
);
}
if (!markdown && !error) {
return (
<div style={{ maxWidth: '1200px', padding: '2em' }}>Loading content...</div>
);
}
return (
<div style={{ maxWidth: '1200px', padding: '1.5em', minHeight: '100vh', display: 'flex', flexDirection: 'column', background: colorMode === 'dark' ? '#1A202C' : '#F7FAFC', color: colorMode === 'dark' ? '#F7FAFC' : '#2D3748' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '2em', flexShrink: 0 }}>
<h1 style={{ fontSize: '2em', margin: 0, fontWeight: 700 }}>Markdown View</h1>
<button
aria-label="Toggle theme"
onClick={toggleColorMode}
style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: '1.5em', color: colorMode === 'dark' ? '#F7FAFC' : '#2D3748' }}
>
{colorMode === 'light' ? '🌙' : '☀️'}
</button>
</div>
<div style={{ padding: '2em', border: '1px solid #E2E8F0', borderRadius: '12px', boxShadow: '0 2px 8px #E2E8F0', overflowY: 'auto', flexGrow: 1, background: colorMode === 'dark' ? '#2D3748' : '#fff' }}>
<ReactMarkdown components={markdownComponents} remarkPlugins={[remarkGfm]}>
{markdown}
</ReactMarkdown>
</div>
</div>
);
};
export default View;

View File

@@ -0,0 +1,11 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App'; // Removed .tsx extension
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>,
);

View File

@@ -0,0 +1,29 @@
// Chakra UI v3 does not export extendTheme. Use a plain object for theme config or remove custom theme for now.
// Basic color mode config
// No config needed for ChakraProvider in v3
// Define some basic colors similar to Airflow's scheme
// This is a simplified version. For full consistency, you'd replicate more from Airflow's core theme.ts
const colors = {
airflow: {
50: '#EBF8FF', // Lightest blue
100: '#BEE3F8',
200: '#90CDF4',
300: '#63B3ED',
400: '#4299E1', // Primary blue
500: '#3182CE',
600: '#2B6CB0',
700: '#2C5282',
800: '#2A4365', // Darkest blue
900: '#1A365D',
},
success: {
500: '#38A169', // Green
},
error: {
500: '#E53E3E', // Red
},
};
// No theme export needed for ChakraProvider in v3

View File

@@ -1,6 +1,6 @@
[project]
name = "airflow-markdown-view-plugin"
version = "0.1.0"
name = "markdown-view-plugin"
version = "0.1.1"
description = "Airflow UI plugin to render Markdown content using FastAPI and React."
readme = "README.md"
requires-python = ">=3.9"

View File

@@ -1,120 +0,0 @@
import React, { useEffect, useState } from 'react';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import { fetchMarkdownContent } from './api';
import {
Box,
Heading,
Text,
Code,
Link,
ListItem,
OrderedList,
UnorderedList,
Table,
Thead,
Tbody,
Tr,
Th,
Td,
Divider,
Container, // Added for layout
Flex, // Added for layout
IconButton,// Added for theme toggle
useColorMode // Hook for theme toggle
} from '@chakra-ui/react';
import { SunIcon, MoonIcon } from '@chakra-ui/icons'; // Icons for theme toggle
// Attempt to mimic Airflow's Chakra UI components styling
const chakraComponents = {
h1: (props: any) => <Heading as="h1" size="xl" my={4} {...props} />,
h2: (props: any) => <Heading as="h2" size="lg" my={3} {...props} />,
h3: (props: any) => <Heading as="h3" size="md" my={2} {...props} />,
p: (props: any) => <Text my={2} {...props} />,
code: (props: any) => {
const { inline, className, children, ...rest } = props;
return !inline ? (
<Box
as="pre"
p={4}
rounded="md"
bg="chakra-body-bg"
boxShadow="sm"
overflowX="auto"
my={2}
>
<Code className={className} bg="transparent" {...rest}>
{children}
</Code>
</Box>
) : (
<Code colorScheme="purple" fontSize="0.9em" {...rest}>{children}</Code>
);
},
a: (props: any) => <Link color="teal.500" isExternal {...props} />,
ul: (props: any) => <UnorderedList stylePosition="inside" my={2} {...props} />,
ol: (props: any) => <OrderedList stylePosition="inside" my={2} {...props} />,
li: (props: any) => <ListItem {...props} />,
table: (props: any) => <Table variant="simple" my={4} {...props} />,
thead: (props: any) => <Thead {...props} />,
tbody: (props: any) => <Tbody {...props} />,
tr: (props: any) => <Tr {...props} />,
th: (props: any) => <Th {...props} />,
td: (props: any) => <Td {...props} />,
hr: (props: any) => <Divider my={4} {...props} />,
};
const View: React.FC = () => {
const [markdown, setMarkdown] = useState<string>('');
const [error, setError] = useState<string | null>(null);
const { colorMode, toggleColorMode } = useColorMode(); // Hook for theme toggle
useEffect(() => {
const loadContent = async () => {
try {
const content = await fetchMarkdownContent();
setMarkdown(content);
} catch (err) {
setError((err as Error).message);
}
};
loadContent();
}, []);
if (error) {
return (
<Container maxW="container.xl" py={10}>
<Box color="red.500">Error loading content: {error}</Box>
</Container>
);
}
if (!markdown && !error) { // Show loading only if no error
return (
<Container maxW="container.xl" py={10}>
<Box>Loading content...</Box>
</Container>
);
}
return (
<Container maxW="container.xl" py={6} height="100%" display="flex" flexDirection="column">
<Flex justifyContent="space-between" alignItems="center" mb={6} flexShrink={0}>
<Heading as="h1" size="lg">Markdown View</Heading>
<IconButton
aria-label="Toggle theme"
icon={colorMode === 'light' ? <MoonIcon /> : <SunIcon />}
onClick={toggleColorMode}
/>
</Flex>
<Box p={5} borderWidth="1px" borderRadius="lg" boxShadow="md" overflowY="auto" flexGrow={1}>
<ReactMarkdown components={chakraComponents} remarkPlugins={[remarkGfm]}>
{markdown}
</ReactMarkdown>
</Box>
</Container>
);
};
export default View;

View File

@@ -1,14 +0,0 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App'; // Removed .tsx extension
import { ChakraProvider, ColorModeScript } from '@chakra-ui/react';
import theme from './theme'; // Removed .ts extension
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<ChakraProvider theme={theme}> {/* Apply the custom theme */}
<ColorModeScript initialColorMode={theme.config.initialColorMode} />
<App />
</ChakraProvider>
</React.StrictMode>,
);

View File

@@ -1,71 +0,0 @@
import { extendTheme, type ThemeConfig } from '@chakra-ui/react';
// Basic color mode config
const config: ThemeConfig = {
initialColorMode: 'system',
useSystemColorMode: true,
};
// Define some basic colors similar to Airflow's scheme
// This is a simplified version. For full consistency, you'd replicate more from Airflow's core theme.ts
const colors = {
airflow: {
50: '#EBF8FF', // Lightest blue
100: '#BEE3F8',
200: '#90CDF4',
300: '#63B3ED',
400: '#4299E1', // Primary blue
500: '#3182CE',
600: '#2B6CB0',
700: '#2C5282',
800: '#2A4365', // Darkest blue
900: '#1A365D',
},
success: {
500: '#38A169', // Green
},
error: {
500: '#E53E3E', // Red
},
};
const theme = extendTheme({
config,
colors,
styles: {
global: (props: any) => ({
body: {
bg: props.colorMode === 'dark' ? 'gray.800' : 'gray.50',
color: props.colorMode === 'dark' ? 'whiteAlpha.900' : 'gray.800',
height: '100%', // Ensure body takes full height
margin: 0,
},
html: {
height: '100%',
},
'#root': { // Ensure root div also takes full height
height: '100%',
}
}),
},
components: {
Button: {
baseStyle: {
// Example: fontWeight: 'bold',
},
variants: {
solid: (props: any) => ({
bg: props.colorMode === 'dark' ? 'airflow.300' : 'airflow.500',
color: props.colorMode === 'dark' ? 'gray.800' : 'white',
_hover: {
bg: props.colorMode === 'dark' ? 'airflow.400' : 'airflow.600',
}
}),
},
},
// You can add more component-specific style overrides here if needed
// For example, for Markdown components if View.tsx styling needs to be theme-aware
},
});
export default theme;

2896
markdown_view_plugin/uv.lock generated Normal file

File diff suppressed because it is too large Load Diff