diff --git a/.cursorrules b/.cursorrules index 382fabb9e..28262014c 100644 --- a/.cursorrules +++ b/.cursorrules @@ -55,6 +55,30 @@ This is a React + TypeScript application for building and running Machine Learni - Common spacing/layout patterns → Suggest utility classes or component abstractions - Similar form field styling → Create form field components +### UI Primitives (Prefer Over Raw HTML) + +**Always prefer our UI primitives over raw HTML elements:** + +- **Layout**: Use `BlockStack` and `InlineStack` from `@/components/ui/layout` instead of `
` + - `BlockStack` = vertical flex (`flex-col`) + - `InlineStack` = horizontal flex (`flex-row`) + - Both support `gap`, `align`, `blockAlign` props + - Use `as` prop for semantic elements: ``, `` + +- **Typography**: Use `Text` from `@/components/ui/typography` instead of raw ``, `

`, ``, `

`, `
` + - `` instead of `

` + - `` instead of `
` + - `` instead of `

` + - Supports: `as`, `size`, `weight`, `tone`, `font` props + +- **Buttons**: Use `Button` from `@/components/ui/button` +- **Icons**: Use `Icon` from `@/components/ui/icon` + +**When raw HTML is acceptable:** +- Semantic elements not supported by primitives (e.g., `

