feat: UI design system - sidebar layout, workspace system, and shell redesign (#77)
* feat: add workspace system and sidebar layout (UI design system Phase A+B) Backend: Workspace model, migration (036), schemas, CRUD API endpoints. Adds workspace_id to trees and categories, seeds 4 default workspaces per account, auto-assigns existing trees by tree_type. Frontend: Complete AppLayout rewrite from top-nav to CSS Grid shell with persistent sidebar + topbar. New components: WorkspaceSwitcher, NavItem, CategoryList, TagCloud, TopBar, Sidebar. Dashboard components: QuickStats, FiltersBar, SectionGroup, TreeListItem, SessionsPanel. WorkspaceStore with localStorage persistence. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add command palette search, dashboard rewrite, and shell height fixes (Phase C) - Add ⌘K command palette with debounced search across flows and sessions - Rewrite QuickStartPage as dashboard with stats, filters, sessions panel - Fix h-[calc(100vh-4rem)] → h-full across all pages for CSS Grid shell - Add active session count badge to sidebar Sessions nav item Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add sidebar collapse, category/tag filtering, and workspace CRUD (Phase D) - Sidebar collapse/expand toggle with icon-only rail mode (persisted) - Sidebar category/tag clicks navigate to /trees with URL params - TreeLibraryPage syncs filters from URL search params bidirectionally - Workspace create modal with icon picker and auto-slug generation - TopBar logo adapts to collapsed sidebar state Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add Quick Launch modal with actions and recent flows - Zap button opens Quick Launch with create/navigate shortcuts - Shows recent flows for quick session start - Keyboard navigation support (arrows + enter) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add activity notifications panel with session feed - Bell icon shows dot indicator for recent activity - Dropdown panel shows recent sessions with status icons - Links to session detail and sessions list page Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: remove workspace system, add pinned flows and label renames Replace workspace system with pinned flows API (pin/unpin/list/reorder). Rename user-facing labels: Tree→Flow, Procedure→Project. Add sidebar nav sub-items for flow type filtering. Remove 11 workspace files, add migrations 037-038, clean all workspace references. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: collapsed sidebar layout scaling and toggle button size Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: migrate auth pages to new design system Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: migrate TreeLibraryPage to new design system Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: migrate session pages to new design system Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: migrate TreeEditorPage to new design system Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: migrate TreeNavigationPage to new design system Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: migrate session sharing components to new design system Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: remove workspace dropdown animation (dead code) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: migrate common components to new design system Migrate 15 components from monochrome glass-card design to purple gradient accent design system tokens (bg-card, border-border, text-foreground, bg-gradient-brand, etc.) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: migrate procedural and step library components to new design system Migrate 10 components from monochrome glass-card design to purple gradient accent design system tokens. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: migrate admin pages and components to new design system Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: migrate remaining pages to new design system Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * 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> * fix: keep brand text visible on sidebar collapse, hide sub-items until hover - TopBar: always show "ResolutionFlow" text regardless of sidebar state - NavItem: sub-items (Troubleshooting, Projects) hidden by default, revealed on hover or when a child route is active Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit was merged in pull request #77.
This commit is contained in:
@@ -51,7 +51,7 @@ export function DynamicArrayField<T>({
|
||||
type="button"
|
||||
onClick={() => handleMoveUp(index)}
|
||||
disabled={index === 0}
|
||||
className="rounded p-0.5 text-white/50 hover:bg-white/[0.06] hover:text-white disabled:opacity-30"
|
||||
className="rounded p-0.5 text-muted-foreground hover:bg-accent hover:text-foreground disabled:opacity-30"
|
||||
title="Move up"
|
||||
aria-label="Move up"
|
||||
>
|
||||
@@ -61,7 +61,7 @@ export function DynamicArrayField<T>({
|
||||
type="button"
|
||||
onClick={() => handleMoveDown(index)}
|
||||
disabled={index === items.length - 1}
|
||||
className="rounded p-0.5 text-white/50 hover:bg-white/[0.06] hover:text-white disabled:opacity-30"
|
||||
className="rounded p-0.5 text-muted-foreground hover:bg-accent hover:text-foreground disabled:opacity-30"
|
||||
title="Move down"
|
||||
aria-label="Move down"
|
||||
>
|
||||
@@ -78,7 +78,7 @@ export function DynamicArrayField<T>({
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onRemove(index)}
|
||||
className="mt-1 rounded p-1 text-white/50 hover:bg-red-400/20 hover:text-red-400"
|
||||
className="mt-1 rounded p-1 text-muted-foreground hover:bg-red-400/20 hover:text-red-400"
|
||||
title="Remove"
|
||||
aria-label="Remove"
|
||||
>
|
||||
@@ -94,9 +94,9 @@ export function DynamicArrayField<T>({
|
||||
type="button"
|
||||
onClick={onAdd}
|
||||
className={cn(
|
||||
'flex w-full items-center justify-center gap-1 rounded-md border border-dashed border-white/10',
|
||||
'px-3 py-2 text-sm text-white/50',
|
||||
'hover:border-white/30 hover:text-white'
|
||||
'flex w-full items-center justify-center gap-1 rounded-md border border-dashed border-border',
|
||||
'px-3 py-2 text-sm text-muted-foreground',
|
||||
'hover:border-border hover:text-foreground'
|
||||
)}
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
@@ -106,7 +106,7 @@ export function DynamicArrayField<T>({
|
||||
|
||||
{/* Empty state */}
|
||||
{items.length === 0 && !canAdd && (
|
||||
<p className="text-center text-sm text-white/40">No items</p>
|
||||
<p className="text-center text-sm text-muted-foreground">No items</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -68,14 +68,14 @@ export function NodeEditorModal({ node, onClose, isNewNode = false }: NodeEditor
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleCancel}
|
||||
className="rounded-md border border-white/10 px-4 py-2 text-sm font-medium text-white/60 hover:bg-white/10 hover:text-white"
|
||||
className="rounded-md border border-border px-4 py-2 text-sm font-medium text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleSave}
|
||||
className="rounded-md bg-white px-4 py-2 text-sm font-medium text-black hover:bg-white/90"
|
||||
className="rounded-md bg-gradient-brand text-white shadow-lg shadow-primary/20 px-4 py-2 text-sm font-medium hover:opacity-90"
|
||||
>
|
||||
Done
|
||||
</button>
|
||||
@@ -85,8 +85,8 @@ export function NodeEditorModal({ node, onClose, isNewNode = false }: NodeEditor
|
||||
return (
|
||||
<Modal isOpen={true} onClose={onClose} title={getTitle()} size="lg" footer={footerContent}>
|
||||
{/* Node ID display */}
|
||||
<div className="mb-4 text-xs text-white/40">
|
||||
Node ID: <code className="rounded bg-white/10 px-1 py-0.5">{node.id}</code>
|
||||
<div className="mb-4 text-xs text-muted-foreground">
|
||||
Node ID: <code className="rounded bg-accent px-1 py-0.5">{node.id}</code>
|
||||
</div>
|
||||
|
||||
{/* Validation errors */}
|
||||
|
||||
@@ -52,7 +52,7 @@ export function NodeFormAction({ node, onUpdate }: NodeFormActionProps) {
|
||||
<div className="space-y-4">
|
||||
{/* Title */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-white">
|
||||
<label className="block text-sm font-medium text-foreground">
|
||||
Title <span className="text-red-400">*</span>
|
||||
</label>
|
||||
<input
|
||||
@@ -62,9 +62,9 @@ export function NodeFormAction({ node, onUpdate }: NodeFormActionProps) {
|
||||
placeholder="e.g., Restart the Service"
|
||||
className={cn(
|
||||
'mt-1 block w-full rounded-md border px-3 py-2 text-sm',
|
||||
'bg-black/50 text-white placeholder:text-white/40',
|
||||
'focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20',
|
||||
titleError ? 'border-red-400' : 'border-white/10'
|
||||
'bg-card text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
|
||||
titleError ? 'border-red-400' : 'border-border'
|
||||
)}
|
||||
/>
|
||||
{titleError && (
|
||||
@@ -75,24 +75,24 @@ export function NodeFormAction({ node, onUpdate }: NodeFormActionProps) {
|
||||
{/* Description */}
|
||||
<div>
|
||||
<div className="flex items-center justify-between">
|
||||
<label className="block text-sm font-medium text-white">
|
||||
<label className="block text-sm font-medium text-foreground">
|
||||
Description
|
||||
</label>
|
||||
{node.description && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowPreview(!showPreview)}
|
||||
className="text-xs text-white/50 hover:text-white hover:underline"
|
||||
className="text-xs text-muted-foreground hover:text-foreground hover:underline"
|
||||
>
|
||||
{showPreview ? 'Edit' : 'Preview'}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<p className="mb-1 text-xs text-white/40">
|
||||
<p className="mb-1 text-xs text-muted-foreground">
|
||||
Supports markdown: **bold**, *italic*, - lists, 1. numbered lists, `code`
|
||||
</p>
|
||||
{showPreview && node.description ? (
|
||||
<div className="mt-1 rounded-md border border-white/10 bg-white/[0.04] p-3 text-sm">
|
||||
<div className="mt-1 rounded-md border border-border bg-accent/50 p-3 text-sm">
|
||||
<MarkdownContent content={node.description} />
|
||||
</div>
|
||||
) : (
|
||||
@@ -108,7 +108,7 @@ export function NodeFormAction({ node, onUpdate }: NodeFormActionProps) {
|
||||
**Note:** Important information here"
|
||||
rows={5}
|
||||
className={cn(
|
||||
'mt-1 block w-full rounded-md border border-white/10 px-3 py-2 text-sm',
|
||||
'mt-1 block w-full rounded-md border border-border px-3 py-2 text-sm',
|
||||
'bg-background text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary'
|
||||
)}
|
||||
@@ -118,10 +118,10 @@ export function NodeFormAction({ node, onUpdate }: NodeFormActionProps) {
|
||||
|
||||
{/* Commands */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-white">
|
||||
<label className="block text-sm font-medium text-foreground">
|
||||
Commands
|
||||
</label>
|
||||
<p className="mb-2 text-xs text-white/40">
|
||||
<p className="mb-2 text-xs text-muted-foreground">
|
||||
PowerShell or CLI commands to execute
|
||||
</p>
|
||||
<DynamicArrayField
|
||||
@@ -137,7 +137,7 @@ export function NodeFormAction({ node, onUpdate }: NodeFormActionProps) {
|
||||
onChange={(e) => handleUpdateCommand(index, e.target.value)}
|
||||
placeholder="e.g., Get-Service BrokerAgent"
|
||||
className={cn(
|
||||
'block w-full rounded-md border border-white/10 px-3 py-2 font-mono text-sm',
|
||||
'block w-full rounded-md border border-border px-3 py-2 font-mono text-sm',
|
||||
'bg-background text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary'
|
||||
)}
|
||||
@@ -148,7 +148,7 @@ export function NodeFormAction({ node, onUpdate }: NodeFormActionProps) {
|
||||
|
||||
{/* Expected Outcome */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-white">
|
||||
<label className="block text-sm font-medium text-foreground">
|
||||
Expected Outcome
|
||||
</label>
|
||||
<input
|
||||
@@ -157,7 +157,7 @@ export function NodeFormAction({ node, onUpdate }: NodeFormActionProps) {
|
||||
onChange={(e) => onUpdate({ expected_outcome: e.target.value })}
|
||||
placeholder="e.g., Service should show as Running"
|
||||
className={cn(
|
||||
'mt-1 block w-full rounded-md border border-white/10 px-3 py-2 text-sm',
|
||||
'mt-1 block w-full rounded-md border border-border px-3 py-2 text-sm',
|
||||
'bg-background text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary'
|
||||
)}
|
||||
|
||||
@@ -67,7 +67,7 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
|
||||
<h3 className="font-semibold text-blue-400">
|
||||
Starting Question
|
||||
</h3>
|
||||
<p className="mt-1 text-sm text-white/40">
|
||||
<p className="mt-1 text-sm text-muted-foreground">
|
||||
This is the first question users will see when they start this troubleshooting tree.
|
||||
Each option below creates a different troubleshooting path.
|
||||
</p>
|
||||
@@ -78,11 +78,11 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
|
||||
|
||||
{/* Question */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-white">
|
||||
<label className="block text-sm font-medium text-foreground">
|
||||
{isRootNode ? 'Starting Question' : 'Question'} <span className="text-red-400">*</span>
|
||||
</label>
|
||||
{isRootNode && (
|
||||
<p className="mt-0.5 text-xs text-white/40">
|
||||
<p className="mt-0.5 text-xs text-muted-foreground">
|
||||
What's the main question to diagnose the issue?
|
||||
</p>
|
||||
)}
|
||||
@@ -95,9 +95,9 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
|
||||
: "e.g., Can you ping the server?"}
|
||||
className={cn(
|
||||
'mt-1 block w-full rounded-md border px-3 py-2 text-sm',
|
||||
'bg-black/50 text-white placeholder:text-white/40',
|
||||
'focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20',
|
||||
questionError ? 'border-red-400' : 'border-white/10'
|
||||
'bg-card text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
|
||||
questionError ? 'border-red-400' : 'border-border'
|
||||
)}
|
||||
/>
|
||||
{questionError && (
|
||||
@@ -107,7 +107,7 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
|
||||
|
||||
{/* Help Text */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-white">
|
||||
<label className="block text-sm font-medium text-foreground">
|
||||
Help Text
|
||||
</label>
|
||||
<textarea
|
||||
@@ -116,7 +116,7 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
|
||||
placeholder="Additional context or instructions for this decision..."
|
||||
rows={2}
|
||||
className={cn(
|
||||
'mt-1 block w-full rounded-md border border-white/10 px-3 py-2 text-sm',
|
||||
'mt-1 block w-full rounded-md border border-border px-3 py-2 text-sm',
|
||||
'bg-background text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary'
|
||||
)}
|
||||
@@ -125,15 +125,15 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
|
||||
|
||||
{/* Options */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-white">
|
||||
<label className="block text-sm font-medium text-foreground">
|
||||
{isRootNode ? 'Answer Options (Branches)' : 'Options'} <span className="text-red-400">*</span>
|
||||
</label>
|
||||
{isRootNode ? (
|
||||
<p className="mt-0.5 text-xs text-white/40">
|
||||
<p className="mt-0.5 text-xs text-muted-foreground">
|
||||
Add as many options as needed (A, B, C, D...). Each option leads to a completely different troubleshooting path.
|
||||
</p>
|
||||
) : (
|
||||
<p className="mt-0.5 text-xs text-white/40">
|
||||
<p className="mt-0.5 text-xs text-muted-foreground">
|
||||
Each option can branch to a different next step.
|
||||
</p>
|
||||
)}
|
||||
@@ -158,14 +158,14 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
|
||||
const letter = indexToLetter(index)
|
||||
|
||||
return (
|
||||
<div className="rounded-md border border-white/10 bg-white/[0.04] p-3">
|
||||
<div className="rounded-md border border-border bg-accent/50 p-3">
|
||||
<div className="mb-2 flex items-center gap-2">
|
||||
{/* Letter badge */}
|
||||
<span className={cn(
|
||||
'flex h-6 w-6 items-center justify-center rounded-full text-xs font-bold',
|
||||
isRootNode
|
||||
? 'bg-blue-500/20 text-blue-400'
|
||||
: 'bg-white/10 text-white/50'
|
||||
: 'bg-accent text-muted-foreground'
|
||||
)}>
|
||||
{letter}
|
||||
</span>
|
||||
@@ -180,7 +180,7 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
|
||||
'block flex-1 rounded-md border px-3 py-2 text-sm',
|
||||
'bg-background text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary',
|
||||
optionLabelError ? 'border-red-400' : 'border-white/10'
|
||||
optionLabelError ? 'border-red-400' : 'border-border'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
@@ -207,7 +207,7 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
|
||||
|
||||
{/* Example hint for root node */}
|
||||
{isRootNode && (node.options?.length || 0) < 2 && (
|
||||
<div className="mt-3 rounded-md border border-dashed border-white/10 bg-white/[0.02] p-3 text-xs text-white/40">
|
||||
<div className="mt-3 rounded-md border border-dashed border-border bg-accent/50 p-3 text-xs text-muted-foreground">
|
||||
<strong>Tip:</strong> Most troubleshooting trees start with 2-5 main branches.
|
||||
For example: "Connection Issues", "Performance Problems", "Error Messages", "Other".
|
||||
</div>
|
||||
|
||||
@@ -47,7 +47,7 @@ export function NodeFormResolution({ node, onUpdate }: NodeFormResolutionProps)
|
||||
<div className="space-y-4">
|
||||
{/* Title */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-white">
|
||||
<label className="block text-sm font-medium text-foreground">
|
||||
Title <span className="text-red-400">*</span>
|
||||
</label>
|
||||
<input
|
||||
@@ -57,9 +57,9 @@ export function NodeFormResolution({ node, onUpdate }: NodeFormResolutionProps)
|
||||
placeholder="e.g., VDA Successfully Registered"
|
||||
className={cn(
|
||||
'mt-1 block w-full rounded-md border px-3 py-2 text-sm',
|
||||
'bg-black/50 text-white placeholder:text-white/40',
|
||||
'focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20',
|
||||
titleError ? 'border-red-400' : 'border-white/10'
|
||||
'bg-card text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
|
||||
titleError ? 'border-red-400' : 'border-border'
|
||||
)}
|
||||
/>
|
||||
{titleError && (
|
||||
@@ -70,24 +70,24 @@ export function NodeFormResolution({ node, onUpdate }: NodeFormResolutionProps)
|
||||
{/* Description */}
|
||||
<div>
|
||||
<div className="flex items-center justify-between">
|
||||
<label className="block text-sm font-medium text-white">
|
||||
<label className="block text-sm font-medium text-foreground">
|
||||
Description
|
||||
</label>
|
||||
{node.description && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowPreview(!showPreview)}
|
||||
className="text-xs text-white/50 hover:text-white hover:underline"
|
||||
className="text-xs text-muted-foreground hover:text-foreground hover:underline"
|
||||
>
|
||||
{showPreview ? 'Edit' : 'Preview'}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<p className="mb-1 text-xs text-white/40">
|
||||
<p className="mb-1 text-xs text-muted-foreground">
|
||||
Supports markdown: **bold**, *italic*, - lists, 1. numbered lists, `code`
|
||||
</p>
|
||||
{showPreview && node.description ? (
|
||||
<div className="mt-1 rounded-md border border-white/10 bg-white/[0.04] p-3 text-sm">
|
||||
<div className="mt-1 rounded-md border border-border bg-accent/50 p-3 text-sm">
|
||||
<MarkdownContent content={node.description} />
|
||||
</div>
|
||||
) : (
|
||||
@@ -102,7 +102,7 @@ Document what was done and the outcome.
|
||||
**Close ticket as:** Resolved"
|
||||
rows={5}
|
||||
className={cn(
|
||||
'mt-1 block w-full rounded-md border border-white/10 px-3 py-2 text-sm',
|
||||
'mt-1 block w-full rounded-md border border-border px-3 py-2 text-sm',
|
||||
'bg-background text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary'
|
||||
)}
|
||||
@@ -112,10 +112,10 @@ Document what was done and the outcome.
|
||||
|
||||
{/* Resolution Steps */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-white">
|
||||
<label className="block text-sm font-medium text-foreground">
|
||||
Resolution Steps
|
||||
</label>
|
||||
<p className="mb-2 text-xs text-white/40">
|
||||
<p className="mb-2 text-xs text-muted-foreground">
|
||||
Step-by-step instructions for resolving the issue
|
||||
</p>
|
||||
<DynamicArrayField
|
||||
@@ -135,7 +135,7 @@ Document what was done and the outcome.
|
||||
onChange={(e) => handleUpdateStep(index, e.target.value)}
|
||||
placeholder={`Step ${index + 1}`}
|
||||
className={cn(
|
||||
'block w-full rounded-md border border-white/10 px-3 py-2 text-sm',
|
||||
'block w-full rounded-md border border-border px-3 py-2 text-sm',
|
||||
'bg-background text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary'
|
||||
)}
|
||||
|
||||
@@ -95,9 +95,9 @@ function NodeListItem({
|
||||
}
|
||||
|
||||
const nodeTypeColors: Record<NodeType, string> = {
|
||||
decision: 'bg-blue-500/20 text-blue-600 dark:text-blue-400',
|
||||
action: 'bg-yellow-500/20 text-yellow-600 dark:text-yellow-400',
|
||||
solution: 'bg-green-500/20 text-green-600 dark:text-green-400'
|
||||
decision: 'bg-blue-500/20 text-blue-400',
|
||||
action: 'bg-yellow-500/20 text-yellow-400',
|
||||
solution: 'bg-green-500/20 text-green-400'
|
||||
}
|
||||
|
||||
const getNodeLabel = () => {
|
||||
@@ -223,7 +223,7 @@ function NodeListItem({
|
||||
|
||||
{/* Node type icon - special treatment for root */}
|
||||
{isRootNode ? (
|
||||
<span className="flex items-center gap-1 rounded px-1.5 py-0.5 text-xs bg-blue-500/30 text-blue-600 dark:text-blue-400 font-semibold">
|
||||
<span className="flex items-center gap-1 rounded px-1.5 py-0.5 text-xs bg-blue-500/30 text-blue-400 font-semibold">
|
||||
<Play className="h-4 w-4" />
|
||||
<span className="hidden sm:inline">START</span>
|
||||
</span>
|
||||
@@ -254,7 +254,7 @@ function NodeListItem({
|
||||
'flex items-center gap-1 rounded px-1.5 py-0.5 text-xs',
|
||||
hasError
|
||||
? 'bg-destructive/20 text-destructive'
|
||||
: 'bg-yellow-500/20 text-yellow-600 dark:text-yellow-500'
|
||||
: 'bg-yellow-500/20 text-yellow-500'
|
||||
)}
|
||||
>
|
||||
{hasError ? (
|
||||
|
||||
@@ -11,9 +11,9 @@ const CREATE_SOLUTION = `${CREATE_PREFIX}solution__`
|
||||
|
||||
// Unicode symbols for node types (works in select options)
|
||||
const NODE_TYPE_SYMBOLS: Record<NodeType, string> = {
|
||||
decision: 'ⓘ', // Information/question symbol
|
||||
action: '⚡', // Lightning bolt for action
|
||||
solution: '✓' // Checkmark for solution
|
||||
decision: '\u24D8', // Information/question symbol
|
||||
action: '\u26A1', // Lightning bolt for action
|
||||
solution: '\u2713' // Checkmark for solution
|
||||
}
|
||||
|
||||
// Node type labels for UI
|
||||
@@ -139,7 +139,7 @@ export function NodePicker({
|
||||
return (
|
||||
<div className={className}>
|
||||
{label && (
|
||||
<label className="mb-1 block text-sm font-medium text-white">
|
||||
<label className="mb-1 block text-sm font-medium text-foreground">
|
||||
{label}
|
||||
</label>
|
||||
)}
|
||||
@@ -147,8 +147,8 @@ export function NodePicker({
|
||||
{/* Inline node creation UI */}
|
||||
{creatingNodeType ? (
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center gap-2 rounded-md border border-white/20 bg-white/[0.04] p-2">
|
||||
<span className="text-xs font-medium text-white">
|
||||
<div className="flex items-center gap-2 rounded-md border border-border bg-accent/50 p-2">
|
||||
<span className="text-xs font-medium text-foreground">
|
||||
New {NODE_TYPE_LABELS[creatingNodeType]}:
|
||||
</span>
|
||||
<input
|
||||
@@ -159,9 +159,9 @@ export function NodePicker({
|
||||
onKeyDown={handleKeyDown}
|
||||
placeholder={creatingNodeType === 'decision' ? 'Enter question...' : 'Enter title...'}
|
||||
className={cn(
|
||||
'flex-1 rounded-md border border-white/10 px-2 py-1 text-sm',
|
||||
'bg-black/50 text-white placeholder:text-white/40',
|
||||
'focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20'
|
||||
'flex-1 rounded-md border border-border px-2 py-1 text-sm',
|
||||
'bg-card text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
@@ -169,7 +169,7 @@ export function NodePicker({
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleCancelCreate}
|
||||
className="flex-1 rounded-md border border-white/10 px-3 py-1.5 text-xs font-medium text-white/60 hover:bg-white/10 hover:text-white"
|
||||
className="flex-1 rounded-md border border-border px-3 py-1.5 text-xs font-medium text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
@@ -179,7 +179,7 @@ export function NodePicker({
|
||||
disabled={!newNodeTitle.trim()}
|
||||
className={cn(
|
||||
'flex-1 rounded-md px-3 py-1.5 text-xs font-medium',
|
||||
'bg-white text-black hover:bg-white/90',
|
||||
'bg-gradient-brand text-white shadow-lg shadow-primary/20 hover:opacity-90',
|
||||
'disabled:opacity-50 disabled:cursor-not-allowed'
|
||||
)}
|
||||
>
|
||||
@@ -194,9 +194,9 @@ export function NodePicker({
|
||||
onChange={(e) => handleChange(e.target.value)}
|
||||
className={cn(
|
||||
'block w-full rounded-md border px-3 py-2 text-sm',
|
||||
'bg-black/50 text-white',
|
||||
'focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20',
|
||||
error ? 'border-red-400' : 'border-white/10'
|
||||
'bg-card text-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
|
||||
error ? 'border-red-400' : 'border-border'
|
||||
)}
|
||||
>
|
||||
<option value="">{placeholder}</option>
|
||||
@@ -242,7 +242,7 @@ export function NodePicker({
|
||||
|
||||
{/* Show what's selected */}
|
||||
{value && selectedNode && (
|
||||
<p className="mt-1 text-xs text-white/40">
|
||||
<p className="mt-1 text-xs text-muted-foreground">
|
||||
→ {selectedNode.label}
|
||||
</p>
|
||||
)}
|
||||
|
||||
@@ -28,12 +28,12 @@ export function TreeEditorLayout({ isMobile = false }: TreeEditorLayoutProps) {
|
||||
<>
|
||||
{/* Code Mode: Monaco editor (60%) + Preview (40%) */}
|
||||
<div className={cn(
|
||||
'flex flex-col overflow-hidden border-white/[0.06]',
|
||||
'flex flex-col overflow-hidden border-border',
|
||||
isMobile ? 'h-full w-full border-b' : 'w-3/5 border-r'
|
||||
)}>
|
||||
<Suspense fallback={
|
||||
<div className="flex h-full items-center justify-center bg-[#0a0a0a]">
|
||||
<div className="h-6 w-6 animate-spin rounded-full border-2 border-white/20 border-t-white" />
|
||||
<div className="flex h-full items-center justify-center bg-card">
|
||||
<div className="h-6 w-6 animate-spin rounded-full border-2 border-border border-t-foreground" />
|
||||
</div>
|
||||
}>
|
||||
<CodeModeEditor />
|
||||
@@ -42,7 +42,7 @@ export function TreeEditorLayout({ isMobile = false }: TreeEditorLayoutProps) {
|
||||
|
||||
{/* Right Panel - Preview */}
|
||||
<div className={cn(
|
||||
'flex-1 overflow-hidden bg-white/[0.02]',
|
||||
'flex-1 overflow-hidden bg-accent/50',
|
||||
isMobile ? 'hidden' : 'block'
|
||||
)}>
|
||||
<TreePreviewPanel />
|
||||
@@ -52,7 +52,7 @@ export function TreeEditorLayout({ isMobile = false }: TreeEditorLayoutProps) {
|
||||
<>
|
||||
{/* Flow Mode: Form editor (60%) + Preview (40%) */}
|
||||
<div className={cn(
|
||||
'flex flex-col overflow-y-auto border-white/[0.06]',
|
||||
'flex flex-col overflow-y-auto border-border',
|
||||
isMobile ? 'h-full w-full border-b' : 'w-3/5 border-r'
|
||||
)}>
|
||||
<div className="space-y-4 p-4">
|
||||
@@ -63,7 +63,7 @@ export function TreeEditorLayout({ isMobile = false }: TreeEditorLayoutProps) {
|
||||
|
||||
{/* Right Panel - Preview */}
|
||||
<div className={cn(
|
||||
'flex-1 overflow-hidden bg-white/[0.02]',
|
||||
'flex-1 overflow-hidden bg-accent/50',
|
||||
isMobile ? 'hidden' : 'block'
|
||||
)}>
|
||||
<TreePreviewPanel />
|
||||
|
||||
@@ -56,12 +56,12 @@ export function TreeMetadataForm() {
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="space-y-4 glass-card rounded-2xl p-4">
|
||||
<h2 className="text-sm font-semibold text-white">Tree Details</h2>
|
||||
<div className="space-y-4 bg-card border border-border rounded-2xl p-4">
|
||||
<h2 className="text-sm font-semibold text-foreground">Tree Details</h2>
|
||||
|
||||
{/* Name */}
|
||||
<div>
|
||||
<label htmlFor="tree-name" className="block text-sm font-medium text-white">
|
||||
<label htmlFor="tree-name" className="block text-sm font-medium text-foreground">
|
||||
Name <span className="text-red-400">*</span>
|
||||
</label>
|
||||
<input
|
||||
@@ -72,9 +72,9 @@ export function TreeMetadataForm() {
|
||||
placeholder="e.g., VDA Registration Troubleshooting"
|
||||
className={cn(
|
||||
'mt-1 block w-full rounded-md border px-3 py-2 text-sm',
|
||||
'bg-black/50 text-white placeholder:text-white/40',
|
||||
'focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20',
|
||||
nameError ? 'border-red-400' : 'border-white/10'
|
||||
'bg-card text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
|
||||
nameError ? 'border-red-400' : 'border-border'
|
||||
)}
|
||||
/>
|
||||
{nameError && <p className="mt-1 text-xs text-red-400">{nameError.message}</p>}
|
||||
@@ -82,7 +82,7 @@ export function TreeMetadataForm() {
|
||||
|
||||
{/* Description */}
|
||||
<div>
|
||||
<label htmlFor="tree-description" className="block text-sm font-medium text-white">
|
||||
<label htmlFor="tree-description" className="block text-sm font-medium text-foreground">
|
||||
Description
|
||||
</label>
|
||||
<textarea
|
||||
@@ -92,16 +92,16 @@ export function TreeMetadataForm() {
|
||||
placeholder="Brief description of what this tree troubleshoots..."
|
||||
rows={2}
|
||||
className={cn(
|
||||
'mt-1 block w-full rounded-md border border-white/10 px-3 py-2 text-sm',
|
||||
'bg-black/50 text-white placeholder:text-white/40',
|
||||
'focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20'
|
||||
'mt-1 block w-full rounded-md border border-border px-3 py-2 text-sm',
|
||||
'bg-card text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Category */}
|
||||
<div>
|
||||
<label htmlFor="tree-category" className="block text-sm font-medium text-white">
|
||||
<label htmlFor="tree-category" className="block text-sm font-medium text-foreground">
|
||||
Category
|
||||
</label>
|
||||
{!customCategory ? (
|
||||
@@ -110,9 +110,9 @@ export function TreeMetadataForm() {
|
||||
value={categoryId || ''}
|
||||
onChange={(e) => handleCategoryChange(e.target.value)}
|
||||
className={cn(
|
||||
'mt-1 block w-full rounded-md border border-white/10 px-3 py-2 text-sm',
|
||||
'bg-black/50 text-white',
|
||||
'focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20'
|
||||
'mt-1 block w-full rounded-md border border-border px-3 py-2 text-sm',
|
||||
'bg-card text-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
|
||||
)}
|
||||
>
|
||||
<option value="">No category</option>
|
||||
@@ -132,9 +132,9 @@ export function TreeMetadataForm() {
|
||||
onChange={(e) => setCategory(e.target.value)}
|
||||
placeholder="Enter new category"
|
||||
className={cn(
|
||||
'block flex-1 rounded-md border border-white/10 px-3 py-2 text-sm',
|
||||
'bg-black/50 text-white placeholder:text-white/40',
|
||||
'focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20'
|
||||
'block flex-1 rounded-md border border-border px-3 py-2 text-sm',
|
||||
'bg-card text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
|
||||
)}
|
||||
autoFocus
|
||||
/>
|
||||
@@ -144,7 +144,7 @@ export function TreeMetadataForm() {
|
||||
setCategory('')
|
||||
setCategoryId(null)
|
||||
}}
|
||||
className="rounded-md border border-white/10 px-3 py-2 text-sm text-white/60 hover:bg-white/10 hover:text-white"
|
||||
className="rounded-md border border-border px-3 py-2 text-sm text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
@@ -154,7 +154,7 @@ export function TreeMetadataForm() {
|
||||
|
||||
{/* Tags */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-white">Tags</label>
|
||||
<label className="block text-sm font-medium text-foreground">Tags</label>
|
||||
<div className="mt-1">
|
||||
<TagInput tags={tags} onChange={setTags} maxTags={10} placeholder="Add tags..." />
|
||||
</div>
|
||||
@@ -162,13 +162,13 @@ export function TreeMetadataForm() {
|
||||
|
||||
{/* Visibility */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-white">Visibility</label>
|
||||
<label className="block text-sm font-medium text-foreground">Visibility</label>
|
||||
<div className="mt-2 flex gap-4">
|
||||
<label
|
||||
className={cn(
|
||||
'flex cursor-pointer items-center gap-2 rounded-md border px-4 py-2',
|
||||
'transition-colors',
|
||||
!isPublic ? 'border-white/30 bg-white/10 text-white' : 'border-white/10 text-white/60 hover:bg-white/[0.06]'
|
||||
!isPublic ? 'border-primary/30 bg-accent text-foreground' : 'border-border text-muted-foreground hover:bg-accent/50'
|
||||
)}
|
||||
>
|
||||
<input
|
||||
@@ -185,7 +185,7 @@ export function TreeMetadataForm() {
|
||||
className={cn(
|
||||
'flex cursor-pointer items-center gap-2 rounded-md border px-4 py-2',
|
||||
'transition-colors',
|
||||
isPublic ? 'border-white/30 bg-white/10 text-white' : 'border-white/10 text-white/60 hover:bg-white/[0.06]'
|
||||
isPublic ? 'border-primary/30 bg-accent text-foreground' : 'border-border text-muted-foreground hover:bg-accent/50'
|
||||
)}
|
||||
>
|
||||
<input
|
||||
@@ -199,7 +199,7 @@ export function TreeMetadataForm() {
|
||||
<span className="text-sm">Public</span>
|
||||
</label>
|
||||
</div>
|
||||
<p className="mt-1 text-xs text-white/40">
|
||||
<p className="mt-1 text-xs text-muted-foreground">
|
||||
{isPublic
|
||||
? 'Anyone can view this tree'
|
||||
: 'Only you and your team can view this tree'}
|
||||
|
||||
@@ -35,7 +35,7 @@ export function ValidationSummary({ errors, onSelectNode }: ValidationSummaryPro
|
||||
<button
|
||||
onClick={() => setIsExpanded(!isExpanded)}
|
||||
className={cn(
|
||||
'flex w-full items-center justify-between p-3 text-left transition-colors hover:bg-white/5',
|
||||
'flex w-full items-center justify-between p-3 text-left transition-colors hover:bg-accent',
|
||||
errorItems.length > 0 ? 'text-red-400' : 'text-yellow-400'
|
||||
)}
|
||||
>
|
||||
@@ -81,7 +81,7 @@ export function ValidationSummary({ errors, onSelectNode }: ValidationSummaryPro
|
||||
<div className="flex-1">
|
||||
<p className="text-red-400">{error.message}</p>
|
||||
{error.nodeId && (
|
||||
<p className="mt-0.5 text-xs text-white/40">
|
||||
<p className="mt-0.5 text-xs text-muted-foreground">
|
||||
Click to select node: {error.nodeId}
|
||||
</p>
|
||||
)}
|
||||
@@ -105,7 +105,7 @@ export function ValidationSummary({ errors, onSelectNode }: ValidationSummaryPro
|
||||
<div className="flex-1">
|
||||
<p className="text-yellow-400">{warning.message}</p>
|
||||
{warning.nodeId && (
|
||||
<p className="mt-0.5 text-xs text-white/40">
|
||||
<p className="mt-0.5 text-xs text-muted-foreground">
|
||||
Click to select node: {warning.nodeId}
|
||||
</p>
|
||||
)}
|
||||
|
||||
@@ -166,8 +166,8 @@ export function CodeModeEditor() {
|
||||
beforeMount={handleEditorWillMount}
|
||||
onMount={handleEditorDidMount}
|
||||
loading={
|
||||
<div className="flex h-full items-center justify-center bg-[#0a0a0a]">
|
||||
<div className="h-6 w-6 animate-spin rounded-full border-2 border-white/20 border-t-white" />
|
||||
<div className="flex h-full items-center justify-center bg-card">
|
||||
<div className="h-6 w-6 animate-spin rounded-full border-2 border-border border-t-foreground" />
|
||||
</div>
|
||||
}
|
||||
options={{
|
||||
|
||||
@@ -91,15 +91,15 @@ export function CodeModeToolbar({
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between border-b border-white/[0.06] bg-black/50 px-3 py-1.5">
|
||||
<div className="flex items-center justify-between border-b border-border bg-card px-3 py-1.5">
|
||||
<div className="flex items-center gap-2">
|
||||
{/* Insert Node dropdown */}
|
||||
<div className="relative" ref={dropdownRef}>
|
||||
<button
|
||||
onClick={() => setInsertOpen(!insertOpen)}
|
||||
className={cn(
|
||||
'flex items-center gap-1 rounded-md border border-white/10 px-2.5 py-1 text-xs font-medium text-white/60',
|
||||
'hover:bg-white/10 hover:text-white'
|
||||
'flex items-center gap-1 rounded-md border border-border px-2.5 py-1 text-xs font-medium text-muted-foreground',
|
||||
'hover:bg-accent hover:text-foreground'
|
||||
)}
|
||||
>
|
||||
<Plus className="h-3 w-3" />
|
||||
@@ -107,24 +107,24 @@ export function CodeModeToolbar({
|
||||
<ChevronDown className="h-3 w-3" />
|
||||
</button>
|
||||
{insertOpen && (
|
||||
<div className="absolute left-0 top-full z-50 mt-1 w-44 rounded-lg border border-white/10 bg-[#111] py-1 shadow-xl">
|
||||
<div className="absolute left-0 top-full z-50 mt-1 w-44 rounded-lg border border-border bg-card py-1 shadow-xl">
|
||||
<button
|
||||
onClick={() => { onInsertTemplate(NODE_TEMPLATES.decision); setInsertOpen(false) }}
|
||||
className="flex w-full items-center gap-2 px-3 py-1.5 text-xs text-white/70 hover:bg-white/10"
|
||||
className="flex w-full items-center gap-2 px-3 py-1.5 text-xs text-muted-foreground hover:bg-accent"
|
||||
>
|
||||
<span className="h-2 w-2 rounded-full bg-blue-400" />
|
||||
Decision
|
||||
</button>
|
||||
<button
|
||||
onClick={() => { onInsertTemplate(NODE_TEMPLATES.action); setInsertOpen(false) }}
|
||||
className="flex w-full items-center gap-2 px-3 py-1.5 text-xs text-white/70 hover:bg-white/10"
|
||||
className="flex w-full items-center gap-2 px-3 py-1.5 text-xs text-muted-foreground hover:bg-accent"
|
||||
>
|
||||
<span className="h-2 w-2 rounded-full bg-amber-400" />
|
||||
Action
|
||||
</button>
|
||||
<button
|
||||
onClick={() => { onInsertTemplate(NODE_TEMPLATES.solution); setInsertOpen(false) }}
|
||||
className="flex w-full items-center gap-2 px-3 py-1.5 text-xs text-white/70 hover:bg-white/10"
|
||||
className="flex w-full items-center gap-2 px-3 py-1.5 text-xs text-muted-foreground hover:bg-accent"
|
||||
>
|
||||
<span className="h-2 w-2 rounded-full bg-emerald-400" />
|
||||
Solution
|
||||
@@ -139,8 +139,8 @@ export function CodeModeToolbar({
|
||||
<div className="flex items-center gap-1.5 text-xs">
|
||||
{isValidating ? (
|
||||
<>
|
||||
<Loader2 className="h-3 w-3 animate-spin text-white/40" />
|
||||
<span className="text-white/40">Validating...</span>
|
||||
<Loader2 className="h-3 w-3 animate-spin text-muted-foreground" />
|
||||
<span className="text-muted-foreground">Validating...</span>
|
||||
</>
|
||||
) : isValid ? (
|
||||
<>
|
||||
@@ -173,8 +173,8 @@ export function CodeModeToolbar({
|
||||
className={cn(
|
||||
'flex items-center gap-1 rounded-md px-2 py-1 text-xs',
|
||||
syntaxHelpOpen
|
||||
? 'bg-white/10 text-white'
|
||||
: 'text-white/40 hover:bg-white/[0.06] hover:text-white/60'
|
||||
? 'bg-accent text-foreground'
|
||||
: 'text-muted-foreground hover:bg-accent hover:text-muted-foreground'
|
||||
)}
|
||||
>
|
||||
<HelpCircle className="h-3 w-3" />
|
||||
|
||||
@@ -10,17 +10,17 @@ export function SyntaxHelpPanel({ open, onClose }: SyntaxHelpPanelProps) {
|
||||
if (!open) return null
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col border-l border-white/[0.06] bg-[#0a0a0a]">
|
||||
<div className="flex items-center justify-between border-b border-white/[0.06] px-3 py-2">
|
||||
<span className="text-xs font-medium text-white/60">Syntax Reference</span>
|
||||
<div className="flex h-full flex-col border-l border-border bg-card">
|
||||
<div className="flex items-center justify-between border-b border-border px-3 py-2">
|
||||
<span className="text-xs font-medium text-muted-foreground">Syntax Reference</span>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="rounded p-0.5 text-white/30 hover:bg-white/10 hover:text-white/60"
|
||||
className="rounded p-0.5 text-muted-foreground hover:bg-accent hover:text-muted-foreground"
|
||||
>
|
||||
<X className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex-1 overflow-y-auto px-3 py-3 text-[11px] leading-relaxed text-white/50">
|
||||
<div className="flex-1 overflow-y-auto px-3 py-3 text-[11px] leading-relaxed text-muted-foreground">
|
||||
<Section title="Node Structure">
|
||||
<Code>{`---
|
||||
id: node_id
|
||||
@@ -82,7 +82,7 @@ Description paragraph.
|
||||
function Section({ title, children }: { title: string; children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="mb-4">
|
||||
<h4 className="mb-1.5 text-[11px] font-semibold uppercase tracking-wider text-white/30">{title}</h4>
|
||||
<h4 className="mb-1.5 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground">{title}</h4>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
@@ -91,8 +91,8 @@ function Section({ title, children }: { title: string; children: React.ReactNode
|
||||
function Code({ children }: { children: string }) {
|
||||
return (
|
||||
<pre className={cn(
|
||||
'mb-2 overflow-x-auto rounded-md border border-white/[0.06] bg-black/50 px-2 py-1.5',
|
||||
'text-[10px] leading-relaxed text-white/50 whitespace-pre'
|
||||
'mb-2 overflow-x-auto rounded-md border border-border bg-card px-2 py-1.5',
|
||||
'text-[10px] leading-relaxed text-muted-foreground whitespace-pre'
|
||||
)}>
|
||||
{children}
|
||||
</pre>
|
||||
@@ -102,8 +102,8 @@ function Code({ children }: { children: string }) {
|
||||
function Row({ label, code }: { label: string; code: string }) {
|
||||
return (
|
||||
<div className="flex items-center justify-between py-0.5">
|
||||
<span className="text-white/40">{label}</span>
|
||||
<code className="rounded bg-white/[0.06] px-1 py-0.5 text-[10px] text-white/50">{code}</code>
|
||||
<span className="text-muted-foreground">{label}</span>
|
||||
<code className="rounded bg-accent px-1 py-0.5 text-[10px] text-muted-foreground">{code}</code>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user