refactor: migrate remaining components to new design system
Migrates 38 files: tree-editor forms, session modals, step library, common components, library views, tree preview, and misc UI to use design tokens (bg-card, border-border, text-foreground, bg-accent, bg-gradient-brand) replacing old monochrome patterns. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -60,23 +60,23 @@ export function Modal({ isOpen, onClose, title, children, footer, size = 'md' }:
|
||||
{/* Modal Content */}
|
||||
<div
|
||||
className={cn(
|
||||
'relative flex w-full flex-col border border-white/[0.06] bg-[#0a0a0a] shadow-lg',
|
||||
'relative flex w-full flex-col border border-border bg-card shadow-lg',
|
||||
'max-h-[100vh] rounded-t-2xl sm:max-h-[85vh] sm:rounded-2xl',
|
||||
'animate-scale-in',
|
||||
sizeClasses[size]
|
||||
)}
|
||||
>
|
||||
{/* Header - Fixed at top */}
|
||||
<div className="flex flex-shrink-0 items-center justify-between border-b border-white/[0.06] px-4 py-3 sm:px-6 sm:py-4">
|
||||
<h2 id="modal-title" className="text-lg font-semibold text-white">
|
||||
<div className="flex flex-shrink-0 items-center justify-between border-b border-border px-4 py-3 sm:px-6 sm:py-4">
|
||||
<h2 id="modal-title" className="text-lg font-semibold text-foreground">
|
||||
{title}
|
||||
</h2>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className={cn(
|
||||
'rounded-md p-1.5 text-white/40 transition-colors sm:p-1',
|
||||
'hover:bg-white/10 hover:text-white',
|
||||
'focus:outline-none focus:ring-2 focus:ring-white/20'
|
||||
'rounded-md p-1.5 text-muted-foreground transition-colors sm:p-1',
|
||||
'hover:bg-accent hover:text-foreground',
|
||||
'focus:outline-none focus:ring-2 focus:ring-primary/20'
|
||||
)}
|
||||
aria-label="Close modal"
|
||||
>
|
||||
@@ -91,7 +91,7 @@ export function Modal({ isOpen, onClose, title, children, footer, size = 'md' }:
|
||||
|
||||
{/* Footer - Fixed at bottom */}
|
||||
{footer && (
|
||||
<div className="flex-shrink-0 border-t border-white/[0.06] px-4 py-3 sm:px-6 sm:py-4">
|
||||
<div className="flex-shrink-0 border-t border-border px-4 py-3 sm:px-6 sm:py-4">
|
||||
{footer}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -2,8 +2,8 @@ export function PageLoader() {
|
||||
return (
|
||||
<div className="flex h-screen items-center justify-center bg-black">
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<div className="h-12 w-12 animate-spin rounded-full border-4 border-white/20 border-t-white" />
|
||||
<p className="text-sm text-white/40">Loading...</p>
|
||||
<div className="h-12 w-12 animate-spin rounded-full border-4 border-border border-t-foreground" />
|
||||
<p className="text-sm text-muted-foreground">Loading...</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -19,7 +19,7 @@ export function PasswordInput({ className, ...props }: PasswordInputProps) {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setVisible((v) => !v)}
|
||||
className="absolute right-2 top-1/2 -translate-y-1/2 rounded p-1 text-white/40 hover:bg-white/10 hover:text-white"
|
||||
className="absolute right-2 top-1/2 -translate-y-1/2 rounded p-1 text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||
tabIndex={-1}
|
||||
title={visible ? 'Hide password' : 'Show password'}
|
||||
>
|
||||
|
||||
@@ -19,17 +19,17 @@ export function RouteError() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col items-center justify-center bg-black p-8">
|
||||
<div className="max-w-md text-center">
|
||||
<h1 className="mb-2 text-4xl font-bold text-white">Oops!</h1>
|
||||
<h1 className="mb-2 text-4xl font-bold text-foreground">Oops!</h1>
|
||||
<h2 className="mb-2 text-xl font-semibold text-red-400">{errorMessage}</h2>
|
||||
{errorDetails && (
|
||||
<p className="mb-4 text-white/70">{errorDetails}</p>
|
||||
<p className="mb-4 text-muted-foreground">{errorDetails}</p>
|
||||
)}
|
||||
<div className="flex justify-center gap-4">
|
||||
<button
|
||||
onClick={() => navigate(-1)}
|
||||
className={cn(
|
||||
'rounded-xl border border-white/10 px-4 py-2 text-sm font-medium text-white/60',
|
||||
'hover:bg-white/10 hover:text-white'
|
||||
'rounded-xl border border-border px-4 py-2 text-sm font-medium text-muted-foreground',
|
||||
'hover:bg-accent hover:text-foreground'
|
||||
)}
|
||||
>
|
||||
Go Back
|
||||
@@ -37,8 +37,8 @@ export function RouteError() {
|
||||
<button
|
||||
onClick={() => navigate('/trees')}
|
||||
className={cn(
|
||||
'rounded-xl bg-white px-4 py-2 text-sm font-medium text-black',
|
||||
'hover:bg-white/90'
|
||||
'rounded-xl bg-gradient-brand px-4 py-2 text-sm font-medium text-white shadow-lg shadow-primary/20',
|
||||
'hover:opacity-90'
|
||||
)}
|
||||
>
|
||||
Go Home
|
||||
|
||||
@@ -48,14 +48,14 @@ export function StarRating({
|
||||
sizeClasses[size],
|
||||
star <= value
|
||||
? 'fill-yellow-400 text-yellow-400'
|
||||
: 'fill-none text-white/30',
|
||||
: 'fill-none text-muted-foreground',
|
||||
!readonly && 'hover:text-yellow-300'
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
))}
|
||||
{showCount && (
|
||||
<span className="ml-1 text-sm text-white/40">
|
||||
<span className="ml-1 text-sm text-muted-foreground">
|
||||
({value}/5)
|
||||
</span>
|
||||
)}
|
||||
|
||||
@@ -37,8 +37,8 @@ export function TagBadges({
|
||||
'rounded-full transition-colors',
|
||||
size === 'sm' ? 'px-2 py-0.5 text-xs' : 'px-2.5 py-1 text-sm',
|
||||
variant === 'default'
|
||||
? 'bg-white/10 text-white/70 hover:bg-white/15'
|
||||
: 'bg-white/5 text-white/40 hover:bg-white/10',
|
||||
? 'bg-accent text-muted-foreground hover:bg-accent'
|
||||
: 'bg-accent/50 text-muted-foreground hover:bg-accent',
|
||||
!onTagClick && 'cursor-default'
|
||||
)}
|
||||
>
|
||||
@@ -50,7 +50,7 @@ export function TagBadges({
|
||||
className={cn(
|
||||
'rounded-full',
|
||||
size === 'sm' ? 'px-2 py-0.5 text-xs' : 'px-2.5 py-1 text-sm',
|
||||
'bg-white/5 text-white/40'
|
||||
'bg-accent/50 text-muted-foreground'
|
||||
)}
|
||||
title={tags.slice(maxVisible).join(', ')}
|
||||
>
|
||||
|
||||
@@ -123,10 +123,10 @@ export function TagInput({
|
||||
<div
|
||||
className={cn(
|
||||
'flex flex-wrap gap-1.5 rounded-xl border px-2 py-1.5',
|
||||
'bg-black/50 text-white',
|
||||
'focus-within:border-white/30 focus-within:ring-1 focus-within:ring-white/20',
|
||||
'bg-card text-foreground',
|
||||
'focus-within:border-primary focus-within:ring-1 focus-within:ring-primary/20',
|
||||
disabled ? 'cursor-not-allowed opacity-50' : '',
|
||||
'border-white/10'
|
||||
'border-border'
|
||||
)}
|
||||
onClick={() => inputRef.current?.focus()}
|
||||
>
|
||||
@@ -136,7 +136,7 @@ export function TagInput({
|
||||
key={tag}
|
||||
className={cn(
|
||||
'inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs',
|
||||
'bg-white/10 text-white/70'
|
||||
'bg-accent text-muted-foreground'
|
||||
)}
|
||||
>
|
||||
{tag}
|
||||
@@ -147,7 +147,7 @@ export function TagInput({
|
||||
e.stopPropagation()
|
||||
removeTag(tag)
|
||||
}}
|
||||
className="rounded-full p-0.5 hover:bg-white/20"
|
||||
className="rounded-full p-0.5 hover:bg-accent"
|
||||
>
|
||||
<X className="h-3 w-3" />
|
||||
</button>
|
||||
@@ -184,8 +184,8 @@ export function TagInput({
|
||||
placeholder={tags.length === 0 ? placeholder : ''}
|
||||
disabled={disabled}
|
||||
className={cn(
|
||||
'flex-1 min-w-[80px] border-0 bg-transparent px-1 py-0.5 text-sm text-white',
|
||||
'placeholder:text-white/40',
|
||||
'flex-1 min-w-[80px] border-0 bg-transparent px-1 py-0.5 text-sm text-foreground',
|
||||
'placeholder:text-muted-foreground',
|
||||
'focus:outline-none focus:ring-0'
|
||||
)}
|
||||
/>
|
||||
@@ -196,8 +196,8 @@ export function TagInput({
|
||||
{showSuggestions && suggestions.length > 0 && (
|
||||
<div
|
||||
className={cn(
|
||||
'absolute z-10 mt-1 w-full rounded-xl border border-white/[0.06]',
|
||||
'bg-[#0a0a0a] shadow-lg'
|
||||
'absolute z-10 mt-1 w-full rounded-xl border border-border',
|
||||
'bg-card shadow-lg'
|
||||
)}
|
||||
>
|
||||
{suggestions.map((suggestion, index) => (
|
||||
@@ -206,13 +206,13 @@ export function TagInput({
|
||||
type="button"
|
||||
onClick={() => addTag(suggestion.name)}
|
||||
className={cn(
|
||||
'flex w-full items-center justify-between px-3 py-2 text-sm text-white/70',
|
||||
'hover:bg-white/10',
|
||||
index === selectedIndex && 'bg-white/10'
|
||||
'flex w-full items-center justify-between px-3 py-2 text-sm text-muted-foreground',
|
||||
'hover:bg-accent',
|
||||
index === selectedIndex && 'bg-accent'
|
||||
)}
|
||||
>
|
||||
<span>{suggestion.name}</span>
|
||||
<span className="text-xs text-white/40">
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{suggestion.usage_count} trees
|
||||
</span>
|
||||
</button>
|
||||
@@ -225,8 +225,8 @@ export function TagInput({
|
||||
type="button"
|
||||
onClick={() => addTag(inputValue)}
|
||||
className={cn(
|
||||
'flex w-full items-center gap-2 border-t border-white/[0.06] px-3 py-2 text-sm',
|
||||
'hover:bg-white/10 text-white'
|
||||
'flex w-full items-center gap-2 border-t border-border px-3 py-2 text-sm',
|
||||
'hover:bg-accent text-foreground'
|
||||
)}
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
@@ -237,7 +237,7 @@ export function TagInput({
|
||||
)}
|
||||
|
||||
{/* Helper text */}
|
||||
<p className="mt-1 text-xs text-white/40">
|
||||
<p className="mt-1 text-xs text-muted-foreground">
|
||||
{tags.length}/{maxTags} tags. Press Enter, Tab, comma, or semicolon to add.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -15,7 +15,7 @@ export function ProtectedRoute({ requiredRole, children }: ProtectedRouteProps)
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex h-screen items-center justify-center">
|
||||
<div className="h-8 w-8 animate-spin rounded-full border-4 border-white/20 border-t-white" />
|
||||
<div className="h-8 w-8 animate-spin rounded-full border-4 border-border border-t-foreground" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -89,8 +89,8 @@ export function AddToFolderMenu({ treeId, onFolderCreated }: AddToFolderMenuProp
|
||||
setIsOpen(!isOpen)
|
||||
}}
|
||||
className={cn(
|
||||
'rounded-md border border-white/10 p-1.5 text-white/60',
|
||||
'hover:bg-white/10 hover:text-white'
|
||||
'rounded-md border border-border p-1.5 text-muted-foreground',
|
||||
'hover:bg-accent hover:text-foreground'
|
||||
)}
|
||||
title="Add to folder"
|
||||
aria-label="Add to folder"
|
||||
@@ -101,14 +101,14 @@ export function AddToFolderMenu({ treeId, onFolderCreated }: AddToFolderMenuProp
|
||||
{isOpen && (
|
||||
<div
|
||||
className={cn(
|
||||
'absolute right-0 top-full z-20 mt-1 w-48 rounded-md border border-white/10',
|
||||
'bg-black/90 backdrop-blur-sm py-1 shadow-lg'
|
||||
'absolute right-0 top-full z-20 mt-1 w-48 rounded-md border border-border',
|
||||
'bg-card backdrop-blur-sm py-1 shadow-lg'
|
||||
)}
|
||||
>
|
||||
{isLoading ? (
|
||||
<div className="px-3 py-2 text-sm text-white/40">Loading...</div>
|
||||
<div className="px-3 py-2 text-sm text-muted-foreground">Loading...</div>
|
||||
) : folders.length === 0 ? (
|
||||
<div className="px-3 py-2 text-sm text-white/40">No folders yet</div>
|
||||
<div className="px-3 py-2 text-sm text-muted-foreground">No folders yet</div>
|
||||
) : (
|
||||
folders.map((folder) => (
|
||||
<button
|
||||
@@ -117,7 +117,7 @@ export function AddToFolderMenu({ treeId, onFolderCreated }: AddToFolderMenuProp
|
||||
e.stopPropagation()
|
||||
toggleFolder(folder.id)
|
||||
}}
|
||||
className="flex w-full items-center gap-2 px-3 py-1.5 text-sm text-white/70 hover:bg-white/[0.06] hover:text-white"
|
||||
className="flex w-full items-center gap-2 px-3 py-1.5 text-sm text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||
>
|
||||
<div
|
||||
className="h-3 w-3 rounded-sm"
|
||||
@@ -125,13 +125,13 @@ export function AddToFolderMenu({ treeId, onFolderCreated }: AddToFolderMenuProp
|
||||
/>
|
||||
<span className="flex-1 truncate text-left">{folder.name}</span>
|
||||
{treeFolderIds.has(folder.id) && (
|
||||
<Check className="h-4 w-4 text-white" />
|
||||
<Check className="h-4 w-4 text-foreground" />
|
||||
)}
|
||||
</button>
|
||||
))
|
||||
)}
|
||||
|
||||
<div className="border-t border-white/10 my-1" />
|
||||
<div className="border-t border-border my-1" />
|
||||
|
||||
<button
|
||||
onClick={(e) => {
|
||||
@@ -139,7 +139,7 @@ export function AddToFolderMenu({ treeId, onFolderCreated }: AddToFolderMenuProp
|
||||
setIsOpen(false)
|
||||
onFolderCreated?.()
|
||||
}}
|
||||
className="flex w-full items-center gap-2 px-3 py-1.5 text-sm text-white/70 hover:bg-white/[0.06] hover:text-white"
|
||||
className="flex w-full items-center gap-2 px-3 py-1.5 text-sm text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
Create new folder
|
||||
|
||||
@@ -113,8 +113,8 @@ function FolderItem({
|
||||
onClick={() => onFolderSelect(folder.id)}
|
||||
className={cn(
|
||||
'flex w-full items-center gap-1 rounded-md py-1.5 text-sm',
|
||||
'transition-colors hover:bg-white/[0.06]',
|
||||
selectedFolderId === folder.id && 'bg-white/10 text-white font-medium'
|
||||
'transition-colors hover:bg-accent',
|
||||
selectedFolderId === folder.id && 'bg-accent text-foreground font-medium'
|
||||
)}
|
||||
style={{ paddingLeft: `${8 + depth * 16}px`, paddingRight: '8px' }}
|
||||
>
|
||||
@@ -125,7 +125,7 @@ function FolderItem({
|
||||
e.stopPropagation()
|
||||
onToggleExpand(folder.id)
|
||||
}}
|
||||
className="shrink-0 p-0.5 hover:bg-white/[0.06] rounded"
|
||||
className="shrink-0 p-0.5 hover:bg-accent rounded"
|
||||
>
|
||||
{isExpanded ? (
|
||||
<ChevronDown className="h-3 w-3" />
|
||||
@@ -138,7 +138,7 @@ function FolderItem({
|
||||
)}
|
||||
<Folder className="h-4 w-4 shrink-0" style={{ color: folder.color }} />
|
||||
<span className="flex-1 truncate text-left">{folder.name}</span>
|
||||
<span className="text-xs text-white/40 group-hover:hidden">{folder.tree_count}</span>
|
||||
<span className="text-xs text-muted-foreground group-hover:hidden">{folder.tree_count}</span>
|
||||
</button>
|
||||
|
||||
{/* Folder menu button - replaces tree count on hover */}
|
||||
@@ -150,7 +150,7 @@ function FolderItem({
|
||||
className={cn(
|
||||
'absolute right-1 top-1/2 -translate-y-1/2 rounded p-1',
|
||||
'hidden group-hover:block',
|
||||
'hover:bg-white/[0.06]'
|
||||
'hover:bg-accent'
|
||||
)}
|
||||
>
|
||||
<MoreVertical className="h-3 w-3" />
|
||||
@@ -160,8 +160,8 @@ function FolderItem({
|
||||
{menuOpenId === folder.id && (
|
||||
<div
|
||||
className={cn(
|
||||
'absolute right-0 top-full z-10 mt-1 w-40 rounded-md border border-white/10',
|
||||
'bg-black/90 backdrop-blur-sm py-1 shadow-lg'
|
||||
'absolute right-0 top-full z-10 mt-1 w-40 rounded-md border border-border',
|
||||
'bg-card backdrop-blur-sm py-1 shadow-lg'
|
||||
)}
|
||||
>
|
||||
<button
|
||||
@@ -170,7 +170,7 @@ function FolderItem({
|
||||
onEditFolder(folder)
|
||||
onMenuToggle(null)
|
||||
}}
|
||||
className="flex w-full items-center gap-2 px-3 py-1.5 text-sm text-white/70 hover:bg-white/[0.06] hover:text-white"
|
||||
className="flex w-full items-center gap-2 px-3 py-1.5 text-sm text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||
>
|
||||
<Pencil className="h-3 w-3" />
|
||||
Edit
|
||||
@@ -182,7 +182,7 @@ function FolderItem({
|
||||
onAddSubfolder(folder.id)
|
||||
onMenuToggle(null)
|
||||
}}
|
||||
className="flex w-full items-center gap-2 px-3 py-1.5 text-sm text-white/70 hover:bg-white/[0.06] hover:text-white"
|
||||
className="flex w-full items-center gap-2 px-3 py-1.5 text-sm text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||
>
|
||||
<FolderPlus className="h-3 w-3" />
|
||||
Add Subfolder
|
||||
@@ -362,7 +362,7 @@ export function FolderSidebar({
|
||||
/>
|
||||
)}
|
||||
<div className={cn(
|
||||
'w-56 shrink-0 border-r border-white/[0.06] bg-transparent',
|
||||
'w-56 shrink-0 border-r border-border bg-transparent',
|
||||
'hidden md:block',
|
||||
mobileOpen && 'fixed inset-y-0 left-0 z-50 block animate-slide-in-left md:relative md:animate-none'
|
||||
)}>
|
||||
@@ -370,10 +370,10 @@ export function FolderSidebar({
|
||||
{/* Mobile close button */}
|
||||
{mobileOpen && (
|
||||
<div className="mb-3 flex items-center justify-between md:hidden">
|
||||
<span className="text-sm font-medium text-white">Folders</span>
|
||||
<span className="text-sm font-medium text-foreground">Folders</span>
|
||||
<button
|
||||
onClick={onMobileClose}
|
||||
className="rounded-md p-1.5 text-white/40 hover:bg-white/[0.06]"
|
||||
className="rounded-md p-1.5 text-muted-foreground hover:bg-accent"
|
||||
aria-label="Close folders"
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
@@ -382,7 +382,7 @@ export function FolderSidebar({
|
||||
)}
|
||||
<button
|
||||
onClick={() => setIsExpanded(!isExpanded)}
|
||||
className="flex w-full items-center gap-2 text-sm font-medium text-white"
|
||||
className="flex w-full items-center gap-2 text-sm font-medium text-foreground"
|
||||
>
|
||||
{isExpanded ? (
|
||||
<ChevronDown className="h-4 w-4" />
|
||||
@@ -399,8 +399,8 @@ export function FolderSidebar({
|
||||
onClick={() => onFolderSelect(null)}
|
||||
className={cn(
|
||||
'flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm',
|
||||
'transition-colors hover:bg-white/[0.06]',
|
||||
selectedFolderId === null && 'bg-white/10 text-white font-medium'
|
||||
'transition-colors hover:bg-accent',
|
||||
selectedFolderId === null && 'bg-accent text-foreground font-medium'
|
||||
)}
|
||||
>
|
||||
<Folder className="h-4 w-4" />
|
||||
@@ -409,7 +409,7 @@ export function FolderSidebar({
|
||||
|
||||
{/* Loading state */}
|
||||
{isLoading ? (
|
||||
<div className="px-2 py-1.5 text-sm text-white/40">Loading...</div>
|
||||
<div className="px-2 py-1.5 text-sm text-muted-foreground">Loading...</div>
|
||||
) : (
|
||||
<>
|
||||
{/* User folders (hierarchical) */}
|
||||
@@ -439,7 +439,7 @@ export function FolderSidebar({
|
||||
onClick={() => onCreateFolder(null)}
|
||||
className={cn(
|
||||
'flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm',
|
||||
'text-white/50 transition-colors hover:bg-white/[0.06] hover:text-white'
|
||||
'text-muted-foreground transition-colors hover:bg-accent hover:text-foreground'
|
||||
)}
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
@@ -454,8 +454,8 @@ export function FolderSidebar({
|
||||
{contextMenu && (
|
||||
<div
|
||||
className={cn(
|
||||
'fixed z-50 w-44 rounded-md border border-white/10',
|
||||
'bg-black/90 backdrop-blur-sm py-1 shadow-lg'
|
||||
'fixed z-50 w-44 rounded-md border border-border',
|
||||
'bg-card backdrop-blur-sm py-1 shadow-lg'
|
||||
)}
|
||||
style={{ left: contextMenu.x, top: contextMenu.y }}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
@@ -465,7 +465,7 @@ export function FolderSidebar({
|
||||
onEditFolder(contextMenu.folder)
|
||||
closeContextMenu()
|
||||
}}
|
||||
className="flex w-full items-center gap-2 px-3 py-1.5 text-sm text-white/70 hover:bg-white/[0.06] hover:text-white"
|
||||
className="flex w-full items-center gap-2 px-3 py-1.5 text-sm text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||
>
|
||||
<Pencil className="h-3 w-3" />
|
||||
Edit
|
||||
@@ -476,7 +476,7 @@ export function FolderSidebar({
|
||||
handleAddSubfolder(contextMenu.folder.id)
|
||||
closeContextMenu()
|
||||
}}
|
||||
className="flex w-full items-center gap-2 px-3 py-1.5 text-sm text-white/70 hover:bg-white/[0.06] hover:text-white"
|
||||
className="flex w-full items-center gap-2 px-3 py-1.5 text-sm text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||
>
|
||||
<FolderPlus className="h-3 w-3" />
|
||||
Add Subfolder
|
||||
|
||||
@@ -21,7 +21,7 @@ const sortOptions: { value: SortBy; label: string }[] = [
|
||||
export function SortDropdown({ value, onChange, className }: SortDropdownProps) {
|
||||
return (
|
||||
<div className={cn('relative inline-flex items-center', className)}>
|
||||
<span className="mr-2 flex items-center gap-1.5 text-sm text-white/40">
|
||||
<span className="mr-2 flex items-center gap-1.5 text-sm text-muted-foreground">
|
||||
<ArrowUpDown className="h-4 w-4" />
|
||||
<span className="hidden sm:inline">Sort:</span>
|
||||
</span>
|
||||
@@ -29,8 +29,8 @@ export function SortDropdown({ value, onChange, className }: SortDropdownProps)
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value as SortBy)}
|
||||
className={cn(
|
||||
'rounded-md border border-white/10 bg-black/50 px-3 py-1.5 text-sm',
|
||||
'text-white focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20'
|
||||
'rounded-md border border-border bg-card px-3 py-1.5 text-sm',
|
||||
'text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
|
||||
)}
|
||||
>
|
||||
{sortOptions.map((option) => (
|
||||
|
||||
@@ -70,12 +70,12 @@ export function TreeTableView({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="overflow-x-auto rounded-2xl border border-white/[0.06]">
|
||||
<div className="overflow-x-auto rounded-2xl border border-border">
|
||||
<table className="w-full">
|
||||
<thead className="bg-white/[0.02] sticky top-0 z-10">
|
||||
<tr className="border-b border-white/[0.06]">
|
||||
<thead className="bg-accent/50 sticky top-0 z-10">
|
||||
<tr className="border-b border-border">
|
||||
<th
|
||||
className="px-4 py-3 text-left text-sm font-medium text-white/50 cursor-pointer hover:text-white"
|
||||
className="px-4 py-3 text-left text-sm font-medium text-muted-foreground cursor-pointer hover:text-foreground"
|
||||
onClick={() => handleSort('name')}
|
||||
>
|
||||
<div className="flex items-center gap-1">
|
||||
@@ -83,11 +83,11 @@ export function TreeTableView({
|
||||
{getSortIcon('name')}
|
||||
</div>
|
||||
</th>
|
||||
<th className="hidden md:table-cell px-4 py-3 text-left text-sm font-medium text-white/50">
|
||||
<th className="hidden md:table-cell px-4 py-3 text-left text-sm font-medium text-muted-foreground">
|
||||
Description
|
||||
</th>
|
||||
<th
|
||||
className="hidden lg:table-cell px-4 py-3 text-left text-sm font-medium text-white/50 cursor-pointer hover:text-white"
|
||||
className="hidden lg:table-cell px-4 py-3 text-left text-sm font-medium text-muted-foreground cursor-pointer hover:text-foreground"
|
||||
onClick={() => handleSort('category')}
|
||||
>
|
||||
<div className="flex items-center gap-1">
|
||||
@@ -95,11 +95,11 @@ export function TreeTableView({
|
||||
{getSortIcon('category')}
|
||||
</div>
|
||||
</th>
|
||||
<th className="hidden xl:table-cell px-4 py-3 text-left text-sm font-medium text-white/50">
|
||||
<th className="hidden xl:table-cell px-4 py-3 text-left text-sm font-medium text-muted-foreground">
|
||||
Tags
|
||||
</th>
|
||||
<th
|
||||
className="hidden sm:table-cell px-4 py-3 text-center text-sm font-medium text-white/50 cursor-pointer hover:text-white"
|
||||
className="hidden sm:table-cell px-4 py-3 text-center text-sm font-medium text-muted-foreground cursor-pointer hover:text-foreground"
|
||||
onClick={() => handleSort('version')}
|
||||
>
|
||||
<div className="flex items-center justify-center gap-1">
|
||||
@@ -108,7 +108,7 @@ export function TreeTableView({
|
||||
</div>
|
||||
</th>
|
||||
<th
|
||||
className="hidden sm:table-cell px-4 py-3 text-center text-sm font-medium text-white/50 cursor-pointer hover:text-white"
|
||||
className="hidden sm:table-cell px-4 py-3 text-center text-sm font-medium text-muted-foreground cursor-pointer hover:text-foreground"
|
||||
onClick={() => handleSort('usage')}
|
||||
>
|
||||
<div className="flex items-center justify-center gap-1">
|
||||
@@ -117,7 +117,7 @@ export function TreeTableView({
|
||||
</div>
|
||||
</th>
|
||||
<th
|
||||
className="hidden md:table-cell px-4 py-3 text-left text-sm font-medium text-white/50 cursor-pointer hover:text-white"
|
||||
className="hidden md:table-cell px-4 py-3 text-left text-sm font-medium text-muted-foreground cursor-pointer hover:text-foreground"
|
||||
onClick={() => handleSort('updated')}
|
||||
>
|
||||
<div className="flex items-center gap-1">
|
||||
@@ -125,17 +125,17 @@ export function TreeTableView({
|
||||
{getSortIcon('updated')}
|
||||
</div>
|
||||
</th>
|
||||
<th className="px-4 py-3 text-right text-sm font-medium text-white/50">
|
||||
<th className="px-4 py-3 text-right text-sm font-medium text-muted-foreground">
|
||||
Actions
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="bg-transparent">
|
||||
{trees.map((tree) => (
|
||||
<tr key={tree.id} className="border-b border-white/[0.06] last:border-0 hover:bg-white/[0.04]">
|
||||
<tr key={tree.id} className="border-b border-border last:border-0 hover:bg-accent/50">
|
||||
<td className="px-4 py-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-medium text-white truncate max-w-[200px]">
|
||||
<span className="font-medium text-foreground truncate max-w-[200px]">
|
||||
{tree.name}
|
||||
</span>
|
||||
{tree.status === 'draft' && (
|
||||
@@ -146,23 +146,23 @@ export function TreeTableView({
|
||||
)}
|
||||
{tree.is_public ? (
|
||||
<span title="Public tree">
|
||||
<Globe className="h-3.5 w-3.5 text-white/40 flex-shrink-0" />
|
||||
<Globe className="h-3.5 w-3.5 text-muted-foreground flex-shrink-0" />
|
||||
</span>
|
||||
) : (
|
||||
<span title="Private tree">
|
||||
<Lock className="h-3.5 w-3.5 text-white/40 flex-shrink-0" />
|
||||
<Lock className="h-3.5 w-3.5 text-muted-foreground flex-shrink-0" />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
<td className="hidden md:table-cell px-4 py-3 text-sm text-white/70">
|
||||
<td className="hidden md:table-cell px-4 py-3 text-sm text-muted-foreground">
|
||||
<span className="truncate block max-w-[250px]">
|
||||
{tree.description || 'No description'}
|
||||
</span>
|
||||
</td>
|
||||
<td className="hidden lg:table-cell px-4 py-3">
|
||||
{tree.category_info && (
|
||||
<span className="inline-block rounded-full bg-white/10 px-2 py-0.5 text-xs text-white/70">
|
||||
<span className="inline-block rounded-full bg-accent px-2 py-0.5 text-xs text-muted-foreground">
|
||||
{tree.category_info.name}
|
||||
</span>
|
||||
)}
|
||||
@@ -172,13 +172,13 @@ export function TreeTableView({
|
||||
<TagBadges tags={tree.tags} maxVisible={2} onTagClick={onTagClick} />
|
||||
)}
|
||||
</td>
|
||||
<td className="hidden sm:table-cell px-4 py-3 text-center text-sm text-white/70">
|
||||
<td className="hidden sm:table-cell px-4 py-3 text-center text-sm text-muted-foreground">
|
||||
v{tree.version}
|
||||
</td>
|
||||
<td className="hidden sm:table-cell px-4 py-3 text-center text-sm text-white/70">
|
||||
<td className="hidden sm:table-cell px-4 py-3 text-center text-sm text-muted-foreground">
|
||||
{tree.usage_count}
|
||||
</td>
|
||||
<td className="hidden md:table-cell px-4 py-3 text-sm text-white/70">
|
||||
<td className="hidden md:table-cell px-4 py-3 text-sm text-muted-foreground">
|
||||
{formatDate(tree.updated_at)}
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
@@ -189,8 +189,8 @@ export function TreeTableView({
|
||||
type="button"
|
||||
onClick={() => onForkTree(tree.id)}
|
||||
className={cn(
|
||||
'rounded-md border border-white/10 p-1.5 text-white/60',
|
||||
'hover:bg-white/10 hover:text-white'
|
||||
'rounded-md border border-border p-1.5 text-muted-foreground',
|
||||
'hover:bg-accent hover:text-foreground'
|
||||
)}
|
||||
title="Fork tree"
|
||||
aria-label="Fork tree"
|
||||
@@ -203,8 +203,8 @@ export function TreeTableView({
|
||||
<Link
|
||||
to={`/trees/${tree.id}/edit`}
|
||||
className={cn(
|
||||
'rounded-md border border-white/10 p-1.5 text-white/60',
|
||||
'hover:bg-white/10 hover:text-white'
|
||||
'rounded-md border border-border p-1.5 text-muted-foreground',
|
||||
'hover:bg-accent hover:text-foreground'
|
||||
)}
|
||||
title="Edit tree"
|
||||
aria-label="Edit tree"
|
||||
@@ -215,7 +215,7 @@ export function TreeTableView({
|
||||
type="button"
|
||||
onClick={() => onDeleteTree(tree)}
|
||||
className={cn(
|
||||
'rounded-md border border-white/10 p-1.5 text-white/60',
|
||||
'rounded-md border border-border p-1.5 text-muted-foreground',
|
||||
'hover:bg-red-500/20 hover:text-red-400'
|
||||
)}
|
||||
title="Delete tree"
|
||||
@@ -229,8 +229,8 @@ export function TreeTableView({
|
||||
type="button"
|
||||
onClick={() => onStartSession(tree.id, tree.tree_type)}
|
||||
className={cn(
|
||||
'rounded-md bg-white px-3 py-1.5 text-xs font-medium text-black',
|
||||
'hover:bg-white/90 whitespace-nowrap'
|
||||
'rounded-md bg-gradient-brand px-3 py-1.5 text-xs font-medium text-white shadow-lg shadow-primary/20',
|
||||
'hover:opacity-90 whitespace-nowrap'
|
||||
)}
|
||||
>
|
||||
Start
|
||||
|
||||
@@ -11,15 +11,15 @@ interface ViewToggleProps {
|
||||
|
||||
export function ViewToggle({ view, onChange, className }: ViewToggleProps) {
|
||||
return (
|
||||
<div className={cn('flex items-center gap-1 rounded-md border border-white/10 p-1', className)}>
|
||||
<div className={cn('flex items-center gap-1 rounded-md border border-border p-1', className)}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onChange('grid')}
|
||||
className={cn(
|
||||
'rounded p-1.5 transition-colors',
|
||||
view === 'grid'
|
||||
? 'bg-white/10 text-white border-white/20'
|
||||
: 'text-white/50 hover:bg-white/[0.06] hover:text-white'
|
||||
? 'bg-accent text-foreground border-border'
|
||||
: 'text-muted-foreground hover:bg-accent hover:text-foreground'
|
||||
)}
|
||||
title="Grid view"
|
||||
aria-label="Grid view"
|
||||
@@ -32,8 +32,8 @@ export function ViewToggle({ view, onChange, className }: ViewToggleProps) {
|
||||
className={cn(
|
||||
'rounded p-1.5 transition-colors',
|
||||
view === 'list'
|
||||
? 'bg-white/10 text-white border-white/20'
|
||||
: 'text-white/50 hover:bg-white/[0.06] hover:text-white'
|
||||
? 'bg-accent text-foreground border-border'
|
||||
: 'text-muted-foreground hover:bg-accent hover:text-foreground'
|
||||
)}
|
||||
title="List view"
|
||||
aria-label="List view"
|
||||
@@ -46,8 +46,8 @@ export function ViewToggle({ view, onChange, className }: ViewToggleProps) {
|
||||
className={cn(
|
||||
'rounded p-1.5 transition-colors',
|
||||
view === 'table'
|
||||
? 'bg-white/10 text-white border-white/20'
|
||||
: 'text-white/50 hover:bg-white/[0.06] hover:text-white'
|
||||
? 'bg-accent text-foreground border-border'
|
||||
: 'text-muted-foreground hover:bg-accent hover:text-foreground'
|
||||
)}
|
||||
title="Table view"
|
||||
aria-label="Table view"
|
||||
|
||||
@@ -60,7 +60,7 @@ export function StepList() {
|
||||
return (
|
||||
<div
|
||||
key={step.id}
|
||||
className="flex items-center gap-2 rounded-lg border border-dashed border-border bg-white/[0.02] px-3 py-2"
|
||||
className="flex items-center gap-2 rounded-lg border border-dashed border-border bg-accent/50 px-3 py-2"
|
||||
>
|
||||
<CheckCircle2 className="h-4 w-4 text-emerald-400/50" />
|
||||
<input
|
||||
@@ -142,7 +142,7 @@ export function StepList() {
|
||||
<div
|
||||
className={cn(
|
||||
'group flex items-center gap-2 rounded-xl border border-border px-3 py-2.5 transition-colors',
|
||||
'hover:border-primary/30 hover:bg-white/[0.03]'
|
||||
'hover:border-primary/30 hover:bg-accent/50'
|
||||
)}
|
||||
>
|
||||
<GripVertical className="h-4 w-4 shrink-0 cursor-grab text-muted-foreground group-hover:text-muted-foreground" />
|
||||
|
||||
@@ -20,24 +20,24 @@ export function ProgressBar({ currentStep, totalSteps, elapsedMinutes, estimated
|
||||
return (
|
||||
<div className="space-y-1.5">
|
||||
<div className="flex items-center justify-between text-xs">
|
||||
<span className="text-white/60">
|
||||
<span className="text-muted-foreground">
|
||||
Step {currentStep} of {totalSteps}
|
||||
</span>
|
||||
<div className="flex items-center gap-3">
|
||||
{elapsedMinutes !== undefined && (
|
||||
<span className="text-white/50">
|
||||
<span className="text-muted-foreground">
|
||||
{formatTime(elapsed)}
|
||||
{estimatedTotalMinutes ? (
|
||||
<span className="text-white/25"> / est. {formatTime(estimatedTotalMinutes)}</span>
|
||||
<span className="text-muted-foreground"> / est. {formatTime(estimatedTotalMinutes)}</span>
|
||||
) : null}
|
||||
</span>
|
||||
)}
|
||||
<span className="font-medium text-white/70">{percentage}%</span>
|
||||
<span className="font-medium text-muted-foreground">{percentage}%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-1.5 overflow-hidden rounded-full bg-white/10">
|
||||
<div className="h-1.5 overflow-hidden rounded-full bg-accent">
|
||||
<div
|
||||
className="h-full rounded-full bg-white transition-all duration-300"
|
||||
className="h-full rounded-full bg-gradient-brand transition-all duration-300"
|
||||
style={{ width: `${percentage}%` }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -45,7 +45,7 @@ export function ContinuationModal({
|
||||
{/* Descendant Selection */}
|
||||
{hasDescendants && (
|
||||
<div>
|
||||
<p className="mb-4 text-sm text-white/70">
|
||||
<p className="mb-4 text-sm text-muted-foreground">
|
||||
Select the next step in your troubleshooting path:
|
||||
</p>
|
||||
|
||||
@@ -56,20 +56,20 @@ export function ContinuationModal({
|
||||
onClick={() => onSelectNode(node.id)}
|
||||
title={`From: ${node.parentOptionLabel}`}
|
||||
className={cn(
|
||||
'flex w-full items-center gap-3 rounded-lg border border-white/[0.06] p-3 text-left transition-colors',
|
||||
'hover:border-white/20 hover:bg-white/10'
|
||||
'flex w-full items-center gap-3 rounded-lg border border-border p-3 text-left transition-colors',
|
||||
'hover:border-border hover:bg-accent'
|
||||
)}
|
||||
>
|
||||
<div className="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-white/10">
|
||||
<div className="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-accent">
|
||||
{nodeTypeIcons[node.type]}
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate font-medium text-white">{node.label}</p>
|
||||
<p className="text-xs text-white/40">
|
||||
<p className="truncate font-medium text-foreground">{node.label}</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{nodeTypeLabels[node.type]}
|
||||
</p>
|
||||
</div>
|
||||
<ArrowRight className="h-4 w-4 flex-shrink-0 text-white/40" />
|
||||
<ArrowRight className="h-4 w-4 flex-shrink-0 text-muted-foreground" />
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
@@ -79,11 +79,11 @@ export function ContinuationModal({
|
||||
{/* Divider */}
|
||||
{hasDescendants && (
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="h-px flex-1 bg-white/[0.06]" />
|
||||
<span className="text-xs font-medium uppercase tracking-wide text-white/40">
|
||||
<div className="h-px flex-1 bg-border" />
|
||||
<span className="text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
||||
Or
|
||||
</span>
|
||||
<div className="h-px flex-1 bg-white/[0.06]" />
|
||||
<div className="h-px flex-1 bg-border" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -100,8 +100,8 @@ export function ContinuationModal({
|
||||
<GitBranch className="h-5 w-5 text-amber-500" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="font-medium text-white">Build Custom Branch</p>
|
||||
<p className="text-sm text-white/70">
|
||||
<p className="font-medium text-foreground">Build Custom Branch</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Create your own troubleshooting path with custom steps
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -69,9 +69,9 @@ export function ExportPreviewModal({
|
||||
<Modal isOpen={isOpen} onClose={handleClose} title="Export Preview" size="xl">
|
||||
{/* Filename, format info, and controls */}
|
||||
<div className="mb-3 flex flex-wrap items-center justify-between gap-2">
|
||||
<p className="text-sm text-white/70">
|
||||
Filename: <span className="font-mono text-white">{filename}</span>
|
||||
<span className="ml-3 rounded bg-white/10 px-2 py-0.5 text-xs text-white/70">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Filename: <span className="font-mono text-foreground">{filename}</span>
|
||||
<span className="ml-3 rounded bg-accent px-2 py-0.5 text-xs text-muted-foreground">
|
||||
{format === 'markdown' ? 'Markdown' : format === 'html' ? 'HTML' : format === 'psa' ? 'PSA' : 'Plain Text'}
|
||||
</span>
|
||||
{isModified && (
|
||||
@@ -81,23 +81,23 @@ export function ExportPreviewModal({
|
||||
<div className="flex flex-col items-end gap-1">
|
||||
<div className="flex items-center gap-3">
|
||||
{onToggleSummary && (
|
||||
<label className="flex items-center gap-2 text-sm text-white/60 cursor-pointer">
|
||||
<label className="flex items-center gap-2 text-sm text-muted-foreground cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={includeSummary}
|
||||
onChange={(e) => onToggleSummary(e.target.checked)}
|
||||
className="h-4 w-4 rounded border-white/20 bg-black/50"
|
||||
className="h-4 w-4 rounded border-border bg-card"
|
||||
/>
|
||||
Include Summary
|
||||
</label>
|
||||
)}
|
||||
{onToggleRedaction && (
|
||||
<label className="flex items-center gap-2 text-sm text-white/60 cursor-pointer">
|
||||
<label className="flex items-center gap-2 text-sm text-muted-foreground cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={redactionEnabled}
|
||||
onChange={(e) => onToggleRedaction(e.target.checked)}
|
||||
className="h-4 w-4 rounded border-white/20 bg-black/50"
|
||||
className="h-4 w-4 rounded border-border bg-card"
|
||||
/>
|
||||
Mask Sensitive Data
|
||||
</label>
|
||||
@@ -114,13 +114,13 @@ export function ExportPreviewModal({
|
||||
</p>
|
||||
)}
|
||||
{redactionEnabled && redactionSummary && redactionSummary.total === 0 && (
|
||||
<p className="text-xs text-white/40">No sensitive data detected</p>
|
||||
<p className="text-xs text-muted-foreground">No sensitive data detected</p>
|
||||
)}
|
||||
{isModified && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleReset}
|
||||
className="flex items-center gap-1 text-xs text-white/40 hover:text-white"
|
||||
className="flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground"
|
||||
title="Reset to original"
|
||||
>
|
||||
<RotateCcw className="h-3 w-3" />
|
||||
@@ -139,9 +139,9 @@ export function ExportPreviewModal({
|
||||
value={editedContent}
|
||||
onChange={(e) => setEditedContent(e.target.value)}
|
||||
className={cn(
|
||||
'h-96 w-full resize-y rounded-md border border-white/10 bg-black/50 p-4',
|
||||
'font-mono text-sm text-white',
|
||||
'focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20'
|
||||
'h-96 w-full resize-y rounded-md border border-border bg-card p-4',
|
||||
'font-mono text-sm text-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -151,9 +151,9 @@ export function ExportPreviewModal({
|
||||
type="button"
|
||||
onClick={handleCopy}
|
||||
className={cn(
|
||||
'flex items-center gap-2 rounded-md border border-white/10 px-3 py-2 text-sm font-medium',
|
||||
'text-white/60 hover:bg-white/10 hover:text-white',
|
||||
'focus:outline-none focus:ring-2 focus:ring-white/20'
|
||||
'flex items-center gap-2 rounded-md border border-border px-3 py-2 text-sm font-medium',
|
||||
'text-muted-foreground hover:bg-accent hover:text-foreground',
|
||||
'focus:outline-none focus:ring-2 focus:ring-primary/20'
|
||||
)}
|
||||
>
|
||||
{copied ? (
|
||||
@@ -172,8 +172,8 @@ export function ExportPreviewModal({
|
||||
type="button"
|
||||
onClick={handleDownload}
|
||||
className={cn(
|
||||
'flex items-center gap-2 rounded-md bg-white px-3 py-2 text-sm font-medium text-black',
|
||||
'hover:bg-white/90 focus:outline-none focus:ring-2 focus:ring-white/20'
|
||||
'flex items-center gap-2 rounded-md bg-gradient-brand text-white shadow-lg shadow-primary/20 px-3 py-2 text-sm font-medium',
|
||||
'hover:opacity-90 focus:outline-none focus:ring-2 focus:ring-primary/20'
|
||||
)}
|
||||
>
|
||||
<Download className="h-4 w-4" />
|
||||
|
||||
@@ -49,7 +49,7 @@ export function ForkTreeModal({
|
||||
disabled={isSaving}
|
||||
className={cn(
|
||||
'rounded-md px-4 py-2 text-sm font-medium transition-colors',
|
||||
'text-white/60 hover:bg-white/10 hover:text-white',
|
||||
'text-muted-foreground hover:bg-accent hover:text-foreground',
|
||||
'disabled:cursor-not-allowed disabled:opacity-50'
|
||||
)}
|
||||
>
|
||||
@@ -59,8 +59,8 @@ export function ForkTreeModal({
|
||||
onClick={handleFork}
|
||||
disabled={isSaving || !name.trim()}
|
||||
className={cn(
|
||||
'flex items-center gap-2 rounded-md bg-white px-4 py-2 text-sm font-medium text-black transition-colors',
|
||||
'hover:bg-white/90',
|
||||
'flex items-center gap-2 rounded-md bg-gradient-brand text-white shadow-lg shadow-primary/20 px-4 py-2 text-sm font-medium transition-colors',
|
||||
'hover:opacity-90',
|
||||
'disabled:cursor-not-allowed disabled:opacity-50'
|
||||
)}
|
||||
>
|
||||
@@ -82,13 +82,13 @@ export function ForkTreeModal({
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={onClose} title="Save Custom Tree?" footer={footer}>
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-start gap-3 rounded-lg bg-white/5 p-4">
|
||||
<div className="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-full bg-white/10">
|
||||
<GitFork className="h-5 w-5 text-white" />
|
||||
<div className="flex items-start gap-3 rounded-lg bg-accent/50 p-4">
|
||||
<div className="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-full bg-accent">
|
||||
<GitFork className="h-5 w-5 text-foreground" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-medium text-white">You've created a custom troubleshooting path!</p>
|
||||
<p className="mt-1 text-sm text-white/70">
|
||||
<p className="font-medium text-foreground">You've created a custom troubleshooting path!</p>
|
||||
<p className="mt-1 text-sm text-muted-foreground">
|
||||
Save it as your own personal tree to reuse this troubleshooting flow in the future.
|
||||
</p>
|
||||
</div>
|
||||
@@ -96,7 +96,7 @@ export function ForkTreeModal({
|
||||
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label htmlFor="tree-name" className="mb-1.5 block text-sm font-medium text-white">
|
||||
<label htmlFor="tree-name" className="mb-1.5 block text-sm font-medium text-foreground">
|
||||
Tree Name <span className="text-red-400">*</span>
|
||||
</label>
|
||||
<input
|
||||
@@ -106,15 +106,15 @@ export function ForkTreeModal({
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
placeholder="My Custom Tree"
|
||||
className={cn(
|
||||
'w-full rounded-md border border-white/10 bg-black/50 px-3 py-2 text-sm text-white',
|
||||
'focus:outline-none focus:border-white/30 focus:ring-1 focus:ring-white/20'
|
||||
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
|
||||
'focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="tree-description" className="mb-1.5 block text-sm font-medium text-white">
|
||||
Description <span className="text-white/40">(optional)</span>
|
||||
<label htmlFor="tree-description" className="mb-1.5 block text-sm font-medium text-foreground">
|
||||
Description <span className="text-muted-foreground">(optional)</span>
|
||||
</label>
|
||||
<textarea
|
||||
id="tree-description"
|
||||
@@ -123,8 +123,8 @@ export function ForkTreeModal({
|
||||
placeholder="Describe what this tree helps troubleshoot..."
|
||||
rows={3}
|
||||
className={cn(
|
||||
'w-full rounded-md border border-white/10 bg-black/50 px-3 py-2 text-sm text-white',
|
||||
'focus:outline-none focus:border-white/30 focus:ring-1 focus:ring-white/20',
|
||||
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
|
||||
'focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20',
|
||||
'resize-none'
|
||||
)}
|
||||
/>
|
||||
@@ -135,7 +135,7 @@ export function ForkTreeModal({
|
||||
<p className="text-sm text-red-400">{error}</p>
|
||||
)}
|
||||
|
||||
<p className="text-xs text-white/40">
|
||||
<p className="text-xs text-muted-foreground">
|
||||
The new tree will include your custom steps and will be saved to your personal tree library.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -28,8 +28,8 @@ export function PostStepActionModal({
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={onClose} title="What would you like to do?">
|
||||
<div className="space-y-3">
|
||||
<p className="mb-4 text-sm text-white/70">
|
||||
You've created: <strong className="text-white">{step.title}</strong>
|
||||
<p className="mb-4 text-sm text-muted-foreground">
|
||||
You've created: <strong className="text-foreground">{step.title}</strong>
|
||||
</p>
|
||||
|
||||
{/* Save for Later - Only show if not already from library */}
|
||||
@@ -48,8 +48,8 @@ export function PostStepActionModal({
|
||||
<Bookmark className="h-5 w-5 text-blue-500" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-medium text-white">Save for Later</p>
|
||||
<p className="text-sm text-white/70">
|
||||
<p className="font-medium text-foreground">Save for Later</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Add to your step library for future use
|
||||
</p>
|
||||
</div>
|
||||
@@ -62,8 +62,8 @@ export function PostStepActionModal({
|
||||
onClick={onUseNow}
|
||||
disabled={isSaving}
|
||||
className={cn(
|
||||
'w-full rounded-lg border border-white/[0.06] p-4 text-left transition-colors',
|
||||
'hover:border-white/20 hover:bg-white/10',
|
||||
'w-full rounded-lg border border-border p-4 text-left transition-colors',
|
||||
'hover:border-border hover:bg-accent',
|
||||
'disabled:cursor-not-allowed disabled:opacity-50'
|
||||
)}
|
||||
>
|
||||
@@ -96,8 +96,8 @@ export function PostStepActionModal({
|
||||
<BookmarkPlus className="h-5 w-5 text-purple-500" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-medium text-white">Do Both</p>
|
||||
<p className="text-sm text-white/70">
|
||||
<p className="font-medium text-foreground">Do Both</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Save to library AND use in this session
|
||||
</p>
|
||||
</div>
|
||||
@@ -106,7 +106,7 @@ export function PostStepActionModal({
|
||||
)}
|
||||
|
||||
{isSaving && (
|
||||
<p className="text-center text-sm text-white/40">Saving...</p>
|
||||
<p className="text-center text-sm text-muted-foreground">Saving...</p>
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
@@ -124,8 +124,8 @@ export function ScratchpadSidebar({ sessionId, initialContent, onSave, onOpenCha
|
||||
onClick={() => setIsCollapsed(false)}
|
||||
className={cn(
|
||||
'fixed right-2 top-1/2 z-40 -translate-y-1/2 rounded-md p-2.5',
|
||||
'bg-[#0a0a0a] border border-white/[0.06] shadow-md',
|
||||
'text-white/40 hover:bg-white/10 hover:text-white',
|
||||
'bg-card border border-border shadow-md',
|
||||
'text-muted-foreground hover:bg-accent hover:text-foreground',
|
||||
'transition-opacity duration-200',
|
||||
isCollapsed ? 'opacity-100' : 'pointer-events-none opacity-0'
|
||||
)}
|
||||
@@ -153,29 +153,29 @@ export function ScratchpadSidebar({ sessionId, initialContent, onSave, onOpenCha
|
||||
'fixed z-40',
|
||||
'inset-0 sm:inset-auto sm:right-2 sm:top-1/2 sm:-translate-y-1/2',
|
||||
'flex w-full flex-col sm:h-[55vh] sm:w-[420px]',
|
||||
'border-white/[0.06] bg-[#0a0a0a]/95 backdrop-blur-md shadow-xl sm:rounded-lg sm:border',
|
||||
'border-border bg-card/95 backdrop-blur-md shadow-xl sm:rounded-lg sm:border',
|
||||
'transition-transform duration-200 ease-out',
|
||||
isCollapsed ? 'translate-x-full' : 'translate-x-0'
|
||||
)}
|
||||
>
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between border-b border-white/[0.06] px-3 py-2">
|
||||
<div className="flex items-center justify-between border-b border-border px-3 py-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<StickyNote className="h-4 w-4 text-white/40" />
|
||||
<span className="text-sm font-medium text-white">Scratchpad</span>
|
||||
<span className="text-xs text-white/30">Ctrl+/</span>
|
||||
<StickyNote className="h-4 w-4 text-muted-foreground" />
|
||||
<span className="text-sm font-medium text-foreground">Scratchpad</span>
|
||||
<span className="text-xs text-muted-foreground">Ctrl+/</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<button
|
||||
onClick={() => setShowPreview(!showPreview)}
|
||||
className="rounded p-1 text-white/40 hover:bg-white/10 hover:text-white"
|
||||
className="rounded p-1 text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||
title={showPreview ? 'Edit' : 'Preview'}
|
||||
>
|
||||
{showPreview ? <Pencil className="h-3.5 w-3.5" /> : <Eye className="h-3.5 w-3.5" />}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setIsCollapsed(true)}
|
||||
className="rounded p-1 text-white/40 hover:bg-white/10 hover:text-white"
|
||||
className="rounded p-1 text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||
title="Close scratchpad"
|
||||
aria-label="Close scratchpad"
|
||||
>
|
||||
@@ -191,7 +191,7 @@ export function ScratchpadSidebar({ sessionId, initialContent, onSave, onOpenCha
|
||||
{content.trim() ? (
|
||||
<MarkdownContent content={content} className="text-sm" />
|
||||
) : (
|
||||
<p className="text-sm italic text-white/40">Nothing to preview</p>
|
||||
<p className="text-sm italic text-muted-foreground">Nothing to preview</p>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
@@ -202,7 +202,7 @@ export function ScratchpadSidebar({ sessionId, initialContent, onSave, onOpenCha
|
||||
placeholder={"Capture IPs, error codes, server names, user info...\n\nSupports markdown formatting."}
|
||||
className={cn(
|
||||
'h-full min-h-[200px] w-full resize-none rounded-md border-0 bg-transparent p-0 text-sm',
|
||||
'text-white placeholder:text-white/40',
|
||||
'text-foreground placeholder:text-muted-foreground',
|
||||
'focus:outline-none focus:ring-0'
|
||||
)}
|
||||
/>
|
||||
@@ -210,15 +210,15 @@ export function ScratchpadSidebar({ sessionId, initialContent, onSave, onOpenCha
|
||||
</div>
|
||||
|
||||
{/* Save Indicator */}
|
||||
<div className="border-t border-white/[0.06] px-3 py-1.5">
|
||||
<div className="border-t border-border px-3 py-1.5">
|
||||
<div className="flex items-center gap-1.5 text-xs">
|
||||
{saveStatus === 'unsaved' && (
|
||||
<span className="text-white/40">Unsaved changes</span>
|
||||
<span className="text-muted-foreground">Unsaved changes</span>
|
||||
)}
|
||||
{saveStatus === 'saving' && (
|
||||
<>
|
||||
<Loader2 className="h-3 w-3 animate-spin text-white/40" />
|
||||
<span className="text-white/40">Saving...</span>
|
||||
<Loader2 className="h-3 w-3 animate-spin text-muted-foreground" />
|
||||
<span className="text-muted-foreground">Saving...</span>
|
||||
</>
|
||||
)}
|
||||
{saveStatus === 'saved' && (
|
||||
@@ -228,7 +228,7 @@ export function ScratchpadSidebar({ sessionId, initialContent, onSave, onOpenCha
|
||||
<span className="text-red-400">Save failed</span>
|
||||
)}
|
||||
{saveStatus === 'idle' && (
|
||||
<span className="text-white/30">Markdown supported</span>
|
||||
<span className="text-muted-foreground">Markdown supported</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -51,8 +51,8 @@ export function SessionOutcomeModal({
|
||||
onClick={onClose}
|
||||
disabled={isSubmitting}
|
||||
className={cn(
|
||||
'rounded-md border border-white/10 px-4 py-2 text-sm font-medium text-white/60',
|
||||
'hover:bg-white/10 hover:text-white disabled:opacity-50'
|
||||
'rounded-md border border-border px-4 py-2 text-sm font-medium text-muted-foreground',
|
||||
'hover:bg-accent hover:text-foreground disabled:opacity-50'
|
||||
)}
|
||||
>
|
||||
Cancel
|
||||
@@ -62,8 +62,8 @@ export function SessionOutcomeModal({
|
||||
onClick={handleSubmit}
|
||||
disabled={isSubmitting}
|
||||
className={cn(
|
||||
'rounded-md bg-white px-4 py-2 text-sm font-medium text-black',
|
||||
'hover:bg-white/90 disabled:opacity-50'
|
||||
'rounded-md bg-gradient-brand text-white shadow-lg shadow-primary/20 px-4 py-2 text-sm font-medium',
|
||||
'hover:opacity-90 disabled:opacity-50'
|
||||
)}
|
||||
>
|
||||
{isSubmitting ? 'Completing...' : 'Complete Session'}
|
||||
@@ -72,7 +72,7 @@ export function SessionOutcomeModal({
|
||||
)}
|
||||
>
|
||||
<form key={String(isOpen)} ref={formRef} className="space-y-4">
|
||||
<p className="text-sm text-white/70">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Select the session outcome before completion.
|
||||
</p>
|
||||
<div className="space-y-2">
|
||||
@@ -80,8 +80,8 @@ export function SessionOutcomeModal({
|
||||
<label
|
||||
key={option.value}
|
||||
className={cn(
|
||||
'block cursor-pointer rounded-lg border border-white/10 p-3 transition-colors',
|
||||
'hover:bg-white/[0.04]'
|
||||
'block cursor-pointer rounded-lg border border-border p-3 transition-colors',
|
||||
'hover:bg-accent/50'
|
||||
)}
|
||||
>
|
||||
<div className="flex items-start gap-3">
|
||||
@@ -93,8 +93,8 @@ export function SessionOutcomeModal({
|
||||
className="mt-1 h-4 w-4"
|
||||
/>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-white">{option.label}</p>
|
||||
<p className="text-xs text-white/50">{option.description}</p>
|
||||
<p className="text-sm font-medium text-foreground">{option.label}</p>
|
||||
<p className="text-xs text-muted-foreground">{option.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
@@ -102,31 +102,31 @@ export function SessionOutcomeModal({
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-white">Outcome Notes (optional)</label>
|
||||
<label className="block text-sm font-medium text-foreground">Outcome Notes (optional)</label>
|
||||
<textarea
|
||||
name="outcome-notes"
|
||||
defaultValue=""
|
||||
rows={3}
|
||||
placeholder="Add context for this outcome..."
|
||||
className={cn(
|
||||
'mt-1 block w-full rounded-md border border-white/10 bg-black/50 px-3 py-2',
|
||||
'text-sm text-white placeholder:text-white/40',
|
||||
'focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20'
|
||||
'mt-1 block w-full rounded-md border border-border bg-card px-3 py-2',
|
||||
'text-sm text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-white">Next Steps / Follow-Up (optional)</label>
|
||||
<label className="block text-sm font-medium text-foreground">Next Steps / Follow-Up (optional)</label>
|
||||
<textarea
|
||||
name="next-steps"
|
||||
defaultValue=""
|
||||
rows={3}
|
||||
placeholder="Actions to take after this session..."
|
||||
className={cn(
|
||||
'mt-1 block w-full rounded-md border border-white/10 bg-black/50 px-3 py-2',
|
||||
'text-sm text-white placeholder:text-white/40',
|
||||
'focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20'
|
||||
'mt-1 block w-full rounded-md border border-border bg-card px-3 py-2',
|
||||
'text-sm text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -65,13 +65,13 @@ export function CustomStepModal({ isOpen, onClose, onInsertStep }: CustomStepMod
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex items-end justify-center bg-black/80 backdrop-blur-sm sm:items-center sm:p-4">
|
||||
<div className="relative flex h-[95vh] w-full max-w-full flex-col border border-white/[0.06] bg-[#0a0a0a] shadow-lg sm:h-[90vh] sm:max-w-4xl sm:rounded-2xl">
|
||||
<div className="relative flex h-[95vh] w-full max-w-full flex-col border border-border bg-card shadow-lg sm:h-[90vh] sm:max-w-4xl sm:rounded-2xl">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between border-b border-white/[0.06] p-4">
|
||||
<h2 className="text-lg font-semibold text-white">Add Custom Step</h2>
|
||||
<div className="flex items-center justify-between border-b border-border p-4">
|
||||
<h2 className="text-lg font-semibold text-foreground">Add Custom Step</h2>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="rounded-md p-1.5 text-white/40 hover:bg-white/10 hover:text-white"
|
||||
className="rounded-md p-1.5 text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||
aria-label="Close"
|
||||
>
|
||||
<X className="h-5 w-5" />
|
||||
@@ -79,15 +79,15 @@ export function CustomStepModal({ isOpen, onClose, onInsertStep }: CustomStepMod
|
||||
</div>
|
||||
|
||||
{/* Tabs */}
|
||||
<div className="flex border-b border-white/[0.06]">
|
||||
<div className="flex border-b border-border">
|
||||
{canCreateSteps && (
|
||||
<button
|
||||
onClick={() => setActiveTab('create')}
|
||||
className={cn(
|
||||
'flex-1 px-4 py-3 text-sm font-medium transition-colors',
|
||||
activeTab === 'create'
|
||||
? 'border-b-2 border-white bg-white/5 text-white'
|
||||
: 'text-white/40 hover:bg-white/10 hover:text-white'
|
||||
? 'border-b-2 border-primary bg-primary/5 text-foreground'
|
||||
: 'text-muted-foreground hover:bg-accent hover:text-foreground'
|
||||
)}
|
||||
>
|
||||
Type My Own
|
||||
@@ -134,8 +134,8 @@ export function CustomStepModal({ isOpen, onClose, onInsertStep }: CustomStepMod
|
||||
{isSubmitting && (
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-black/80 backdrop-blur-sm">
|
||||
<div className="flex flex-col items-center gap-3">
|
||||
<div className="h-8 w-8 animate-spin rounded-full border-4 border-white/20 border-t-white" />
|
||||
<p className="text-sm text-white/40">Creating step...</p>
|
||||
<div className="h-8 w-8 animate-spin rounded-full border-4 border-border border-t-foreground" />
|
||||
<p className="text-sm text-muted-foreground">Creating step...</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -27,7 +27,7 @@ export function StepCard({ step, onPreview, onInsert }: StepCardProps) {
|
||||
const remainingTags = step.tags.length - 3
|
||||
|
||||
return (
|
||||
<div className="group rounded-lg border border-white/[0.06] bg-[#0a0a0a] p-4 transition-shadow hover:shadow-md">
|
||||
<div className="group rounded-lg border border-border bg-card p-4 transition-shadow hover:shadow-md">
|
||||
{/* Header */}
|
||||
<div className="mb-3 flex items-start justify-between gap-2">
|
||||
<div className="flex-1">
|
||||
@@ -52,12 +52,12 @@ export function StepCard({ step, onPreview, onInsert }: StepCardProps) {
|
||||
</div>
|
||||
|
||||
{/* Title */}
|
||||
<h3 className="font-semibold text-white line-clamp-2">{step.title}</h3>
|
||||
<h3 className="font-semibold text-foreground line-clamp-2">{step.title}</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Metadata */}
|
||||
<div className="mb-3 space-y-1.5 text-sm text-white/40">
|
||||
<div className="mb-3 space-y-1.5 text-sm text-muted-foreground">
|
||||
{/* Category */}
|
||||
{step.category_name && (
|
||||
<div className="flex items-center gap-1.5">
|
||||
@@ -103,7 +103,7 @@ export function StepCard({ step, onPreview, onInsert }: StepCardProps) {
|
||||
{visibleTags.map(tag => (
|
||||
<span
|
||||
key={tag}
|
||||
className="rounded-full bg-white/10 px-2 py-0.5 text-xs text-white/70"
|
||||
className="rounded-full bg-accent px-2 py-0.5 text-xs text-muted-foreground"
|
||||
>
|
||||
{tag}
|
||||
</span>
|
||||
@@ -121,8 +121,8 @@ export function StepCard({ step, onPreview, onInsert }: StepCardProps) {
|
||||
<button
|
||||
onClick={() => onPreview(step)}
|
||||
className={cn(
|
||||
'flex flex-1 items-center justify-center gap-2 rounded-md border border-white/10 px-3 py-2 text-sm font-medium text-white/60',
|
||||
'hover:bg-white/10 hover:text-white transition-colors'
|
||||
'flex flex-1 items-center justify-center gap-2 rounded-md border border-border px-3 py-2 text-sm font-medium text-muted-foreground',
|
||||
'hover:bg-accent hover:text-foreground transition-colors'
|
||||
)}
|
||||
>
|
||||
<Eye className="h-4 w-4" />
|
||||
@@ -131,8 +131,8 @@ export function StepCard({ step, onPreview, onInsert }: StepCardProps) {
|
||||
<button
|
||||
onClick={() => onInsert(step)}
|
||||
className={cn(
|
||||
'flex flex-1 items-center justify-center gap-2 rounded-md bg-white px-3 py-2 text-sm font-medium text-black',
|
||||
'hover:bg-white/90 transition-colors'
|
||||
'flex flex-1 items-center justify-center gap-2 rounded-md bg-gradient-brand text-white shadow-lg shadow-primary/20 px-3 py-2 text-sm font-medium',
|
||||
'hover:opacity-90 transition-colors'
|
||||
)}
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
|
||||
@@ -136,7 +136,7 @@ export function StepForm({ onSubmit, onCancel, initialData }: StepFormProps) {
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
{/* Step Type */}
|
||||
<div>
|
||||
<label className="mb-2 block text-sm font-medium text-white">
|
||||
<label className="mb-2 block text-sm font-medium text-foreground">
|
||||
Step Type <span className="text-red-400">*</span>
|
||||
</label>
|
||||
<div className="grid grid-cols-3 gap-2">
|
||||
@@ -150,15 +150,15 @@ export function StepForm({ onSubmit, onCancel, initialData }: StepFormProps) {
|
||||
className={cn(
|
||||
'rounded-lg border p-3 text-left transition-colors',
|
||||
stepType === option.value
|
||||
? 'border-white/20 bg-white/10 ring-2 ring-white/20'
|
||||
: 'border-white/[0.06] hover:border-white/20'
|
||||
? 'border-border bg-accent ring-2 ring-primary/20'
|
||||
: 'border-border hover:border-border'
|
||||
)}
|
||||
>
|
||||
<div className="mb-1 flex items-center gap-2">
|
||||
<Icon className="h-4 w-4" />
|
||||
<span className="font-medium text-sm text-white">{option.label}</span>
|
||||
<span className="font-medium text-sm text-foreground">{option.label}</span>
|
||||
</div>
|
||||
<p className="text-xs text-white/40">{option.description}</p>
|
||||
<p className="text-xs text-muted-foreground">{option.description}</p>
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
@@ -167,7 +167,7 @@ export function StepForm({ onSubmit, onCancel, initialData }: StepFormProps) {
|
||||
|
||||
{/* Title */}
|
||||
<div>
|
||||
<label htmlFor="title" className="mb-2 block text-sm font-medium text-white">
|
||||
<label htmlFor="title" className="mb-2 block text-sm font-medium text-foreground">
|
||||
Title <span className="text-red-400">*</span>
|
||||
</label>
|
||||
<input
|
||||
@@ -177,8 +177,8 @@ export function StepForm({ onSubmit, onCancel, initialData }: StepFormProps) {
|
||||
onChange={(e) => setTitle(e.target.value)}
|
||||
placeholder="Enter step title"
|
||||
className={cn(
|
||||
'w-full rounded-md border bg-black/50 px-3 py-2 text-sm text-white focus:outline-none focus:border-white/30 focus:ring-1 focus:ring-white/20',
|
||||
errors.title ? 'border-red-400/50' : 'border-white/10'
|
||||
'w-full rounded-md border bg-card px-3 py-2 text-sm text-foreground focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20',
|
||||
errors.title ? 'border-red-400/50' : 'border-border'
|
||||
)}
|
||||
/>
|
||||
{errors.title && (
|
||||
@@ -188,9 +188,9 @@ export function StepForm({ onSubmit, onCancel, initialData }: StepFormProps) {
|
||||
|
||||
{/* Instructions */}
|
||||
<div>
|
||||
<label htmlFor="instructions" className="mb-2 block text-sm font-medium text-white">
|
||||
<label htmlFor="instructions" className="mb-2 block text-sm font-medium text-foreground">
|
||||
Instructions <span className="text-red-400">*</span>
|
||||
<span className="ml-2 text-xs font-normal text-white/40">(Markdown supported)</span>
|
||||
<span className="ml-2 text-xs font-normal text-muted-foreground">(Markdown supported)</span>
|
||||
</label>
|
||||
<textarea
|
||||
id="instructions"
|
||||
@@ -199,8 +199,8 @@ export function StepForm({ onSubmit, onCancel, initialData }: StepFormProps) {
|
||||
placeholder="Describe what to do in this step..."
|
||||
rows={6}
|
||||
className={cn(
|
||||
'w-full rounded-md border bg-black/50 px-3 py-2 text-sm text-white focus:outline-none focus:border-white/30 focus:ring-1 focus:ring-white/20',
|
||||
errors.instructions ? 'border-red-400/50' : 'border-white/10'
|
||||
'w-full rounded-md border bg-card px-3 py-2 text-sm text-foreground focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20',
|
||||
errors.instructions ? 'border-red-400/50' : 'border-border'
|
||||
)}
|
||||
/>
|
||||
{errors.instructions && (
|
||||
@@ -210,8 +210,8 @@ export function StepForm({ onSubmit, onCancel, initialData }: StepFormProps) {
|
||||
|
||||
{/* Help Text */}
|
||||
<div>
|
||||
<label htmlFor="helpText" className="mb-2 block text-sm font-medium text-white">
|
||||
Help Text <span className="text-xs font-normal text-white/40">(Optional)</span>
|
||||
<label htmlFor="helpText" className="mb-2 block text-sm font-medium text-foreground">
|
||||
Help Text <span className="text-xs font-normal text-muted-foreground">(Optional)</span>
|
||||
</label>
|
||||
<textarea
|
||||
id="helpText"
|
||||
@@ -219,20 +219,20 @@ export function StepForm({ onSubmit, onCancel, initialData }: StepFormProps) {
|
||||
onChange={(e) => setHelpText(e.target.value)}
|
||||
placeholder="Additional context or tips..."
|
||||
rows={3}
|
||||
className="w-full rounded-md border border-white/10 bg-black/50 px-3 py-2 text-sm text-white focus:outline-none focus:border-white/30 focus:ring-1 focus:ring-white/20"
|
||||
className="w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Commands */}
|
||||
<div>
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<label className="text-sm font-medium text-white">
|
||||
Commands <span className="text-xs font-normal text-white/40">(Optional)</span>
|
||||
<label className="text-sm font-medium text-foreground">
|
||||
Commands <span className="text-xs font-normal text-muted-foreground">(Optional)</span>
|
||||
</label>
|
||||
<button
|
||||
type="button"
|
||||
onClick={addCommand}
|
||||
className="flex items-center gap-1 rounded-md bg-white/10 px-2 py-1 text-xs font-medium text-white/70 hover:bg-white/20 hover:text-white"
|
||||
className="flex items-center gap-1 rounded-md bg-accent px-2 py-1 text-xs font-medium text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||
>
|
||||
<Plus className="h-3 w-3" />
|
||||
Add Command
|
||||
@@ -241,13 +241,13 @@ export function StepForm({ onSubmit, onCancel, initialData }: StepFormProps) {
|
||||
{commands.length > 0 && (
|
||||
<div className="space-y-3">
|
||||
{commands.map((cmd, index) => (
|
||||
<div key={index} className="rounded-lg border border-white/[0.06] bg-white/5 p-3">
|
||||
<div key={index} className="rounded-lg border border-border bg-accent/50 p-3">
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<span className="text-xs font-medium text-white/40">Command {index + 1}</span>
|
||||
<span className="text-xs font-medium text-muted-foreground">Command {index + 1}</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => removeCommand(index)}
|
||||
className="rounded p-1 text-white/40 hover:bg-red-400/10 hover:text-red-400"
|
||||
className="rounded p-1 text-muted-foreground hover:bg-red-400/10 hover:text-red-400"
|
||||
>
|
||||
<X className="h-3 w-3" />
|
||||
</button>
|
||||
@@ -259,8 +259,8 @@ export function StepForm({ onSubmit, onCancel, initialData }: StepFormProps) {
|
||||
onChange={(e) => updateCommand(index, 'label', e.target.value)}
|
||||
placeholder="Command label (e.g., 'Restart service')"
|
||||
className={cn(
|
||||
'w-full rounded-md border bg-black/50 px-3 py-1.5 text-sm text-white',
|
||||
errors[`command_${index}_label`] ? 'border-red-400/50' : 'border-white/10'
|
||||
'w-full rounded-md border bg-card px-3 py-1.5 text-sm text-foreground',
|
||||
errors[`command_${index}_label`] ? 'border-red-400/50' : 'border-border'
|
||||
)}
|
||||
/>
|
||||
<input
|
||||
@@ -269,8 +269,8 @@ export function StepForm({ onSubmit, onCancel, initialData }: StepFormProps) {
|
||||
onChange={(e) => updateCommand(index, 'command', e.target.value)}
|
||||
placeholder="Command (e.g., 'systemctl restart nginx')"
|
||||
className={cn(
|
||||
'w-full rounded-md border bg-black/50 px-3 py-1.5 font-mono text-sm text-white',
|
||||
errors[`command_${index}_command`] ? 'border-red-400/50' : 'border-white/10'
|
||||
'w-full rounded-md border bg-card px-3 py-1.5 font-mono text-sm text-foreground',
|
||||
errors[`command_${index}_command`] ? 'border-red-400/50' : 'border-border'
|
||||
)}
|
||||
/>
|
||||
{(errors[`command_${index}_label`] || errors[`command_${index}_command`]) && (
|
||||
@@ -287,14 +287,14 @@ export function StepForm({ onSubmit, onCancel, initialData }: StepFormProps) {
|
||||
|
||||
{/* Category */}
|
||||
<div>
|
||||
<label htmlFor="category" className="mb-2 block text-sm font-medium text-white">
|
||||
Category <span className="text-xs font-normal text-white/40">(Optional)</span>
|
||||
<label htmlFor="category" className="mb-2 block text-sm font-medium text-foreground">
|
||||
Category <span className="text-xs font-normal text-muted-foreground">(Optional)</span>
|
||||
</label>
|
||||
<select
|
||||
id="category"
|
||||
value={categoryId}
|
||||
onChange={(e) => setCategoryId(e.target.value)}
|
||||
className="w-full rounded-md border border-white/10 bg-black/50 px-3 py-2 text-sm text-white focus:outline-none focus:border-white/30 focus:ring-1 focus:ring-white/20"
|
||||
className="w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20"
|
||||
>
|
||||
<option value="">None</option>
|
||||
{categories.map(cat => (
|
||||
@@ -305,8 +305,8 @@ export function StepForm({ onSubmit, onCancel, initialData }: StepFormProps) {
|
||||
|
||||
{/* Tags */}
|
||||
<div>
|
||||
<label htmlFor="tagInput" className="mb-2 block text-sm font-medium text-white">
|
||||
Tags <span className="text-xs font-normal text-white/40">(Optional)</span>
|
||||
<label htmlFor="tagInput" className="mb-2 block text-sm font-medium text-foreground">
|
||||
Tags <span className="text-xs font-normal text-muted-foreground">(Optional)</span>
|
||||
</label>
|
||||
<div className="flex gap-2">
|
||||
<input
|
||||
@@ -316,12 +316,12 @@ export function StepForm({ onSubmit, onCancel, initialData }: StepFormProps) {
|
||||
onChange={(e) => setTagInput(e.target.value)}
|
||||
onKeyDown={handleTagInputKeyDown}
|
||||
placeholder="Type tag and press Enter"
|
||||
className="flex-1 rounded-md border border-white/10 bg-black/50 px-3 py-2 text-sm text-white focus:outline-none focus:border-white/30 focus:ring-1 focus:ring-white/20"
|
||||
className="flex-1 rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={addTag}
|
||||
className="rounded-md bg-white/10 px-4 py-2 text-sm font-medium text-white/70 hover:bg-white/20 hover:text-white"
|
||||
className="rounded-md bg-accent px-4 py-2 text-sm font-medium text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||
>
|
||||
Add
|
||||
</button>
|
||||
@@ -331,13 +331,13 @@ export function StepForm({ onSubmit, onCancel, initialData }: StepFormProps) {
|
||||
{tags.map(tag => (
|
||||
<span
|
||||
key={tag}
|
||||
className="flex items-center gap-1 rounded-full bg-white/10 px-2.5 py-1 text-xs text-white/70"
|
||||
className="flex items-center gap-1 rounded-full bg-accent px-2.5 py-1 text-xs text-muted-foreground"
|
||||
>
|
||||
{tag}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => removeTag(tag)}
|
||||
className="rounded-full hover:bg-white/20"
|
||||
className="rounded-full hover:bg-accent"
|
||||
aria-label={`Remove tag ${tag}`}
|
||||
>
|
||||
<X className="h-3 w-3" />
|
||||
@@ -350,14 +350,14 @@ export function StepForm({ onSubmit, onCancel, initialData }: StepFormProps) {
|
||||
|
||||
{/* Visibility */}
|
||||
<div>
|
||||
<label htmlFor="visibility" className="mb-2 block text-sm font-medium text-white">
|
||||
<label htmlFor="visibility" className="mb-2 block text-sm font-medium text-foreground">
|
||||
Visibility
|
||||
</label>
|
||||
<select
|
||||
id="visibility"
|
||||
value={visibility}
|
||||
onChange={(e) => setVisibility(e.target.value as 'private' | 'team' | 'public')}
|
||||
className="w-full rounded-md border border-white/10 bg-black/50 px-3 py-2 text-sm text-white focus:outline-none focus:border-white/30 focus:ring-1 focus:ring-white/20"
|
||||
className="w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20"
|
||||
>
|
||||
<option value="private">Private (only me)</option>
|
||||
<option value="team">Team (my team members)</option>
|
||||
@@ -370,13 +370,13 @@ export function StepForm({ onSubmit, onCancel, initialData }: StepFormProps) {
|
||||
<button
|
||||
type="button"
|
||||
onClick={onCancel}
|
||||
className="flex-1 rounded-md border border-white/10 px-4 py-2 text-sm font-medium text-white/60 hover:bg-white/10 hover:text-white"
|
||||
className="flex-1 rounded-md border border-border px-4 py-2 text-sm font-medium text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
className="flex-1 rounded-md bg-white px-4 py-2 text-sm font-medium text-black hover:bg-white/90"
|
||||
className="flex-1 rounded-md bg-gradient-brand text-white shadow-lg shadow-primary/20 px-4 py-2 text-sm font-medium hover:opacity-90"
|
||||
>
|
||||
Insert Step
|
||||
</button>
|
||||
|
||||
@@ -51,7 +51,7 @@ export function DynamicArrayField<T>({
|
||||
type="button"
|
||||
onClick={() => handleMoveUp(index)}
|
||||
disabled={index === 0}
|
||||
className="rounded p-0.5 text-white/50 hover:bg-white/[0.06] hover:text-white disabled:opacity-30"
|
||||
className="rounded p-0.5 text-muted-foreground hover:bg-accent hover:text-foreground disabled:opacity-30"
|
||||
title="Move up"
|
||||
aria-label="Move up"
|
||||
>
|
||||
@@ -61,7 +61,7 @@ export function DynamicArrayField<T>({
|
||||
type="button"
|
||||
onClick={() => handleMoveDown(index)}
|
||||
disabled={index === items.length - 1}
|
||||
className="rounded p-0.5 text-white/50 hover:bg-white/[0.06] hover:text-white disabled:opacity-30"
|
||||
className="rounded p-0.5 text-muted-foreground hover:bg-accent hover:text-foreground disabled:opacity-30"
|
||||
title="Move down"
|
||||
aria-label="Move down"
|
||||
>
|
||||
@@ -78,7 +78,7 @@ export function DynamicArrayField<T>({
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onRemove(index)}
|
||||
className="mt-1 rounded p-1 text-white/50 hover:bg-red-400/20 hover:text-red-400"
|
||||
className="mt-1 rounded p-1 text-muted-foreground hover:bg-red-400/20 hover:text-red-400"
|
||||
title="Remove"
|
||||
aria-label="Remove"
|
||||
>
|
||||
@@ -94,9 +94,9 @@ export function DynamicArrayField<T>({
|
||||
type="button"
|
||||
onClick={onAdd}
|
||||
className={cn(
|
||||
'flex w-full items-center justify-center gap-1 rounded-md border border-dashed border-white/10',
|
||||
'px-3 py-2 text-sm text-white/50',
|
||||
'hover:border-white/30 hover:text-white'
|
||||
'flex w-full items-center justify-center gap-1 rounded-md border border-dashed border-border',
|
||||
'px-3 py-2 text-sm text-muted-foreground',
|
||||
'hover:border-border hover:text-foreground'
|
||||
)}
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
@@ -106,7 +106,7 @@ export function DynamicArrayField<T>({
|
||||
|
||||
{/* Empty state */}
|
||||
{items.length === 0 && !canAdd && (
|
||||
<p className="text-center text-sm text-white/40">No items</p>
|
||||
<p className="text-center text-sm text-muted-foreground">No items</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -68,14 +68,14 @@ export function NodeEditorModal({ node, onClose, isNewNode = false }: NodeEditor
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleCancel}
|
||||
className="rounded-md border border-white/10 px-4 py-2 text-sm font-medium text-white/60 hover:bg-white/10 hover:text-white"
|
||||
className="rounded-md border border-border px-4 py-2 text-sm font-medium text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleSave}
|
||||
className="rounded-md bg-white px-4 py-2 text-sm font-medium text-black hover:bg-white/90"
|
||||
className="rounded-md bg-gradient-brand text-white shadow-lg shadow-primary/20 px-4 py-2 text-sm font-medium hover:opacity-90"
|
||||
>
|
||||
Done
|
||||
</button>
|
||||
@@ -85,8 +85,8 @@ export function NodeEditorModal({ node, onClose, isNewNode = false }: NodeEditor
|
||||
return (
|
||||
<Modal isOpen={true} onClose={onClose} title={getTitle()} size="lg" footer={footerContent}>
|
||||
{/* Node ID display */}
|
||||
<div className="mb-4 text-xs text-white/40">
|
||||
Node ID: <code className="rounded bg-white/10 px-1 py-0.5">{node.id}</code>
|
||||
<div className="mb-4 text-xs text-muted-foreground">
|
||||
Node ID: <code className="rounded bg-accent px-1 py-0.5">{node.id}</code>
|
||||
</div>
|
||||
|
||||
{/* Validation errors */}
|
||||
|
||||
@@ -52,7 +52,7 @@ export function NodeFormAction({ node, onUpdate }: NodeFormActionProps) {
|
||||
<div className="space-y-4">
|
||||
{/* Title */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-white">
|
||||
<label className="block text-sm font-medium text-foreground">
|
||||
Title <span className="text-red-400">*</span>
|
||||
</label>
|
||||
<input
|
||||
@@ -62,9 +62,9 @@ export function NodeFormAction({ node, onUpdate }: NodeFormActionProps) {
|
||||
placeholder="e.g., Restart the Service"
|
||||
className={cn(
|
||||
'mt-1 block w-full rounded-md border px-3 py-2 text-sm',
|
||||
'bg-black/50 text-white placeholder:text-white/40',
|
||||
'focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20',
|
||||
titleError ? 'border-red-400' : 'border-white/10'
|
||||
'bg-card text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
|
||||
titleError ? 'border-red-400' : 'border-border'
|
||||
)}
|
||||
/>
|
||||
{titleError && (
|
||||
@@ -75,24 +75,24 @@ export function NodeFormAction({ node, onUpdate }: NodeFormActionProps) {
|
||||
{/* Description */}
|
||||
<div>
|
||||
<div className="flex items-center justify-between">
|
||||
<label className="block text-sm font-medium text-white">
|
||||
<label className="block text-sm font-medium text-foreground">
|
||||
Description
|
||||
</label>
|
||||
{node.description && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowPreview(!showPreview)}
|
||||
className="text-xs text-white/50 hover:text-white hover:underline"
|
||||
className="text-xs text-muted-foreground hover:text-foreground hover:underline"
|
||||
>
|
||||
{showPreview ? 'Edit' : 'Preview'}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<p className="mb-1 text-xs text-white/40">
|
||||
<p className="mb-1 text-xs text-muted-foreground">
|
||||
Supports markdown: **bold**, *italic*, - lists, 1. numbered lists, `code`
|
||||
</p>
|
||||
{showPreview && node.description ? (
|
||||
<div className="mt-1 rounded-md border border-white/10 bg-white/[0.04] p-3 text-sm">
|
||||
<div className="mt-1 rounded-md border border-border bg-accent/50 p-3 text-sm">
|
||||
<MarkdownContent content={node.description} />
|
||||
</div>
|
||||
) : (
|
||||
@@ -108,7 +108,7 @@ export function NodeFormAction({ node, onUpdate }: NodeFormActionProps) {
|
||||
**Note:** Important information here"
|
||||
rows={5}
|
||||
className={cn(
|
||||
'mt-1 block w-full rounded-md border border-white/10 px-3 py-2 text-sm',
|
||||
'mt-1 block w-full rounded-md border border-border px-3 py-2 text-sm',
|
||||
'bg-background text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary'
|
||||
)}
|
||||
@@ -118,10 +118,10 @@ export function NodeFormAction({ node, onUpdate }: NodeFormActionProps) {
|
||||
|
||||
{/* Commands */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-white">
|
||||
<label className="block text-sm font-medium text-foreground">
|
||||
Commands
|
||||
</label>
|
||||
<p className="mb-2 text-xs text-white/40">
|
||||
<p className="mb-2 text-xs text-muted-foreground">
|
||||
PowerShell or CLI commands to execute
|
||||
</p>
|
||||
<DynamicArrayField
|
||||
@@ -137,7 +137,7 @@ export function NodeFormAction({ node, onUpdate }: NodeFormActionProps) {
|
||||
onChange={(e) => handleUpdateCommand(index, e.target.value)}
|
||||
placeholder="e.g., Get-Service BrokerAgent"
|
||||
className={cn(
|
||||
'block w-full rounded-md border border-white/10 px-3 py-2 font-mono text-sm',
|
||||
'block w-full rounded-md border border-border px-3 py-2 font-mono text-sm',
|
||||
'bg-background text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary'
|
||||
)}
|
||||
@@ -148,7 +148,7 @@ export function NodeFormAction({ node, onUpdate }: NodeFormActionProps) {
|
||||
|
||||
{/* Expected Outcome */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-white">
|
||||
<label className="block text-sm font-medium text-foreground">
|
||||
Expected Outcome
|
||||
</label>
|
||||
<input
|
||||
@@ -157,7 +157,7 @@ export function NodeFormAction({ node, onUpdate }: NodeFormActionProps) {
|
||||
onChange={(e) => onUpdate({ expected_outcome: e.target.value })}
|
||||
placeholder="e.g., Service should show as Running"
|
||||
className={cn(
|
||||
'mt-1 block w-full rounded-md border border-white/10 px-3 py-2 text-sm',
|
||||
'mt-1 block w-full rounded-md border border-border px-3 py-2 text-sm',
|
||||
'bg-background text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary'
|
||||
)}
|
||||
|
||||
@@ -67,7 +67,7 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
|
||||
<h3 className="font-semibold text-blue-400">
|
||||
Starting Question
|
||||
</h3>
|
||||
<p className="mt-1 text-sm text-white/40">
|
||||
<p className="mt-1 text-sm text-muted-foreground">
|
||||
This is the first question users will see when they start this troubleshooting tree.
|
||||
Each option below creates a different troubleshooting path.
|
||||
</p>
|
||||
@@ -78,11 +78,11 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
|
||||
|
||||
{/* Question */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-white">
|
||||
<label className="block text-sm font-medium text-foreground">
|
||||
{isRootNode ? 'Starting Question' : 'Question'} <span className="text-red-400">*</span>
|
||||
</label>
|
||||
{isRootNode && (
|
||||
<p className="mt-0.5 text-xs text-white/40">
|
||||
<p className="mt-0.5 text-xs text-muted-foreground">
|
||||
What's the main question to diagnose the issue?
|
||||
</p>
|
||||
)}
|
||||
@@ -95,9 +95,9 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
|
||||
: "e.g., Can you ping the server?"}
|
||||
className={cn(
|
||||
'mt-1 block w-full rounded-md border px-3 py-2 text-sm',
|
||||
'bg-black/50 text-white placeholder:text-white/40',
|
||||
'focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20',
|
||||
questionError ? 'border-red-400' : 'border-white/10'
|
||||
'bg-card text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
|
||||
questionError ? 'border-red-400' : 'border-border'
|
||||
)}
|
||||
/>
|
||||
{questionError && (
|
||||
@@ -107,7 +107,7 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
|
||||
|
||||
{/* Help Text */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-white">
|
||||
<label className="block text-sm font-medium text-foreground">
|
||||
Help Text
|
||||
</label>
|
||||
<textarea
|
||||
@@ -116,7 +116,7 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
|
||||
placeholder="Additional context or instructions for this decision..."
|
||||
rows={2}
|
||||
className={cn(
|
||||
'mt-1 block w-full rounded-md border border-white/10 px-3 py-2 text-sm',
|
||||
'mt-1 block w-full rounded-md border border-border px-3 py-2 text-sm',
|
||||
'bg-background text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary'
|
||||
)}
|
||||
@@ -125,15 +125,15 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
|
||||
|
||||
{/* Options */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-white">
|
||||
<label className="block text-sm font-medium text-foreground">
|
||||
{isRootNode ? 'Answer Options (Branches)' : 'Options'} <span className="text-red-400">*</span>
|
||||
</label>
|
||||
{isRootNode ? (
|
||||
<p className="mt-0.5 text-xs text-white/40">
|
||||
<p className="mt-0.5 text-xs text-muted-foreground">
|
||||
Add as many options as needed (A, B, C, D...). Each option leads to a completely different troubleshooting path.
|
||||
</p>
|
||||
) : (
|
||||
<p className="mt-0.5 text-xs text-white/40">
|
||||
<p className="mt-0.5 text-xs text-muted-foreground">
|
||||
Each option can branch to a different next step.
|
||||
</p>
|
||||
)}
|
||||
@@ -158,14 +158,14 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
|
||||
const letter = indexToLetter(index)
|
||||
|
||||
return (
|
||||
<div className="rounded-md border border-white/10 bg-white/[0.04] p-3">
|
||||
<div className="rounded-md border border-border bg-accent/50 p-3">
|
||||
<div className="mb-2 flex items-center gap-2">
|
||||
{/* Letter badge */}
|
||||
<span className={cn(
|
||||
'flex h-6 w-6 items-center justify-center rounded-full text-xs font-bold',
|
||||
isRootNode
|
||||
? 'bg-blue-500/20 text-blue-400'
|
||||
: 'bg-white/10 text-white/50'
|
||||
: 'bg-accent text-muted-foreground'
|
||||
)}>
|
||||
{letter}
|
||||
</span>
|
||||
@@ -180,7 +180,7 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
|
||||
'block flex-1 rounded-md border px-3 py-2 text-sm',
|
||||
'bg-background text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary',
|
||||
optionLabelError ? 'border-red-400' : 'border-white/10'
|
||||
optionLabelError ? 'border-red-400' : 'border-border'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
@@ -207,7 +207,7 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
|
||||
|
||||
{/* Example hint for root node */}
|
||||
{isRootNode && (node.options?.length || 0) < 2 && (
|
||||
<div className="mt-3 rounded-md border border-dashed border-white/10 bg-white/[0.02] p-3 text-xs text-white/40">
|
||||
<div className="mt-3 rounded-md border border-dashed border-border bg-accent/50 p-3 text-xs text-muted-foreground">
|
||||
<strong>Tip:</strong> Most troubleshooting trees start with 2-5 main branches.
|
||||
For example: "Connection Issues", "Performance Problems", "Error Messages", "Other".
|
||||
</div>
|
||||
|
||||
@@ -47,7 +47,7 @@ export function NodeFormResolution({ node, onUpdate }: NodeFormResolutionProps)
|
||||
<div className="space-y-4">
|
||||
{/* Title */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-white">
|
||||
<label className="block text-sm font-medium text-foreground">
|
||||
Title <span className="text-red-400">*</span>
|
||||
</label>
|
||||
<input
|
||||
@@ -57,9 +57,9 @@ export function NodeFormResolution({ node, onUpdate }: NodeFormResolutionProps)
|
||||
placeholder="e.g., VDA Successfully Registered"
|
||||
className={cn(
|
||||
'mt-1 block w-full rounded-md border px-3 py-2 text-sm',
|
||||
'bg-black/50 text-white placeholder:text-white/40',
|
||||
'focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20',
|
||||
titleError ? 'border-red-400' : 'border-white/10'
|
||||
'bg-card text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
|
||||
titleError ? 'border-red-400' : 'border-border'
|
||||
)}
|
||||
/>
|
||||
{titleError && (
|
||||
@@ -70,24 +70,24 @@ export function NodeFormResolution({ node, onUpdate }: NodeFormResolutionProps)
|
||||
{/* Description */}
|
||||
<div>
|
||||
<div className="flex items-center justify-between">
|
||||
<label className="block text-sm font-medium text-white">
|
||||
<label className="block text-sm font-medium text-foreground">
|
||||
Description
|
||||
</label>
|
||||
{node.description && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowPreview(!showPreview)}
|
||||
className="text-xs text-white/50 hover:text-white hover:underline"
|
||||
className="text-xs text-muted-foreground hover:text-foreground hover:underline"
|
||||
>
|
||||
{showPreview ? 'Edit' : 'Preview'}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<p className="mb-1 text-xs text-white/40">
|
||||
<p className="mb-1 text-xs text-muted-foreground">
|
||||
Supports markdown: **bold**, *italic*, - lists, 1. numbered lists, `code`
|
||||
</p>
|
||||
{showPreview && node.description ? (
|
||||
<div className="mt-1 rounded-md border border-white/10 bg-white/[0.04] p-3 text-sm">
|
||||
<div className="mt-1 rounded-md border border-border bg-accent/50 p-3 text-sm">
|
||||
<MarkdownContent content={node.description} />
|
||||
</div>
|
||||
) : (
|
||||
@@ -102,7 +102,7 @@ Document what was done and the outcome.
|
||||
**Close ticket as:** Resolved"
|
||||
rows={5}
|
||||
className={cn(
|
||||
'mt-1 block w-full rounded-md border border-white/10 px-3 py-2 text-sm',
|
||||
'mt-1 block w-full rounded-md border border-border px-3 py-2 text-sm',
|
||||
'bg-background text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary'
|
||||
)}
|
||||
@@ -112,10 +112,10 @@ Document what was done and the outcome.
|
||||
|
||||
{/* Resolution Steps */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-white">
|
||||
<label className="block text-sm font-medium text-foreground">
|
||||
Resolution Steps
|
||||
</label>
|
||||
<p className="mb-2 text-xs text-white/40">
|
||||
<p className="mb-2 text-xs text-muted-foreground">
|
||||
Step-by-step instructions for resolving the issue
|
||||
</p>
|
||||
<DynamicArrayField
|
||||
@@ -135,7 +135,7 @@ Document what was done and the outcome.
|
||||
onChange={(e) => handleUpdateStep(index, e.target.value)}
|
||||
placeholder={`Step ${index + 1}`}
|
||||
className={cn(
|
||||
'block w-full rounded-md border border-white/10 px-3 py-2 text-sm',
|
||||
'block w-full rounded-md border border-border px-3 py-2 text-sm',
|
||||
'bg-background text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary'
|
||||
)}
|
||||
|
||||
@@ -95,9 +95,9 @@ function NodeListItem({
|
||||
}
|
||||
|
||||
const nodeTypeColors: Record<NodeType, string> = {
|
||||
decision: 'bg-blue-500/20 text-blue-600 dark:text-blue-400',
|
||||
action: 'bg-yellow-500/20 text-yellow-600 dark:text-yellow-400',
|
||||
solution: 'bg-green-500/20 text-green-600 dark:text-green-400'
|
||||
decision: 'bg-blue-500/20 text-blue-400',
|
||||
action: 'bg-yellow-500/20 text-yellow-400',
|
||||
solution: 'bg-green-500/20 text-green-400'
|
||||
}
|
||||
|
||||
const getNodeLabel = () => {
|
||||
@@ -223,7 +223,7 @@ function NodeListItem({
|
||||
|
||||
{/* Node type icon - special treatment for root */}
|
||||
{isRootNode ? (
|
||||
<span className="flex items-center gap-1 rounded px-1.5 py-0.5 text-xs bg-blue-500/30 text-blue-600 dark:text-blue-400 font-semibold">
|
||||
<span className="flex items-center gap-1 rounded px-1.5 py-0.5 text-xs bg-blue-500/30 text-blue-400 font-semibold">
|
||||
<Play className="h-4 w-4" />
|
||||
<span className="hidden sm:inline">START</span>
|
||||
</span>
|
||||
@@ -254,7 +254,7 @@ function NodeListItem({
|
||||
'flex items-center gap-1 rounded px-1.5 py-0.5 text-xs',
|
||||
hasError
|
||||
? 'bg-destructive/20 text-destructive'
|
||||
: 'bg-yellow-500/20 text-yellow-600 dark:text-yellow-500'
|
||||
: 'bg-yellow-500/20 text-yellow-500'
|
||||
)}
|
||||
>
|
||||
{hasError ? (
|
||||
|
||||
@@ -28,12 +28,12 @@ export function TreeEditorLayout({ isMobile = false }: TreeEditorLayoutProps) {
|
||||
<>
|
||||
{/* Code Mode: Monaco editor (60%) + Preview (40%) */}
|
||||
<div className={cn(
|
||||
'flex flex-col overflow-hidden border-white/[0.06]',
|
||||
'flex flex-col overflow-hidden border-border',
|
||||
isMobile ? 'h-full w-full border-b' : 'w-3/5 border-r'
|
||||
)}>
|
||||
<Suspense fallback={
|
||||
<div className="flex h-full items-center justify-center bg-[#0a0a0a]">
|
||||
<div className="h-6 w-6 animate-spin rounded-full border-2 border-white/20 border-t-white" />
|
||||
<div className="flex h-full items-center justify-center bg-card">
|
||||
<div className="h-6 w-6 animate-spin rounded-full border-2 border-border border-t-foreground" />
|
||||
</div>
|
||||
}>
|
||||
<CodeModeEditor />
|
||||
@@ -42,7 +42,7 @@ export function TreeEditorLayout({ isMobile = false }: TreeEditorLayoutProps) {
|
||||
|
||||
{/* Right Panel - Preview */}
|
||||
<div className={cn(
|
||||
'flex-1 overflow-hidden bg-white/[0.02]',
|
||||
'flex-1 overflow-hidden bg-accent/50',
|
||||
isMobile ? 'hidden' : 'block'
|
||||
)}>
|
||||
<TreePreviewPanel />
|
||||
@@ -52,7 +52,7 @@ export function TreeEditorLayout({ isMobile = false }: TreeEditorLayoutProps) {
|
||||
<>
|
||||
{/* Flow Mode: Form editor (60%) + Preview (40%) */}
|
||||
<div className={cn(
|
||||
'flex flex-col overflow-y-auto border-white/[0.06]',
|
||||
'flex flex-col overflow-y-auto border-border',
|
||||
isMobile ? 'h-full w-full border-b' : 'w-3/5 border-r'
|
||||
)}>
|
||||
<div className="space-y-4 p-4">
|
||||
@@ -63,7 +63,7 @@ export function TreeEditorLayout({ isMobile = false }: TreeEditorLayoutProps) {
|
||||
|
||||
{/* Right Panel - Preview */}
|
||||
<div className={cn(
|
||||
'flex-1 overflow-hidden bg-white/[0.02]',
|
||||
'flex-1 overflow-hidden bg-accent/50',
|
||||
isMobile ? 'hidden' : 'block'
|
||||
)}>
|
||||
<TreePreviewPanel />
|
||||
|
||||
@@ -35,7 +35,7 @@ export function ValidationSummary({ errors, onSelectNode }: ValidationSummaryPro
|
||||
<button
|
||||
onClick={() => setIsExpanded(!isExpanded)}
|
||||
className={cn(
|
||||
'flex w-full items-center justify-between p-3 text-left transition-colors hover:bg-white/5',
|
||||
'flex w-full items-center justify-between p-3 text-left transition-colors hover:bg-accent',
|
||||
errorItems.length > 0 ? 'text-red-400' : 'text-yellow-400'
|
||||
)}
|
||||
>
|
||||
@@ -81,7 +81,7 @@ export function ValidationSummary({ errors, onSelectNode }: ValidationSummaryPro
|
||||
<div className="flex-1">
|
||||
<p className="text-red-400">{error.message}</p>
|
||||
{error.nodeId && (
|
||||
<p className="mt-0.5 text-xs text-white/40">
|
||||
<p className="mt-0.5 text-xs text-muted-foreground">
|
||||
Click to select node: {error.nodeId}
|
||||
</p>
|
||||
)}
|
||||
@@ -105,7 +105,7 @@ export function ValidationSummary({ errors, onSelectNode }: ValidationSummaryPro
|
||||
<div className="flex-1">
|
||||
<p className="text-yellow-400">{warning.message}</p>
|
||||
{warning.nodeId && (
|
||||
<p className="mt-0.5 text-xs text-white/40">
|
||||
<p className="mt-0.5 text-xs text-muted-foreground">
|
||||
Click to select node: {warning.nodeId}
|
||||
</p>
|
||||
)}
|
||||
|
||||
@@ -166,8 +166,8 @@ export function CodeModeEditor() {
|
||||
beforeMount={handleEditorWillMount}
|
||||
onMount={handleEditorDidMount}
|
||||
loading={
|
||||
<div className="flex h-full items-center justify-center bg-[#0a0a0a]">
|
||||
<div className="h-6 w-6 animate-spin rounded-full border-2 border-white/20 border-t-white" />
|
||||
<div className="flex h-full items-center justify-center bg-card">
|
||||
<div className="h-6 w-6 animate-spin rounded-full border-2 border-border border-t-foreground" />
|
||||
</div>
|
||||
}
|
||||
options={{
|
||||
|
||||
@@ -91,15 +91,15 @@ export function CodeModeToolbar({
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between border-b border-white/[0.06] bg-black/50 px-3 py-1.5">
|
||||
<div className="flex items-center justify-between border-b border-border bg-card px-3 py-1.5">
|
||||
<div className="flex items-center gap-2">
|
||||
{/* Insert Node dropdown */}
|
||||
<div className="relative" ref={dropdownRef}>
|
||||
<button
|
||||
onClick={() => setInsertOpen(!insertOpen)}
|
||||
className={cn(
|
||||
'flex items-center gap-1 rounded-md border border-white/10 px-2.5 py-1 text-xs font-medium text-white/60',
|
||||
'hover:bg-white/10 hover:text-white'
|
||||
'flex items-center gap-1 rounded-md border border-border px-2.5 py-1 text-xs font-medium text-muted-foreground',
|
||||
'hover:bg-accent hover:text-foreground'
|
||||
)}
|
||||
>
|
||||
<Plus className="h-3 w-3" />
|
||||
@@ -107,24 +107,24 @@ export function CodeModeToolbar({
|
||||
<ChevronDown className="h-3 w-3" />
|
||||
</button>
|
||||
{insertOpen && (
|
||||
<div className="absolute left-0 top-full z-50 mt-1 w-44 rounded-lg border border-white/10 bg-[#111] py-1 shadow-xl">
|
||||
<div className="absolute left-0 top-full z-50 mt-1 w-44 rounded-lg border border-border bg-card py-1 shadow-xl">
|
||||
<button
|
||||
onClick={() => { onInsertTemplate(NODE_TEMPLATES.decision); setInsertOpen(false) }}
|
||||
className="flex w-full items-center gap-2 px-3 py-1.5 text-xs text-white/70 hover:bg-white/10"
|
||||
className="flex w-full items-center gap-2 px-3 py-1.5 text-xs text-muted-foreground hover:bg-accent"
|
||||
>
|
||||
<span className="h-2 w-2 rounded-full bg-blue-400" />
|
||||
Decision
|
||||
</button>
|
||||
<button
|
||||
onClick={() => { onInsertTemplate(NODE_TEMPLATES.action); setInsertOpen(false) }}
|
||||
className="flex w-full items-center gap-2 px-3 py-1.5 text-xs text-white/70 hover:bg-white/10"
|
||||
className="flex w-full items-center gap-2 px-3 py-1.5 text-xs text-muted-foreground hover:bg-accent"
|
||||
>
|
||||
<span className="h-2 w-2 rounded-full bg-amber-400" />
|
||||
Action
|
||||
</button>
|
||||
<button
|
||||
onClick={() => { onInsertTemplate(NODE_TEMPLATES.solution); setInsertOpen(false) }}
|
||||
className="flex w-full items-center gap-2 px-3 py-1.5 text-xs text-white/70 hover:bg-white/10"
|
||||
className="flex w-full items-center gap-2 px-3 py-1.5 text-xs text-muted-foreground hover:bg-accent"
|
||||
>
|
||||
<span className="h-2 w-2 rounded-full bg-emerald-400" />
|
||||
Solution
|
||||
@@ -139,8 +139,8 @@ export function CodeModeToolbar({
|
||||
<div className="flex items-center gap-1.5 text-xs">
|
||||
{isValidating ? (
|
||||
<>
|
||||
<Loader2 className="h-3 w-3 animate-spin text-white/40" />
|
||||
<span className="text-white/40">Validating...</span>
|
||||
<Loader2 className="h-3 w-3 animate-spin text-muted-foreground" />
|
||||
<span className="text-muted-foreground">Validating...</span>
|
||||
</>
|
||||
) : isValid ? (
|
||||
<>
|
||||
@@ -173,8 +173,8 @@ export function CodeModeToolbar({
|
||||
className={cn(
|
||||
'flex items-center gap-1 rounded-md px-2 py-1 text-xs',
|
||||
syntaxHelpOpen
|
||||
? 'bg-white/10 text-white'
|
||||
: 'text-white/40 hover:bg-white/[0.06] hover:text-white/60'
|
||||
? 'bg-accent text-foreground'
|
||||
: 'text-muted-foreground hover:bg-accent hover:text-muted-foreground'
|
||||
)}
|
||||
>
|
||||
<HelpCircle className="h-3 w-3" />
|
||||
|
||||
@@ -10,17 +10,17 @@ export function SyntaxHelpPanel({ open, onClose }: SyntaxHelpPanelProps) {
|
||||
if (!open) return null
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col border-l border-white/[0.06] bg-[#0a0a0a]">
|
||||
<div className="flex items-center justify-between border-b border-white/[0.06] px-3 py-2">
|
||||
<span className="text-xs font-medium text-white/60">Syntax Reference</span>
|
||||
<div className="flex h-full flex-col border-l border-border bg-card">
|
||||
<div className="flex items-center justify-between border-b border-border px-3 py-2">
|
||||
<span className="text-xs font-medium text-muted-foreground">Syntax Reference</span>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="rounded p-0.5 text-white/30 hover:bg-white/10 hover:text-white/60"
|
||||
className="rounded p-0.5 text-muted-foreground hover:bg-accent hover:text-muted-foreground"
|
||||
>
|
||||
<X className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex-1 overflow-y-auto px-3 py-3 text-[11px] leading-relaxed text-white/50">
|
||||
<div className="flex-1 overflow-y-auto px-3 py-3 text-[11px] leading-relaxed text-muted-foreground">
|
||||
<Section title="Node Structure">
|
||||
<Code>{`---
|
||||
id: node_id
|
||||
@@ -82,7 +82,7 @@ Description paragraph.
|
||||
function Section({ title, children }: { title: string; children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="mb-4">
|
||||
<h4 className="mb-1.5 text-[11px] font-semibold uppercase tracking-wider text-white/30">{title}</h4>
|
||||
<h4 className="mb-1.5 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground">{title}</h4>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
@@ -91,8 +91,8 @@ function Section({ title, children }: { title: string; children: React.ReactNode
|
||||
function Code({ children }: { children: string }) {
|
||||
return (
|
||||
<pre className={cn(
|
||||
'mb-2 overflow-x-auto rounded-md border border-white/[0.06] bg-black/50 px-2 py-1.5',
|
||||
'text-[10px] leading-relaxed text-white/50 whitespace-pre'
|
||||
'mb-2 overflow-x-auto rounded-md border border-border bg-card px-2 py-1.5',
|
||||
'text-[10px] leading-relaxed text-muted-foreground whitespace-pre'
|
||||
)}>
|
||||
{children}
|
||||
</pre>
|
||||
@@ -102,8 +102,8 @@ function Code({ children }: { children: string }) {
|
||||
function Row({ label, code }: { label: string; code: string }) {
|
||||
return (
|
||||
<div className="flex items-center justify-between py-0.5">
|
||||
<span className="text-white/40">{label}</span>
|
||||
<code className="rounded bg-white/[0.06] px-1 py-0.5 text-[10px] text-white/50">{code}</code>
|
||||
<span className="text-muted-foreground">{label}</span>
|
||||
<code className="rounded bg-accent px-1 py-0.5 text-[10px] text-muted-foreground">{code}</code>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -154,8 +154,8 @@ export function TreePreviewNode({
|
||||
<div className="relative">
|
||||
{/* From option label */}
|
||||
{fromOption && (
|
||||
<div className="mb-1 text-xs font-medium text-white/40">
|
||||
<span className="rounded bg-white/10 px-1.5 py-0.5">{fromOption}</span>
|
||||
<div className="mb-1 text-xs font-medium text-muted-foreground">
|
||||
<span className="rounded bg-accent px-1.5 py-0.5">{fromOption}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -185,7 +185,7 @@ export function TreePreviewNode({
|
||||
className="absolute -top-1.5 -right-1.5 flex items-center justify-center rounded-full bg-green-500/90 p-0.5 shadow-sm"
|
||||
title="This branch leads to a solution"
|
||||
>
|
||||
<CheckCircle className="h-3 w-3 text-white" />
|
||||
<CheckCircle className="h-3 w-3 text-foreground" />
|
||||
</div>
|
||||
)}
|
||||
{/* Root node START header */}
|
||||
@@ -206,12 +206,12 @@ export function TreePreviewNode({
|
||||
<button
|
||||
type="button"
|
||||
onClick={toggleCollapse}
|
||||
className="mt-0.5 rounded p-0.5 hover:bg-white/10"
|
||||
className="mt-0.5 rounded p-0.5 hover:bg-accent"
|
||||
>
|
||||
{isCollapsed ? (
|
||||
<ChevronRight className="h-4 w-4 text-white/50" />
|
||||
<ChevronRight className="h-4 w-4 text-muted-foreground" />
|
||||
) : (
|
||||
<ChevronDown className="h-4 w-4 text-white/50" />
|
||||
<ChevronDown className="h-4 w-4 text-muted-foreground" />
|
||||
)}
|
||||
</button>
|
||||
) : (
|
||||
@@ -222,14 +222,14 @@ export function TreePreviewNode({
|
||||
{isRootNode && <HelpCircle className="h-4 w-4 text-blue-500" />}
|
||||
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-medium text-white leading-tight">
|
||||
<p className="text-sm font-medium text-foreground leading-tight">
|
||||
{getNodeLabel()}
|
||||
</p>
|
||||
|
||||
{/* Node ID with copy button */}
|
||||
<div className="flex items-center gap-1 mt-1">
|
||||
<span
|
||||
className="text-xs text-white/40 cursor-help"
|
||||
className="text-xs text-muted-foreground cursor-help"
|
||||
title={`Full ID: ${node.id}`}
|
||||
>
|
||||
{node.id === 'root' ? 'root' : node.id.slice(0, 8) + '...'}
|
||||
@@ -237,7 +237,7 @@ export function TreePreviewNode({
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleCopyId}
|
||||
className="rounded p-0.5 text-white/40 hover:bg-white/10 hover:text-white"
|
||||
className="rounded p-0.5 text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||
title="Copy full ID"
|
||||
>
|
||||
{copiedId ? (
|
||||
@@ -252,8 +252,8 @@ export function TreePreviewNode({
|
||||
|
||||
{/* Show options for decision nodes */}
|
||||
{node.type === 'decision' && node.options && node.options.length > 0 && (
|
||||
<div className="mt-2 space-y-1 border-t border-white/[0.06] pt-2">
|
||||
<p className="text-[10px] uppercase tracking-wide text-white/40">Options:</p>
|
||||
<div className="mt-2 space-y-1 border-t border-border pt-2">
|
||||
<p className="text-[10px] uppercase tracking-wide text-muted-foreground">Options:</p>
|
||||
{node.options.map((opt, i) => {
|
||||
const leadsToSolution = nodeLeadsToSolution(opt.next_node_id)
|
||||
return (
|
||||
@@ -261,15 +261,15 @@ export function TreePreviewNode({
|
||||
key={opt.id}
|
||||
className={cn(
|
||||
'flex items-center gap-1 text-xs rounded px-1 py-0.5 -mx-1',
|
||||
opt.next_node_id && 'hover:bg-white/[0.06] cursor-pointer'
|
||||
opt.next_node_id && 'hover:bg-accent cursor-pointer'
|
||||
)}
|
||||
onMouseEnter={() => opt.next_node_id && onHoverNodeId?.(opt.next_node_id)}
|
||||
onMouseLeave={() => onHoverNodeId?.(null)}
|
||||
>
|
||||
<span className="inline-flex h-4 w-4 items-center justify-center rounded bg-white/10 text-[10px] font-medium text-white/50">
|
||||
<span className="inline-flex h-4 w-4 items-center justify-center rounded bg-accent text-[10px] font-medium text-muted-foreground">
|
||||
{i + 1}
|
||||
</span>
|
||||
<span className="truncate text-white/70">{opt.label || 'Untitled'}</span>
|
||||
<span className="truncate text-muted-foreground">{opt.label || 'Untitled'}</span>
|
||||
<span className="ml-auto flex items-center gap-1">
|
||||
{leadsToSolution && (
|
||||
<span title="Leads to solution">
|
||||
@@ -279,7 +279,7 @@ export function TreePreviewNode({
|
||||
{opt.next_node_id ? (
|
||||
<span className="text-blue-500">→</span>
|
||||
) : (
|
||||
<span className="text-white/30 text-[10px]">(no link)</span>
|
||||
<span className="text-muted-foreground text-[10px]">(no link)</span>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
@@ -308,12 +308,12 @@ export function TreePreviewNode({
|
||||
|
||||
return (
|
||||
<div
|
||||
className="mt-2 text-xs border-t border-white/[0.06] pt-2 hover:bg-white/[0.04] cursor-pointer rounded px-1 -mx-1"
|
||||
className="mt-2 text-xs border-t border-border pt-2 hover:bg-accent/50 cursor-pointer rounded px-1 -mx-1"
|
||||
onMouseEnter={() => onHoverNodeId?.(node.next_node_id!)}
|
||||
onMouseLeave={() => onHoverNodeId?.(null)}
|
||||
>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="text-white/40">Next:</span>
|
||||
<span className="text-muted-foreground">Next:</span>
|
||||
{isSharedTarget && (
|
||||
<span title={sharedTooltip} className="flex items-center">
|
||||
<Users className="h-3 w-3 text-purple-500" />
|
||||
@@ -321,7 +321,7 @@ export function TreePreviewNode({
|
||||
)}
|
||||
<span className={cn(
|
||||
'truncate',
|
||||
nextNode?.type === 'solution' ? 'text-green-500 font-medium' : 'text-white/70'
|
||||
nextNode?.type === 'solution' ? 'text-green-500 font-medium' : 'text-muted-foreground'
|
||||
)}>
|
||||
{nextNodeLabel.slice(0, 30)}{nextNodeLabel.length > 30 ? '...' : ''}
|
||||
</span>
|
||||
@@ -347,7 +347,7 @@ export function TreePreviewNode({
|
||||
|
||||
{/* Children - show as branches */}
|
||||
{hasChildren && !isCollapsed && (
|
||||
<div className="relative mt-3 ml-6 pl-6 border-l-2 border-white/[0.06]">
|
||||
<div className="relative mt-3 ml-6 pl-6 border-l-2 border-border">
|
||||
<div className="space-y-4">
|
||||
{node.children!.map((child) => {
|
||||
const optionLabel = getOptionLabelForChild(child.id)
|
||||
@@ -355,7 +355,7 @@ export function TreePreviewNode({
|
||||
return (
|
||||
<div key={child.id} className="relative">
|
||||
{/* Horizontal connector line */}
|
||||
<div className="absolute -left-6 top-6 h-0.5 w-6 bg-white/[0.06]" />
|
||||
<div className="absolute -left-6 top-6 h-0.5 w-6 bg-border" />
|
||||
|
||||
<TreePreviewNode
|
||||
node={child}
|
||||
@@ -377,8 +377,8 @@ export function TreePreviewNode({
|
||||
|
||||
{/* Show collapsed indicator */}
|
||||
{hasChildren && isCollapsed && (
|
||||
<div className="mt-2 ml-6 text-xs text-white/40">
|
||||
<span className="rounded bg-white/10 px-2 py-1">
|
||||
<div className="mt-2 ml-6 text-xs text-muted-foreground">
|
||||
<span className="rounded bg-accent px-2 py-1">
|
||||
{node.children!.length} child node{node.children!.length !== 1 ? 's' : ''} hidden
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -56,7 +56,7 @@ export function TreePreviewPanel() {
|
||||
|
||||
if (!treeStructure) {
|
||||
return (
|
||||
<div className="flex h-full items-center justify-center p-4 text-sm text-white/40">
|
||||
<div className="flex h-full items-center justify-center p-4 text-sm text-muted-foreground">
|
||||
No tree structure to preview
|
||||
</div>
|
||||
)
|
||||
@@ -65,11 +65,11 @@ export function TreePreviewPanel() {
|
||||
return (
|
||||
<div className="flex h-full flex-col">
|
||||
{/* Header */}
|
||||
<div className="border-b border-white/[0.06] px-4 py-2">
|
||||
<h3 className="text-sm font-semibold text-white">
|
||||
<div className="border-b border-border px-4 py-2">
|
||||
<h3 className="text-sm font-semibold text-foreground">
|
||||
Preview: {name || 'Untitled Tree'}
|
||||
</h3>
|
||||
<p className="text-xs text-white/40">
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Click a node to select • Hover options to highlight targets
|
||||
</p>
|
||||
</div>
|
||||
@@ -91,8 +91,8 @@ export function TreePreviewPanel() {
|
||||
</div>
|
||||
|
||||
{/* Legend */}
|
||||
<div className="border-t border-white/[0.06] px-4 py-2">
|
||||
<div className="flex flex-wrap gap-4 text-xs text-white/40">
|
||||
<div className="border-t border-border px-4 py-2">
|
||||
<div className="flex flex-wrap gap-4 text-xs text-muted-foreground">
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="h-3 w-3 rounded bg-blue-500/50" />
|
||||
<span>Decision</span>
|
||||
|
||||
@@ -12,7 +12,7 @@ interface MarkdownContentProps {
|
||||
*/
|
||||
export function MarkdownContent({ content, className }: MarkdownContentProps) {
|
||||
return (
|
||||
<div className={cn('prose prose-sm dark:prose-invert max-w-none', className)}>
|
||||
<div className={cn('prose prose-sm prose-invert max-w-none', className)}>
|
||||
<ReactMarkdown
|
||||
components={{
|
||||
// Style paragraphs
|
||||
@@ -21,7 +21,7 @@ export function MarkdownContent({ content, className }: MarkdownContentProps) {
|
||||
),
|
||||
// Style bold text
|
||||
strong: ({ children }) => (
|
||||
<strong className="font-semibold text-white">{children}</strong>
|
||||
<strong className="font-semibold text-foreground">{children}</strong>
|
||||
),
|
||||
// Style ordered lists
|
||||
ol: ({ children }) => (
|
||||
@@ -33,7 +33,7 @@ export function MarkdownContent({ content, className }: MarkdownContentProps) {
|
||||
),
|
||||
// Style list items
|
||||
li: ({ children }) => (
|
||||
<li className="text-white/60">{children}</li>
|
||||
<li className="text-muted-foreground">{children}</li>
|
||||
),
|
||||
// Style inline code
|
||||
code: ({ className, children, ...props }) => {
|
||||
@@ -43,7 +43,7 @@ export function MarkdownContent({ content, className }: MarkdownContentProps) {
|
||||
return (
|
||||
<code
|
||||
className={cn(
|
||||
'block rounded bg-white/10 p-3 font-mono text-sm overflow-x-auto',
|
||||
'block rounded bg-accent p-3 font-mono text-sm overflow-x-auto',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -54,7 +54,7 @@ export function MarkdownContent({ content, className }: MarkdownContentProps) {
|
||||
}
|
||||
return (
|
||||
<code
|
||||
className="rounded bg-white/10 px-1.5 py-0.5 font-mono text-sm"
|
||||
className="rounded bg-accent px-1.5 py-0.5 font-mono text-sm"
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
@@ -63,25 +63,25 @@ export function MarkdownContent({ content, className }: MarkdownContentProps) {
|
||||
},
|
||||
// Style code blocks (pre)
|
||||
pre: ({ children }) => (
|
||||
<pre className="mb-3 overflow-x-auto rounded bg-white/10 p-0 last:mb-0">
|
||||
<pre className="mb-3 overflow-x-auto rounded bg-accent p-0 last:mb-0">
|
||||
{children}
|
||||
</pre>
|
||||
),
|
||||
// Style headers
|
||||
h1: ({ children }) => (
|
||||
<h1 className="mb-3 text-lg font-bold text-white">{children}</h1>
|
||||
<h1 className="mb-3 text-lg font-bold text-foreground">{children}</h1>
|
||||
),
|
||||
h2: ({ children }) => (
|
||||
<h2 className="mb-2 text-base font-bold text-white">{children}</h2>
|
||||
<h2 className="mb-2 text-base font-bold text-foreground">{children}</h2>
|
||||
),
|
||||
h3: ({ children }) => (
|
||||
<h3 className="mb-2 text-sm font-bold text-white">{children}</h3>
|
||||
<h3 className="mb-2 text-sm font-bold text-foreground">{children}</h3>
|
||||
),
|
||||
// Style horizontal rules
|
||||
hr: () => <hr className="my-4 border-white/[0.06]" />,
|
||||
hr: () => <hr className="my-4 border-border" />,
|
||||
// Style blockquotes
|
||||
blockquote: ({ children }) => (
|
||||
<blockquote className="mb-3 border-l-4 border-white/20 pl-4 italic text-white/50 last:mb-0">
|
||||
<blockquote className="mb-3 border-l-4 border-border pl-4 italic text-muted-foreground last:mb-0">
|
||||
{children}
|
||||
</blockquote>
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user