`, `
    `, `
      `, ``) +- Complex layouts where primitives don't fit +- Performance-critical sections where abstraction overhead matters + ### State Management - Use Tanstack Query for server state @@ -202,6 +226,7 @@ export const useContext = () => { - Don't create side effects in render functions - Don't use barrel exports - Don't modify componentSpec structure without express permission +- Don't use raw HTML elements when UI primitives exist (use `Text`, `BlockStack`, `InlineStack`, etc.) ## Other rules diff --git a/src/components/Editor/PipelineDetails.tsx b/src/components/Editor/PipelineDetails.tsx index e26529a56..e344af60b 100644 --- a/src/components/Editor/PipelineDetails.tsx +++ b/src/components/Editor/PipelineDetails.tsx @@ -7,6 +7,7 @@ import { CopyText } from "@/components/shared/CopyText/CopyText"; import { Button } from "@/components/ui/button"; import { Icon } from "@/components/ui/icon"; import { BlockStack, InlineStack } from "@/components/ui/layout"; +import { Text } from "@/components/ui/typography"; import useToastNotification from "@/hooks/useToastNotification"; import { useComponentSpec } from "@/providers/ComponentSpecProvider"; import { useContextPanel } from "@/providers/ContextPanelProvider"; @@ -145,85 +146,108 @@ const PipelineDetails = () => { /> - {/* General Metadata */} -
      -
      - {fileMeta.createdBy && ( -
      - Created by:{" "} - {fileMeta.createdBy} -
      - )} -
      -
      - {fileMeta.creationTime && ( -
      - Created at:{" "} - {new Date(fileMeta.creationTime).toLocaleString()} -
      - )} - {fileMeta.modificationTime && ( -
      - Last updated:{" "} - {new Date(fileMeta.modificationTime).toLocaleString()} -
      - )} -
      -
      + {(fileMeta.createdBy || + fileMeta.creationTime || + fileMeta.modificationTime) && ( + + + Pipeline Info + +
      + {fileMeta.createdBy && ( + + + Created by: + +
      {fileMeta.createdBy}
      +
      + )} + {fileMeta.creationTime && ( + + + Created at: + +
      {new Date(fileMeta.creationTime).toLocaleString()}
      +
      + )} + {fileMeta.modificationTime && ( + + + Last updated: + +
      {new Date(fileMeta.modificationTime).toLocaleString()}
      +
      + )} +
      +
      + )} - {/* Description */} {componentSpec.description && ( -
      -

      Description

      -
      + + + Description + + {componentSpec.description} -
      -
      + + )} {/* Component Digest */} {digest && ( -
      -

      Digest

      + + + Digest + -
      + )} {/* Annotations */} {Object.keys(annotations).length > 0 && ( -
      -

      Annotations

      + + + Annotations +
        {Object.entries(annotations).map(([key, value]) => (
      • - {key}:{" "} - {String(value)} + + {key}: + {" "} + + {String(value)} +
      • ))}
      -
      + )} {/* Artifacts (Inputs & Outputs) */} -
      -

      Artifacts

      -
      -
      -

      Inputs

      + + + Artifacts + + + + + Inputs + {componentSpec.inputs && componentSpec.inputs.length > 0 ? (
      {componentSpec.inputs.map((input) => { return (
      @@ -267,14 +291,16 @@ const PipelineDetails = () => { ) : (
      No inputs
      )} -
      -
      -

      Outputs

      + + + + Outputs + {componentSpec.outputs && componentSpec.outputs.length > 0 ? (
      {componentSpec.outputs.map((output) => (
      @@ -309,20 +335,22 @@ const PipelineDetails = () => { ) : (
      No outputs
      )} -
      -
      -
      +
      + + {/* Validations */} -
      -

      Validations

      + + + Validations + -
      + ); }; diff --git a/src/components/PipelineRun/RunDetails.tsx b/src/components/PipelineRun/RunDetails.tsx index c92c5ae8f..292424b6b 100644 --- a/src/components/PipelineRun/RunDetails.tsx +++ b/src/components/PipelineRun/RunDetails.tsx @@ -52,8 +52,10 @@ export const RunDetails = () => { if (error || !details || !state || !componentSpec) { return (
      - -
      Error loading run details.
      + +
      + Error loading run details. +
      ); } @@ -62,7 +64,7 @@ export const RunDetails = () => { return (
      -

      Loading run details...

      +

      Loading run details...

      ); } @@ -106,52 +108,73 @@ export const RunDetails = () => { {metadata && ( -
      -
      + + + Run Info + +
      {metadata.id && ( -
      - Run Id: {metadata.id} -
      + + + Run Id: + +
      + + {metadata.id} + +
      +
      )} {metadata.root_execution_id && ( -
      - Execution Id:{" "} - {metadata.root_execution_id} -
      + + + Execution Id: + +
      + + {metadata.root_execution_id} + +
      +
      )} -
      -
      {metadata.created_by && ( -
      - Created by:{" "} - {metadata.created_by} -
      + + + Created by: + +
      {metadata.created_by}
      +
      )} -
      -
      {metadata.created_at && ( -
      - Created at:{" "} - {new Date(metadata.created_at).toLocaleString()} -
      + + + Created at: + +
      {new Date(metadata.created_at).toLocaleString()}
      +
      )} -
      -
      + + )} {componentSpec.description && ( -
      -

      Description

      -
      + + + Description + + {componentSpec.description} -
      -
      + + )} - - - Status: {runStatus} + + Status + + + + {runStatus} @@ -159,24 +182,34 @@ export const RunDetails = () => { {Object.keys(annotations).length > 0 && ( -
      -

      Annotations

      + + + Annotations +
        {Object.entries(annotations).map(([key, value]) => (
      • - {key}:{" "} - {String(value)} + + {key}: + {" "} + + {String(value)} +
      • ))}
      -
      + )} -
      -

      Artifacts

      -
      -
      -

      Inputs

      + + + Artifacts + + + + + Inputs + {componentSpec.inputs && componentSpec.inputs.length > 0 ? (
      {componentSpec.inputs.map((input) => ( @@ -196,9 +229,11 @@ export const RunDetails = () => { ) : (
      No inputs
      )} -
      -
      -

      Outputs

      + + + + Outputs + {componentSpec.outputs && componentSpec.outputs.length > 0 ? (
      {componentSpec.outputs.map((output) => ( @@ -218,9 +253,9 @@ export const RunDetails = () => { ) : (
      No outputs
      )} -
      -
      -
      + + + ); }; diff --git a/src/components/shared/CopyText/CopyText.tsx b/src/components/shared/CopyText/CopyText.tsx index 74f843e69..405601db0 100644 --- a/src/components/shared/CopyText/CopyText.tsx +++ b/src/components/shared/CopyText/CopyText.tsx @@ -1,4 +1,4 @@ -import { type MouseEvent, useCallback, useState } from "react"; +import { type MouseEvent, useCallback, useEffect, useState } from "react"; import { Button } from "@/components/ui/button"; import { Icon } from "@/components/ui/icon"; @@ -26,6 +26,15 @@ export const CopyText = ({ setIsCopied(true); }, [children]); + useEffect(() => { + if (isCopied) { + const timer = setTimeout(() => { + setIsCopied(false); + }, 1500); + return () => clearTimeout(timer); + } + }, [isCopied]); + const handleButtonClick = useCallback( (e: MouseEvent) => { e.stopPropagation(); @@ -34,10 +43,6 @@ export const CopyText = ({ [handleCopy], ); - const handleAnimationEnd = useCallback(() => { - setIsCopied(false); - }, []); - return (
      @@ -82,29 +86,29 @@ export const CopyText = ({ interface CopyIconProps { isCopied: boolean; alwaysShow: boolean; - onAnimationEnd: () => void; } -// -const CopyIcon = ({ isCopied, alwaysShow, onAnimationEnd }: CopyIconProps) => ( - - {isCopied ? ( - - - - ) : ( - - )} + +const CopyIcon = ({ isCopied, alwaysShow }: CopyIconProps) => ( + + + );