Skip to content

Commit c4c8fc3

Browse files
authored
Merge branch 'main' into anu-1.2
2 parents 4ad0bb0 + 02a8920 commit c4c8fc3

15 files changed

Lines changed: 1442 additions & 243 deletions

File tree

‎apps/web/package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"@radix-ui/react-toggle": "^1.1.10",
4343
"@radix-ui/react-toggle-group": "^1.1.11",
4444
"@radix-ui/react-tooltip": "^1.2.8",
45+
"@radix-ui/react-visually-hidden": "^1.2.7",
4546
"@simplewebauthn/browser": "^11.0.0",
4647
"@space-man/react-theme-animation": "^1.1.1",
4748
"@tabler/icons-react": "^3.35.0",

‎apps/web/src/components/diff-checker/diff-checker-layout.tsx‎

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ import { cn } from '@/lib/utils';
1414
import { Trash2 } from 'lucide-react';
1515
import { useTranslations } from 'next-intl';
1616
import { useIsMobile } from '@/components/hooks/use-mobile';
17+
import { IconGitCompare } from '@tabler/icons-react';
18+
import { ToolPageHeader } from '@/components/tools/tool-page-header';
19+
import { ToolMobileTabs } from '@/components/tools/tool-mobile-tabs';
20+
import { RevealItem } from '@/components/dashboard/dashboard-reveal';
1721

