+
{(!isMobile || mobileTab === 'input') && (
-
-
-
-
-
-
-
-
+
+
+
)}
{(!isMobile || mobileTab === 'output') && (
-
-
-
- {!error && formats.length > 0 && (
+
0 ? (
{t('detectedFormats')}
{formats.map(([fmt, count]) => (
-
- {fmt}{count > 1 ? ` ×${count}` : ''}
+
+ {fmt}
+ {count > 1 ? ` ×${count}` : ''}
))}
- )}
-
-
- {error ? (
-
-
-
{t('parseError')}
-
{error}
+ ) : undefined
+ }
+ >
+ {error ? (
+
+
+
{t('parseError')}
+
{error}
+
+ ) : (
+
+
+ {output ? (
+
+
+
+ ) : (
+
{t('outputPlaceholder')}
+ )}
- ) : (
-
-
- {output ? (
-
-
-
- ) : (
-
- {t('outputPlaceholder')}
-
- )}
-
-
- )}
-
-
+
+ )}
+
)}
-
+
);
}
diff --git a/apps/desktop-ui/src/components/json-to-code/json-to-code-layout.tsx b/apps/desktop-ui/src/components/json-to-code/json-to-code-layout.tsx
index 1ae40393..f71bc97b 100644
--- a/apps/desktop-ui/src/components/json-to-code/json-to-code-layout.tsx
+++ b/apps/desktop-ui/src/components/json-to-code/json-to-code-layout.tsx
@@ -4,8 +4,6 @@ import React, { useMemo, useState } from 'react'
import { useCopyToClipboard } from '@/hooks/use-copy-to-clipboard'
import { useTranslations } from 'next-intl'
import { Button } from '@/components/ui/button'
-import { Card } from '@/components/ui/card'
-import { Label } from '@/components/ui/label'
import { Input } from '@/components/ui/input'
import {
Select,
@@ -14,11 +12,10 @@ import {
SelectTrigger,
SelectValue,
} from '@/components/ui/select'
-import { Check, Copy, FileText, Trash2 } from 'lucide-react'
+import { Check, Copy, Trash2 } from 'lucide-react'
import { IconCode } from '@tabler/icons-react'
-import { ToolPageHeader } from '@/components/tools/tool-page-header'
-import { RevealItem } from '@/components/dashboard/dashboard-reveal'
-import { CATEGORY_ACCENT } from '@/components/dashboard/types'
+import { ToolShell } from '@/components/tools/tool-shell'
+import { ToolPanels, IOPanel, ToolTextArea } from '@/components/tools/io-panel'
import { JSON_TO_CODE_TARGETS, jsonToCode, type JsonToCodeTarget } from '@/lib/json-to-code'
const SAMPLE = `{
@@ -42,91 +39,91 @@ export function JsonToCodeLayout() {
[input, target, rootName],
)
- return (
-
-
-
-
-
-
+ const toolbar = (
+
+
+ setRootName(e.target.value)}
+ className="h-8 w-[120px] text-xs"
+ placeholder={t('rootName')}
+ aria-label={t('rootName')}
+ />
+
+ )
-
-
-
-
-
-
-
-
+ return (
+
+
+
-
-
-
-
-
+ >
+ }
+ >
+
setInput(e.target.value)}
+ placeholder={t('placeholder')}
+ autoComplete="off"
+ />
+
-
-
-
-
- setRootName(e.target.value)}
- className="h-8 w-[120px] text-xs"
- placeholder={t('rootName')}
- aria-label={t('rootName')}
- />
-
+
void copyToClipboard(result.code, { silent: true })}
>
{copied ? : }
{copied ? t('copied') : t('copy')}
-
-
- {result.error ? (
-
{result.error}
- ) : result.code ? (
-
{result.code}
- ) : (
-
{t('outputPlaceholder')}
- )}
-
-
-
-
+ }
+ >
+ {result.error ? (
+
{result.error}
+ ) : result.code ? (
+
{result.code}
+ ) : (
+
{t('outputPlaceholder')}
+ )}
+
+
+
)
}
diff --git a/apps/desktop-ui/src/components/json-visualizer/json-visualizer-layout.tsx b/apps/desktop-ui/src/components/json-visualizer/json-visualizer-layout.tsx
index ac67eb49..c100aba4 100644
--- a/apps/desktop-ui/src/components/json-visualizer/json-visualizer-layout.tsx
+++ b/apps/desktop-ui/src/components/json-visualizer/json-visualizer-layout.tsx
@@ -7,7 +7,6 @@ import { useMediaQuery } from '@/hooks/use-media-query'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
-import { Label } from '@/components/ui/label'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@/components/ui/resizable'
import {
@@ -25,7 +24,6 @@ import {
Copy,
Crosshair,
Download,
- FileText,
Maximize,
Route,
Search,
@@ -35,9 +33,8 @@ import {
ZoomOut,
} from 'lucide-react'
import { IconChartDots3 } from '@tabler/icons-react'
-import { ToolPageHeader } from '@/components/tools/tool-page-header'
-import { RevealItem } from '@/components/dashboard/dashboard-reveal'
-import { CATEGORY_ACCENT } from '@/components/dashboard/types'
+import { ToolShell } from '@/components/tools/tool-shell'
+import { IOPanel, ToolTextArea } from '@/components/tools/io-panel'
import { GraphCanvas, type GraphCanvasHandle } from '@/components/json-visualizer/graph-canvas'
import { NodeTableDialog } from '@/components/json-visualizer/node-table-dialog'
import {
@@ -202,15 +199,12 @@ export function JsonVisualizerLayout() {
}, [selectedNode, parsedText])
const editorPane = (
-
-
-
-
-
-
-
+
setInput(SAMPLE)}>
{t('sample')}
@@ -224,30 +218,29 @@ export function JsonVisualizerLayout() {
>
-
-
+ >
+ }
+ >
{error && (
-
+
{error}
)}
-
-
+
)
const graphPane = (
-
-
+
+
)}
-
)
return (
-
-
-
-
-
-
-
+
{isDesktop ? (
@@ -442,6 +424,6 @@ export function JsonVisualizerLayout() {
title={t('tableTitle')}
/>
)}
-
+
)
}
diff --git a/apps/desktop-ui/src/components/jsonpath-playground/jsonpath-playground-layout.tsx b/apps/desktop-ui/src/components/jsonpath-playground/jsonpath-playground-layout.tsx
index c371e3f1..13bf407e 100644
--- a/apps/desktop-ui/src/components/jsonpath-playground/jsonpath-playground-layout.tsx
+++ b/apps/desktop-ui/src/components/jsonpath-playground/jsonpath-playground-layout.tsx
@@ -7,7 +7,6 @@ import { useTranslations } from 'next-intl'
import { useCopyToClipboard } from '@/hooks/use-copy-to-clipboard'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
-import { Card } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import {
@@ -17,22 +16,18 @@ import {
SelectTrigger,
SelectValue,
} from '@/components/ui/select'
-import { ScrollArea } from '@/components/ui/scroll-area'
import {
BookOpen,
- Braces,
Check,
ChevronDown,
Copy,
- FileText,
Sparkles,
Trash2,
WandSparkles,
} from 'lucide-react'
import { IconZoomCode } from '@tabler/icons-react'
-import { ToolPageHeader } from '@/components/tools/tool-page-header'
-import { RevealItem } from '@/components/dashboard/dashboard-reveal'
-import { CATEGORY_ACCENT } from '@/components/dashboard/types'
+import { ToolShell } from '@/components/tools/tool-shell'
+import { ToolPanels, IOPanel, ToolTextArea } from '@/components/tools/io-panel'
import {
CHEATSHEET,
QUERY_ENGINES,
@@ -82,81 +77,74 @@ export function JsonpathPlaygroundLayout() {
}
}
- return (
-
-
-
-
-
-
+ const toolbar = (
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+ setQuery(e.target.value)}
+ placeholder={engine === 'jsonpath' ? '$.store.books[*].title' : 'store.books[*].title'}
+ className="font-mono text-sm"
+ spellCheck={false}
+ autoComplete="off"
+ />
+
-
-
- setQuery(e.target.value)}
- placeholder={engine === 'jsonpath' ? '$.store.books[*].title' : 'store.books[*].title'}
- className="font-mono text-sm"
- spellCheck={false}
- autoComplete="off"
- />
-
+
setCheatsOpen((o) => !o)}>
+
+ {t('cheatsheet')}
+
+
+
- setCheatsOpen((o) => !o)}>
-
- {t('cheatsheet')}
-
-
+ {cheatsOpen && (
+
+ {CHEATSHEET[engine].map((entry) => (
+ setQuery(entry.expression)}
+ className="flex items-baseline justify-between gap-3 rounded-md px-2.5 py-1.5 text-left transition-colors hover:bg-muted"
+ >
+ {entry.expression}
+ {t(`cheats.${entry.key}`)}
+
+ ))}
+ )}
+
+ )
- {cheatsOpen && (
-
- {CHEATSHEET[engine].map((entry) => (
- setQuery(entry.expression)}
- className="flex items-baseline justify-between gap-3 rounded-md px-2.5 py-1.5 text-left transition-colors hover:bg-muted"
- >
- {entry.expression}
- {t(`cheats.${entry.key}`)}
-
- ))}
-
- )}
-
-
-
-
-
-
-
-
-
-
+ return (
+
+
+
{t('sample')}
@@ -168,31 +156,30 @@ export function JsonpathPlaygroundLayout() {
setJsonText('')} disabled={!jsonText} title={t('clear')}>
-
-
-
-
-
-
-
-
-
-
-
+ >
+ }
+ >
+ setJsonText(e.target.value)}
+ placeholder={t('placeholder')}
+ autoComplete="off"
+ />
+
+
+
+ {t('panels.results')}
{!result.error && result.output && (
{t('matchCount', { count: result.count })}
)}
-
+ >
+ }
+ bodyClassName="overflow-auto"
+ actions={
:
}
{copied ? t('copied') : t('copy')}
-
+ }
+ >
{result.error && (
{result.error}
)}
-
- {result.output ? (
- {result.output}
- ) : (
- {t('outputPlaceholder')}
- )}
-
-
-
-
+ {result.output ? (
+
{result.output}
+ ) : (
+
{t('outputPlaceholder')}
+ )}
+
+
+
)
}
diff --git a/apps/desktop-ui/src/components/markdown-preview-html/markdown-preview-html-layout.tsx b/apps/desktop-ui/src/components/markdown-preview-html/markdown-preview-html-layout.tsx
index f268f3c6..a3dd48f9 100644
--- a/apps/desktop-ui/src/components/markdown-preview-html/markdown-preview-html-layout.tsx
+++ b/apps/desktop-ui/src/components/markdown-preview-html/markdown-preview-html-layout.tsx
@@ -7,16 +7,13 @@ import { marked } from 'marked';
import TurndownService from 'turndown';
import { toast } from 'sonner';
import { safeFileName } from '@/lib/markdown-preview-html';
-import { Card } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
-import { Label } from '@/components/ui/label';
-import { Textarea } from '@/components/ui/textarea';
import { Button } from '@/components/ui/button';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Copy, Check, Download, Eye, Code } from 'lucide-react';
import { IconMarkdown } from '@tabler/icons-react';
-import { ToolPageHeader } from '@/components/tools/tool-page-header';
-import { RevealItem } from '@/components/dashboard/dashboard-reveal';
+import { ToolShell } from '@/components/tools/tool-shell';
+import { IOPanel, ToolTextArea } from '@/components/tools/io-panel';
// Configure marked for safe, synchronous rendering
marked.setOptions({ async: false });
@@ -73,6 +70,12 @@ ${bodyHtml}