diff --git a/.github/workflows/agent-deep-modeling.yml b/.github/workflows/agent-deep-modeling.yml deleted file mode 100644 index 1b7e9174d1..0000000000 --- a/.github/workflows/agent-deep-modeling.yml +++ /dev/null @@ -1,124 +0,0 @@ -name: agent-deep-modeling - -on: - pull_request: - paths: - - 'frontend/internal-packages/agent/**' - - 'frontend/internal-packages/artifact/**' - - 'frontend/internal-packages/db/**' - - 'frontend/packages/schema/**' - - 'frontend/internal-packages/pglite-server/**' - - '.github/workflows/agent-deep-modeling.yml' - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - agent-deep-modeling: - runs-on: ubuntu-latest - timeout-minutes: 30 - permissions: - contents: read - pull-requests: write - - steps: - - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - with: - persist-credentials: false - - - uses: ./.github/actions/pnpm-setup - - - name: Start Supabase - run: pnpm --filter @liam-hq/db supabase:start - - - name: Setup environment - run: cp .env.template .env - - - name: Make scripts executable - run: chmod +x ./scripts/extract-supabase-anon-key.sh ./scripts/extract-supabase-service-key.sh - - - name: Extract Supabase keys - run: | - ./scripts/extract-supabase-anon-key.sh - ./scripts/extract-supabase-service-key.sh - - - name: Build schema package - run: pnpm build --filter @dlh/erd-viewer-schema - - - name: Execute deep modeling process - id: deep-modeling - env: - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY }} - LANGSMITH_PROJECT: ${{ secrets.LANGSMITH_PROJECT }} - LANGSMITH_ORGANIZATION_ID: ${{ secrets.LANGSMITH_ORGANIZATION_ID }} - LANGSMITH_PROJECT_ID: ${{ secrets.LANGSMITH_PROJECT_ID }} - NO_COLOR: 1 - shell: bash - run: | - start_time=$(date +%s) - start_time_formatted=$(date -u '+%Y-%m-%d %H:%M:%S UTC') - - echo "## 🤖 Agent Deep Modeling Execution" > execution_log.md - echo "" >> execution_log.md - echo "**Started at:** $start_time_formatted" >> execution_log.md - echo "" >> execution_log.md - echo "
" >> execution_log.md - echo "View Details" >> execution_log.md - echo "" >> execution_log.md - echo "### Command Output" >> execution_log.md - echo "" >> execution_log.md - - set -o pipefail - pnpm --filter @liam-hq/agent execute-deep-modeling 2>&1 | tee -a execution_log.md - exit_code=${PIPESTATUS[0]} - - end_time=$(date +%s) - end_time_formatted=$(date -u '+%Y-%m-%d %H:%M:%S UTC') - duration=$((end_time - start_time)) - - # Convert duration to human readable format - hours=$((duration / 3600)) - minutes=$(((duration % 3600) / 60)) - seconds=$((duration % 60)) - - if [ $hours -gt 0 ]; then - duration_formatted="${hours}h ${minutes}m ${seconds}s" - elif [ $minutes -gt 0 ]; then - duration_formatted="${minutes}m ${seconds}s" - else - duration_formatted="${seconds}s" - fi - - echo "" >> execution_log.md - - if [ $exit_code -eq 0 ]; then - echo "✅ **Status:** Completed successfully" >> execution_log.md - echo "success=true" >> $GITHUB_OUTPUT - else - echo "❌ **Status:** Failed with exit code $exit_code" >> execution_log.md - echo "success=false" >> $GITHUB_OUTPUT - echo "exit_code=$exit_code" >> $GITHUB_OUTPUT - fi - - echo "" >> execution_log.md - echo "**Finished at:** $end_time_formatted" >> execution_log.md - echo "" >> execution_log.md - echo "
" >> execution_log.md - - # Insert processing time after the header using sed - sed -i '2a\\n**Processing time:** '"$duration_formatted"'' execution_log.md - continue-on-error: true - - - name: Post execution log to PR - uses: marocchino/sticky-pull-request-comment@773744901bac0e8cbb5a0dc842800d45e9b2b405 # v2.9.4 - with: - header: agent-deep-modeling-execution - path: execution_log.md - - - name: Check execution result - if: steps.deep-modeling.outputs.success == 'false' - run: | - echo "Agent deep modeling execution failed" - exit 1 diff --git a/.github/workflows/frontend-ci.yml b/.github/workflows/frontend-ci.yml index 3dcd8b37ff..71b0aed9c8 100644 --- a/.github/workflows/frontend-ci.yml +++ b/.github/workflows/frontend-ci.yml @@ -64,23 +64,4 @@ jobs: with: persist-credentials: false - uses: ./.github/actions/pnpm-setup - - run: pnpm --filter @liam-hq/db supabase:start - - run: cp .env.template .env - - name: Make scripts executable - run: chmod +x ./scripts/extract-supabase-anon-key.sh ./scripts/extract-supabase-service-key.sh - - run: ./scripts/extract-supabase-anon-key.sh - - run: ./scripts/extract-supabase-service-key.sh - run: pnpm test - - run: pnpm --filter @liam-hq/db supabase:gen - - name: Check for diff in generated types and schema.sql - env: - # check two files. - GENERATED_FILES: "frontend/internal-packages/db/supabase/database.types.ts frontend/internal-packages/db/supabase/schemas/schema.sql" - run: | - if ! git diff HEAD --ignore-space-at-eol --exit-code ${{ env.GENERATED_FILES }}; then - echo "Generated types and schema.sql differ from committed files." - exit 1 - else - echo "Generated types and schema.sql are up-to-date." - exit 0 - fi diff --git a/frontend/packages/erd-core/src/features/erd/components/ERDContent/components/TableNode/TableDetail/TableDetail.tsx b/frontend/packages/erd-core/src/features/erd/components/ERDContent/components/TableNode/TableDetail/TableDetail.tsx index 3f75e4f401..5c01b69722 100644 --- a/frontend/packages/erd-core/src/features/erd/components/ERDContent/components/TableNode/TableDetail/TableDetail.tsx +++ b/frontend/packages/erd-core/src/features/erd/components/ERDContent/components/TableNode/TableDetail/TableDetail.tsx @@ -1,4 +1,5 @@ import type { Table } from '@dlh/erd-viewer-schema' +import { useStore } from '@xyflow/react' import { type FC, useCallback, useEffect, useRef } from 'react' import { useVersionOrThrow } from '../../../../../../../providers' import { @@ -18,6 +19,8 @@ import { Indexes } from './Indexes' import { RelatedTables } from './RelatedTables' import styles from './TableDetail.module.css' +const MIN_ZOOM_FOR_COLUMN_FOCUS = 1.5 + type Props = { table: Table } @@ -34,9 +37,28 @@ export const TableDetail: FC = ({ table }) => { showMode: 'TABLE_NAME', }) - const { getNodes, getEdges, setNodes, setEdges, fitView } = + const { getNodes, getEdges, setNodes, setEdges, fitView, setCenter } = useCustomReactflow() const { version } = useVersionOrThrow() + const zoomLevel = useStore((store) => store.transform[2]) + + const handleColumnClick = useCallback( + (_columnName: string) => { + const mainPaneNodes = getNodes() + const tableNode = mainPaneNodes.find((node) => node.id === table.name) + + if (tableNode) { + const targetZoom = Math.max(zoomLevel, MIN_ZOOM_FOR_COLUMN_FOCUS) + const nodeX = + tableNode.position.x + (tableNode.measured?.width ?? 0) / 2 + const nodeY = + tableNode.position.y + (tableNode.measured?.height ?? 0) / 2 + + setCenter(nodeX, nodeY, { zoom: targetZoom, duration: 300 }) + } + }, + [getNodes, table.name, zoomLevel, setCenter], + ) const handleOpenMainPane = useCallback(async () => { const visibleNodeIds: string[] = nodes.map((node) => node.id) @@ -96,7 +118,7 @@ export const TableDetail: FC = ({ table }) => {
{table.comment && } - +
diff --git a/frontend/packages/erd-core/src/features/erd/components/ERDRenderer/TableDetailDrawer/TableDetailDrawer.tsx b/frontend/packages/erd-core/src/features/erd/components/ERDRenderer/TableDetailDrawer/TableDetailDrawer.tsx index ce39fbff16..b2cf4480fa 100644 --- a/frontend/packages/erd-core/src/features/erd/components/ERDRenderer/TableDetailDrawer/TableDetailDrawer.tsx +++ b/frontend/packages/erd-core/src/features/erd/components/ERDRenderer/TableDetailDrawer/TableDetailDrawer.tsx @@ -75,12 +75,30 @@ export const TableDetailDrawerRoot: FC = ({ children }) => { } } + // Also stop propagation on click events to prevent ReactFlow from + // triggering fitView when clicking outside the drawer to close it + const handleClickOutsideClick = (event: MouseEvent) => { + if (!(event.target instanceof Element)) { + return + } + + if ( + drawerRef.current && + !drawerRef.current.contains(event.target) && + open + ) { + event.stopPropagation() + } + } + if (open) { document.addEventListener('mousedown', handleClickOutside, true) + document.addEventListener('click', handleClickOutsideClick, true) } return () => { document.removeEventListener('mousedown', handleClickOutside, true) + document.removeEventListener('click', handleClickOutsideClick, true) } }, [open, handleClose]) diff --git a/frontend/packages/erd-core/src/features/erd/hooks/useTableSelection/useTableSelection.ts b/frontend/packages/erd-core/src/features/erd/hooks/useTableSelection/useTableSelection.ts index e64d6c271c..0a9a6a06df 100644 --- a/frontend/packages/erd-core/src/features/erd/hooks/useTableSelection/useTableSelection.ts +++ b/frontend/packages/erd-core/src/features/erd/hooks/useTableSelection/useTableSelection.ts @@ -7,6 +7,7 @@ import { highlightNodesAndEdges } from '../../utils' type SelectTableParams = { tableId: string displayArea: DisplayArea + skipFitView?: boolean } export const useTableSelection = () => { @@ -16,7 +17,11 @@ export const useTableSelection = () => { useCustomReactflow() const selectTable = useCallback( - async ({ tableId, displayArea }: SelectTableParams) => { + async ({ + tableId, + displayArea, + skipFitView = false, + }: SelectTableParams) => { setActiveTableName(tableId) const { nodes, edges } = highlightNodesAndEdges(getNodes(), getEdges(), { @@ -26,7 +31,7 @@ export const useTableSelection = () => { setNodes(nodes) setEdges(edges) - if (displayArea === 'main') { + if (displayArea === 'main' && !skipFitView) { await fitView({ maxZoom: 1, duration: 300, diff --git a/frontend/packages/erd-core/src/stores/userEditing/Provider.tsx b/frontend/packages/erd-core/src/stores/userEditing/Provider.tsx index 1cb6af63e1..6f02566bd9 100644 --- a/frontend/packages/erd-core/src/stores/userEditing/Provider.tsx +++ b/frontend/packages/erd-core/src/stores/userEditing/Provider.tsx @@ -47,7 +47,7 @@ type Props = PropsWithChildren & UserEditingProviderValue export const UserEditingProvider: FC = ({ children, showDiff: initialShowDiff = false, - defaultShowMode = 'TABLE_NAME', + defaultShowMode = 'KEY_ONLY', }) => { const [activeTableName, _setActiveTableName] = useQueryState( 'active',