1822
const SAMPLE_A = `function greet(name) {
1923
console.log("Hello, " + name);
@@ -150,11 +154,11 @@ export function DiffCheckerLayout() {
150154
);
151155

152156
return (
153-
<div className="flex flex-col h-full gap-3 min-h-0">
154-
<div className="shrink-0 md:hidden">
155-
<h1 className="text-lg font-semibold tracking-tight">{t('title')}</h1>
156-
<p className="text-xs text-muted-foreground">{t('subtitle')}</p>
157-
</div>
157+
<div className="relative flex flex-col h-full gap-4 min-h-0 overflow-hidden dashboard-grid-bg">
158+
<div className="dash-ambient -z-10" aria-hidden />
159+
<RevealItem index={0}>
160+
<ToolPageHeader icon={IconGitCompare} title={t('title')} description={t('subtitle')} />
161+
</RevealItem>
158162

159163
{/* Status + clear row */}
160164
<div className="flex flex-wrap items-center gap-3 text-xs shrink-0">
@@ -177,21 +181,15 @@ export function DiffCheckerLayout() {
177181
{isMobile ? (
178182
/* ── Mobile: 3-tab layout (Original / Modified / Diff) ── */
179183
<>
180-
<div className="flex shrink-0 rounded-lg border bg-muted/40 p-1 gap-1">
181-
{(['original', 'modified', 'diff'] as const).map((tab) => (
182-
<button
183-
key={tab}
184-
onClick={() => setMobileTab(tab)}
185-
className={`flex-1 rounded-md px-2 py-2 text-sm font-medium capitalize transition-all ${
186-
mobileTab === tab
187-
? 'bg-background text-foreground shadow-sm'
188-
: 'text-muted-foreground hover:text-foreground'
189-
}`}
190-
>
191-
{tab === 'original' ? t('original') : tab === 'modified' ? t('modified') : t('comparisonLabel')}
192-
</button>
193-
))}
194-
</div>
184+
<ToolMobileTabs
185+
value={mobileTab}
186+
onValueChange={setMobileTab}
187+
tabs={[
188+
{ value: 'original', label: t('original') },
189+
{ value: 'modified', label: t('modified') },
190+
{ value: 'diff', label: t('comparisonLabel') },
191+
]}
192+
/>
195193

196194
<div className="flex-1 min-h-0 flex flex-col">
197195
{mobileTab === 'original' && (

‎apps/web/src/components/format-converter/format-converter-layout.tsx‎

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ import dynamic from 'next/dynamic';
77
import { useTheme } from 'next-themes';
88
import { useTranslations } from 'next-intl';
99
import { ArrowLeftRight, Copy, Check, AlertCircle } from 'lucide-react';
10+
import { IconFileCode } from '@tabler/icons-react';
11+
import { ToolPageHeader } from '@/components/tools/tool-page-header';
12+
import { ToolMobileTabs } from '@/components/tools/tool-mobile-tabs';
13+
import { RevealItem } from '@/components/dashboard/dashboard-reveal';
1014
import { Card } from '@/components/ui/card';
1115
import { Label } from '@/components/ui/label';
1216
import { Button } from '@/components/ui/button';
@@ -95,29 +99,21 @@ export function FormatConverterLayout() {
9599
}, [to]);
96100

97101
return (
98-
<div className="flex h-full min-h-0 w-full flex-col gap-4">
99-
<div className="shrink-0 md:hidden">
100-
<h1 className="text-lg font-semibold tracking-tight">{t('title')}</h1>
101-
<p className="text-xs text-muted-foreground">{t('subtitle')}</p>
102-
</div>
102+
<div className="relative flex h-full min-h-0 w-full flex-col gap-4 overflow-hidden dashboard-grid-bg">
103+
<div className="dash-ambient -z-10" aria-hidden />
104+
<RevealItem index={0}>
105+
<ToolPageHeader icon={IconFileCode} title={t('title')} description={t('subtitle')} />
106+
</RevealItem>
103107

104108
{isMobile && (
105-
<div className="flex shrink-0 rounded-lg border bg-muted/40 p-1 gap-1">
106-
{(['input', 'output'] as const).map((tab) => (
107-
<button
108-
key={tab}
109-
type="button"
110-
onClick={() => setMobileTab(tab)}
111-
className={`flex-1 rounded-md px-3 py-2 text-sm font-medium capitalize transition-all ${
112-
mobileTab === tab
113-
? 'bg-background text-foreground shadow-sm'
114-
: 'text-muted-foreground hover:text-foreground'
115-
}`}
116-
>
117-
{tab === 'input' ? t('fromLabel') : t('toLabel')}
118-
</button>
119-
))}
120-
</div>
109+
<ToolMobileTabs
110+
value={mobileTab}
111+
onValueChange={setMobileTab}
112+
tabs={[
113+
{ value: 'input', label: t('fromLabel') },
114+
{ value: 'output', label: t('toLabel') },
115+
]}
116+
/>
121117
)}
122118

123119
<div className={`min-h-0 flex-1 ${isMobile ? 'flex flex-col' : 'grid grid-cols-2 gap-4'}`}>

‎apps/web/src/components/graphql-formatter/graphql-formatter-layout.tsx‎

Lines changed: 20 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,18 @@
22

33
import dynamic from 'next/dynamic';
44
import { parse, print, stripIgnoredCharacters } from 'graphql';
5-
import { AlertCircle, Check, Copy, Trash2, Wand2 } from 'lucide-react';
5+
import { AlertCircle, Trash2, Wand2 } from 'lucide-react';
66
import { useTheme } from 'next-themes';
77
import { useCallback, useState } from 'react';
88
import { useCopyToClipboard } from '@/hooks/use-copy-to-clipboard';
99
import { useTranslations } from 'next-intl';
1010
import { registerGraphqlMonarch } from '@/components/graphql-formatter/register-graphql-monarch';
1111
import { useIsMobile } from '@/components/hooks/use-mobile';
12+
import { IconBrandGraphql } from '@tabler/icons-react';
13+
import { ToolPageHeader } from '@/components/tools/tool-page-header';
14+
import { ToolMobileTabs } from '@/components/tools/tool-mobile-tabs';
15+
import { CopyIconButton } from '@/components/tools/copy-icon-button';
16+
import { RevealItem } from '@/components/dashboard/dashboard-reveal';
1217
import { Button } from '@/components/ui/button';
1318
import { Card } from '@/components/ui/card';
1419
import { Input } from '@/components/ui/input';
@@ -122,11 +127,11 @@ export function GraphqlFormatterLayout() {
122127
};
123128

124129
return (
125-
<div className="flex flex-col h-full gap-4 min-h-0">
126-
<div className="shrink-0 md:hidden">
127-
<h1 className="text-lg font-semibold tracking-tight">{t('title')}</h1>
128-
<p className="text-xs text-muted-foreground">{t('subtitle')}</p>
129-
</div>
130+
<div className="relative flex flex-col h-full gap-4 min-h-0 overflow-hidden dashboard-grid-bg">
131+
<div className="dash-ambient -z-10" aria-hidden />
132+
<RevealItem index={0}>
133+
<ToolPageHeader icon={IconBrandGraphql} title={t('title')} description={t('subtitle')} />
134+
</RevealItem>
130135

131136
<Card className="p-4 shrink-0 space-y-4">
132137
<Tabs value={panelTab} onValueChange={(v) => setPanelTab(v as 'format' | 'build')}>
@@ -244,30 +249,14 @@ export function GraphqlFormatterLayout() {
244249
)}
245250

246251
{isMobile && (
247-
<div className="flex shrink-0 rounded-lg border bg-muted/40 p-1 gap-1">
248-
<button
249-
type="button"
250-
onClick={() => setMobileTab('input')}
251-
className={`flex-1 rounded-md px-3 py-2 text-sm font-medium transition-all ${
252-
mobileTab === 'input'
253-
? 'bg-background text-foreground shadow-sm'
254-
: 'text-muted-foreground hover:text-foreground'
255-
}`}
256-
>
257-
{t('inputPanel')}
258-
</button>
259-
<button
260-
type="button"
261-
onClick={() => setMobileTab('output')}
262-
className={`flex-1 rounded-md px-3 py-2 text-sm font-medium transition-all ${
263-
mobileTab === 'output'
264-
? 'bg-background text-foreground shadow-sm'
265-
: 'text-muted-foreground hover:text-foreground'
266-
}`}
267-
>
268-
{t('outputPanel')}
269-
</button>
270-
</div>
252+
<ToolMobileTabs
253+
value={mobileTab}
254+
onValueChange={setMobileTab}
255+
tabs={[
256+
{ value: 'input', label: t('inputPanel') },
257+
{ value: 'output', label: t('outputPanel') },
258+
]}
259+
/>
271260
)}
272261

273262
<div
@@ -297,17 +286,7 @@ export function GraphqlFormatterLayout() {
297286
<Label className="text-xs font-medium text-muted-foreground uppercase tracking-wider">
298287
{t('outputPanel')}
299288
</Label>
300-
<Button
301-
type="button"
302-
variant="ghost"
303-
size="icon"
304-
className="h-7 w-7 shrink-0"
305-
disabled={!output}
306-
title={t('copy')}
307-
onClick={handleCopy}
308-
>
309-
{copied ? <Check className="h-3.5 w-3.5 text-green-500" /> : <Copy className="h-3.5 w-3.5" />}
310-
</Button>
289+
<CopyIconButton onCopy={handleCopy} copied={copied} disabled={!output} label={t('copy')} />
311290
</div>
312291
<div className="flex-1 min-h-[240px] relative p-1">
313292
<CodeEditor

‎apps/web/src/components/json-formatter/json-formatter-layout.tsx‎

Lines changed: 17 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ import {
1515
import { toast } from 'sonner'
1616
import { Mode, toTextContent, type Content, type OnChangeStatus } from 'vanilla-jsoneditor'
1717
import { fetchAllPages } from '@/lib/fetch-all-pages'
18-
import { Card, CardHeader } from '@/components/ui/card'
18+
import { ToolPageHeader } from '@/components/tools/tool-page-header'
19+
import { ToolMobileTabs } from '@/components/tools/tool-mobile-tabs'
20+
import { RevealItem } from '@/components/dashboard/dashboard-reveal'
1921
import { Button } from '@/components/ui/button'
2022
import { SendToMenu } from '@/components/ui/send-to-menu'
2123
import useAuth from '@/utils/useAuth'
@@ -318,51 +320,22 @@ export function JsonFormatterLayout() {
318320
}
319321

320322
return (
321-
<div className="flex h-full min-h-0 w-full flex-col gap-3 overflow-hidden">
322-
<Card className="shrink-0 border shadow-lg bg-card/50 backdrop-blur-sm md:hidden">
323-
<CardHeader className="p-3 md:pb-3">
324-
<div className="flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
325-
<div className="flex items-center gap-2.5">
326-
<div className="rounded-xl bg-gradient-to-br from-primary/15 to-violet-500/10 p-2 text-primary shadow-sm ring-1 ring-inset ring-border/50 transition-all hover:scale-105">
327-
<IconJson className="h-5 w-5 text-primary" aria-hidden />
328-
</div>
329-
<div className="flex flex-col gap-0.5">
330-
<h1 className="text-lg font-bold tracking-tight text-foreground md:text-xl">
331-
{t('title')}
332-
</h1>
333-
<p className="text-xs text-muted-foreground md:text-sm">
334-
{t('description')}
335-
</p>
336-
</div>
337-
</div>
338-
</div>
339-
</CardHeader>
340-
</Card>
323+
<div className="relative flex h-full min-h-0 w-full flex-col gap-4 overflow-hidden dashboard-grid-bg">
324+
<div className="dash-ambient -z-10" aria-hidden />
325+
326+
<RevealItem index={0}>
327+
<ToolPageHeader icon={IconJson} title={t('title')} description={t('description')} />
328+
</RevealItem>
341329

342-
{/* Mobile tab switcher */}
343330
{isMobile && (
344-
<div className="flex shrink-0 rounded-lg border bg-muted/40 p-1 gap-1">
345-
<button
346-
onClick={() => setActivePane('left')}
347-
className={`flex-1 rounded-md px-3 py-2 text-sm font-medium transition-all ${
348-
activePane === 'left'
349-
? 'bg-background text-foreground shadow-sm'
350-
: 'text-muted-foreground hover:text-foreground'
351-
}`}
352-
>
353-
Text
354-
</button>
355-
<button
356-
onClick={() => setActivePane('right')}
357-
className={`flex-1 rounded-md px-3 py-2 text-sm font-medium transition-all ${
358-
activePane === 'right'
359-
? 'bg-background text-foreground shadow-sm'
360-
: 'text-muted-foreground hover:text-foreground'
361-
}`}
362-
>
363-
Tree
364-
</button>
365-
</div>
331+
<ToolMobileTabs
332+
value={activePane}
333+
onValueChange={setActivePane}
334+
tabs={[
335+
{ value: 'left', label: 'Text' },
336+
{ value: 'right', label: 'Tree' },
337+
]}
338+
/>
366339
)}
367340

368341
{isMobile ? (

‎apps/web/src/components/json-schema-generator/json-schema-generator-layout.tsx‎

Lines changed: 18 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ import { useCallback, useMemo, useState } from 'react';
44
import { useCopyToClipboard } from '@/hooks/use-copy-to-clipboard';
55
import { useIsMobile } from '@/components/hooks/use-mobile';
66
import { useTranslations } from 'next-intl';
7-
import { AlertCircle, Check, Copy, Download, FileJson, Trash2 } from 'lucide-react';
7+
import { AlertCircle, Check, Copy, Download, Trash2 } from 'lucide-react';
8+
import { IconBraces } from '@tabler/icons-react';
9+
import { ToolPageHeader } from '@/components/tools/tool-page-header';
10+
import { ToolMobileTabs } from '@/components/tools/tool-mobile-tabs';
11+
import { RevealItem } from '@/components/dashboard/dashboard-reveal';
812
import { Button } from '@/components/ui/button';
913
import { Card } from '@/components/ui/card';
1014
import { Label } from '@/components/ui/label';
@@ -107,18 +111,11 @@ export function JsonSchemaGeneratorLayout() {
107111
};
108112

109113
return (
110-
<div className="flex h-full flex-col gap-4">
111-
<div className="flex shrink-0 flex-col gap-3 sm:flex-row sm:items-center sm:justify-between md:hidden">
112-
<div className="flex items-start gap-2.5">
113-
<div className="rounded-xl bg-primary/10 p-2 shadow-sm">
114-
<FileJson className="h-5 w-5 text-primary" aria-hidden />
115-
</div>
116-
<div>
117-
<h1 className="text-lg font-bold tracking-tight">{t('title')}</h1>
118-
<p className="text-xs text-muted-foreground">{t('subtitle')}</p>
119-
</div>
120-
</div>
121-
</div>
114+
<div className="relative flex h-full flex-col gap-4 overflow-hidden dashboard-grid-bg">
115+
<div className="dash-ambient -z-10" aria-hidden />
116+
<RevealItem index={0}>
117+
<ToolPageHeader icon={IconBraces} title={t('title')} description={t('subtitle')} />
118+
</RevealItem>
122119

123120
<div className="flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between">
124121
<div className="flex flex-col gap-1.5 sm:min-w-[240px]">
@@ -167,22 +164,14 @@ export function JsonSchemaGeneratorLayout() {
167164
</div>
168165

169166
{isMobile && (
170-
<div className="flex shrink-0 rounded-lg border bg-muted/40 p-1 gap-1">
171-
{(['input', 'output'] as const).map((tab) => (
172-
<button
173-
key={tab}
174-
type="button"
175-
onClick={() => setMobileTab(tab)}
176-
className={`flex-1 rounded-md px-3 py-2 text-sm font-medium capitalize transition-all ${
177-
mobileTab === tab
178-
? 'bg-background text-foreground shadow-sm'
179-
: 'text-muted-foreground hover:text-foreground'
180-
}`}
181-
>
182-
{tab === 'input' ? t('inputLabel') : t('outputLabel')}
183-
</button>
184-
))}
185-
</div>
167+
<ToolMobileTabs
168+
value={mobileTab}
169+
onValueChange={setMobileTab}
170+
tabs={[
171+
{ value: 'input', label: t('inputLabel') },
172+
{ value: 'output', label: t('outputLabel') },
173+
]}
174+
/>
186175
)}
187176

188177
<div className={`min-h-0 flex-1 ${isMobile ? 'flex flex-col' : 'grid grid-cols-2 gap-4'}`}>

‎apps/web/src/components/markdown-preview-html/markdown-preview-html-layout.tsx‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ import { Textarea } from '@/components/ui/textarea';
1111
import { Button } from '@/components/ui/button';
1212
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
1313
import { Copy, Check, Download, Eye, Code } from 'lucide-react';
14+
import { IconMarkdown } from '@tabler/icons-react';
15+
import { ToolPageHeader } from '@/components/tools/tool-page-header';
16+
import { RevealItem } from '@/components/dashboard/dashboard-reveal';
1417

1518
// Configure marked for safe, synchronous rendering
1619
marked.setOptions({ async: false });
@@ -104,11 +107,11 @@ export function MarkdownPreviewLayout() {
104107
}, [convertedMarkdown, copyMd]);
105108

106109
return (
107-
<div className="flex h-full min-h-0 w-full flex-col gap-4">
108-
<div className="shrink-0 md:hidden">
109-
<h1 className="text-lg font-semibold tracking-tight">{t('title')}</h1>
110-
<p className="text-xs text-muted-foreground">{t('subtitle')}</p>
111-
</div>
110+
<div className="relative flex h-full min-h-0 w-full flex-col gap-4 overflow-hidden dashboard-grid-bg">
111+
<div className="dash-ambient -z-10" aria-hidden />
112+
<RevealItem index={0}>
113+
<ToolPageHeader icon={IconMarkdown} title={t('title')} description={t('subtitle')} />
114+
</RevealItem>
112115

113116
<Tabs defaultValue="md-to-html" className="flex h-full min-h-0 flex-col gap-4">
114117
<TabsList className="shrink-0 w-fit">

0 commit comments

Comments
 (0)