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