Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
082dcfa
Add design spec for formatters/dashboard UI alignment
itsmeakhil Jul 2, 2026
cd8475b
Add implementation plan for formatters/dashboard UI alignment
itsmeakhil Jul 2, 2026
9905c16
feat: add shared ToolPageHeader component for formatter pages
itsmeakhil Jul 2, 2026
dbd5f0e
feat: add shared ToolMobileTabs component for formatter pages
itsmeakhil Jul 2, 2026
7d80c9c
feat: add shared CopyIconButton component for formatter output panels
itsmeakhil Jul 2, 2026
fb986cf
refactor: apply shared ToolPageHeader/ToolMobileTabs to JSON formatter
itsmeakhil Jul 2, 2026
294e601
fix: declare @radix-ui/react-visually-hidden as a direct dependency
itsmeakhil Jul 2, 2026
eba287d
refactor: apply shared tool-page components to YAML formatter
itsmeakhil Jul 2, 2026
06c10e1
refactor: apply shared tool-page components to SQL formatter
itsmeakhil Jul 2, 2026
29dc044
refactor: apply shared tool-page components to GraphQL formatter
itsmeakhil Jul 2, 2026
b0fb9f8
refactor: apply shared ToolPageHeader/ToolMobileTabs to JSON Schema G…
itsmeakhil Jul 2, 2026
71a29cd
refactor: apply shared ToolPageHeader to Markdown Preview
itsmeakhil Jul 2, 2026
469e314
refactor: apply shared tool-page components to Diff Checker
itsmeakhil Jul 2, 2026
dc894e9
refactor: apply shared ToolPageHeader to Regex Tester
itsmeakhil Jul 2, 2026
fbcad05
refactor: apply shared tool-page components to Format Converter
itsmeakhil Jul 2, 2026
fe3730f
fix: normalize JSON formatter root gap and mobile-tab reveal to match…
itsmeakhil Jul 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apps/api-runner/node_modules/.bin/tsc

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions apps/api-runner/node_modules/.bin/tsserver

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"@radix-ui/react-toggle": "^1.1.10",
"@radix-ui/react-toggle-group": "^1.1.11",
"@radix-ui/react-tooltip": "^1.2.8",
"@radix-ui/react-visually-hidden": "^1.2.7",
"@simplewebauthn/browser": "^11.0.0",
"@space-man/react-theme-animation": "^1.1.1",
"@tabler/icons-react": "^3.35.0",
Expand Down
38 changes: 18 additions & 20 deletions apps/web/src/components/diff-checker/diff-checker-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ import { cn } from '@/lib/utils';
import { Trash2 } from 'lucide-react';
import { useTranslations } from 'next-intl';
import { useIsMobile } from '@/components/hooks/use-mobile';
import { IconGitCompare } from '@tabler/icons-react';
import { ToolPageHeader } from '@/components/tools/tool-page-header';
import { ToolMobileTabs } from '@/components/tools/tool-mobile-tabs';
import { RevealItem } from '@/components/dashboard/dashboard-reveal';

const SAMPLE_A = `function greet(name) {
console.log("Hello, " + name);
Expand Down Expand Up @@ -150,11 +154,11 @@ export function DiffCheckerLayout() {
);

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

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

<div className="flex-1 min-h-0 flex flex-col">
{mobileTab === 'original' && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import dynamic from 'next/dynamic';
import { useTheme } from 'next-themes';
import { useTranslations } from 'next-intl';
import { ArrowLeftRight, Copy, Check, AlertCircle } from 'lucide-react';
import { IconFileCode } from '@tabler/icons-react';
import { ToolPageHeader } from '@/components/tools/tool-page-header';
import { ToolMobileTabs } from '@/components/tools/tool-mobile-tabs';
import { RevealItem } from '@/components/dashboard/dashboard-reveal';
import { Card } from '@/components/ui/card';
import { Label } from '@/components/ui/label';
import { Button } from '@/components/ui/button';
Expand Down Expand Up @@ -95,29 +99,21 @@ export function FormatConverterLayout() {
}, [to]);

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

{isMobile && (
<div className="flex shrink-0 rounded-lg border bg-muted/40 p-1 gap-1">
{(['input', 'output'] as const).map((tab) => (
<button
key={tab}
type="button"
onClick={() => setMobileTab(tab)}
className={`flex-1 rounded-md px-3 py-2 text-sm font-medium capitalize transition-all ${
mobileTab === tab
? 'bg-background text-foreground shadow-sm'
: 'text-muted-foreground hover:text-foreground'
}`}
>
{tab === 'input' ? t('fromLabel') : t('toLabel')}
</button>
))}
</div>
<ToolMobileTabs
value={mobileTab}
onValueChange={setMobileTab}
tabs={[
{ value: 'input', label: t('fromLabel') },
{ value: 'output', label: t('toLabel') },
]}
/>
)}

<div className={`min-h-0 flex-1 ${isMobile ? 'flex flex-col' : 'grid grid-cols-2 gap-4'}`}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@

import dynamic from 'next/dynamic';
import { parse, print, stripIgnoredCharacters } from 'graphql';
import { AlertCircle, Check, Copy, Trash2, Wand2 } from 'lucide-react';
import { AlertCircle, Trash2, Wand2 } from 'lucide-react';
import { useTheme } from 'next-themes';
import { useCallback, useState } from 'react';
import { useCopyToClipboard } from '@/hooks/use-copy-to-clipboard';
import { useTranslations } from 'next-intl';
import { registerGraphqlMonarch } from '@/components/graphql-formatter/register-graphql-monarch';
import { useIsMobile } from '@/components/hooks/use-mobile';
import { IconBrandGraphql } from '@tabler/icons-react';
import { ToolPageHeader } from '@/components/tools/tool-page-header';
import { ToolMobileTabs } from '@/components/tools/tool-mobile-tabs';
import { CopyIconButton } from '@/components/tools/copy-icon-button';
import { RevealItem } from '@/components/dashboard/dashboard-reveal';
import { Button } from '@/components/ui/button';
import { Card } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
Expand Down Expand Up @@ -122,11 +127,11 @@ export function GraphqlFormatterLayout() {
};

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

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

{isMobile && (
<div className="flex shrink-0 rounded-lg border bg-muted/40 p-1 gap-1">
<button
type="button"
onClick={() => setMobileTab('input')}
className={`flex-1 rounded-md px-3 py-2 text-sm font-medium transition-all ${
mobileTab === 'input'
? 'bg-background text-foreground shadow-sm'
: 'text-muted-foreground hover:text-foreground'
}`}
>
{t('inputPanel')}
</button>
<button
type="button"
onClick={() => setMobileTab('output')}
className={`flex-1 rounded-md px-3 py-2 text-sm font-medium transition-all ${
mobileTab === 'output'
? 'bg-background text-foreground shadow-sm'
: 'text-muted-foreground hover:text-foreground'
}`}
>
{t('outputPanel')}
</button>
</div>
<ToolMobileTabs
value={mobileTab}
onValueChange={setMobileTab}
tabs={[
{ value: 'input', label: t('inputPanel') },
{ value: 'output', label: t('outputPanel') },
]}
/>
)}

<div
Expand Down Expand Up @@ -297,17 +286,7 @@ export function GraphqlFormatterLayout() {
<Label className="text-xs font-medium text-muted-foreground uppercase tracking-wider">
{t('outputPanel')}
</Label>
<Button
type="button"
variant="ghost"
size="icon"
className="h-7 w-7 shrink-0"
disabled={!output}
title={t('copy')}
onClick={handleCopy}
>
{copied ? <Check className="h-3.5 w-3.5 text-green-500" /> : <Copy className="h-3.5 w-3.5" />}
</Button>
<CopyIconButton onCopy={handleCopy} copied={copied} disabled={!output} label={t('copy')} />
</div>
<div className="flex-1 min-h-[240px] relative p-1">
<CodeEditor
Expand Down
61 changes: 17 additions & 44 deletions apps/web/src/components/json-formatter/json-formatter-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ import {
import { toast } from 'sonner'
import { Mode, toTextContent, type Content, type OnChangeStatus } from 'vanilla-jsoneditor'
import { fetchAllPages } from '@/lib/fetch-all-pages'
import { Card, CardHeader } from '@/components/ui/card'
import { ToolPageHeader } from '@/components/tools/tool-page-header'
import { ToolMobileTabs } from '@/components/tools/tool-mobile-tabs'
import { RevealItem } from '@/components/dashboard/dashboard-reveal'
import { Button } from '@/components/ui/button'
import { SendToMenu } from '@/components/ui/send-to-menu'
import useAuth from '@/utils/useAuth'
Expand Down Expand Up @@ -318,51 +320,22 @@ export function JsonFormatterLayout() {
}

return (
<div className="flex h-full min-h-0 w-full flex-col gap-3 overflow-hidden">
<Card className="shrink-0 border shadow-lg bg-card/50 backdrop-blur-sm md:hidden">
<CardHeader className="p-3 md:pb-3">
<div className="flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
<div className="flex items-center gap-2.5">
<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">
<IconJson className="h-5 w-5 text-primary" aria-hidden />
</div>
<div className="flex flex-col gap-0.5">
<h1 className="text-lg font-bold tracking-tight text-foreground md:text-xl">
{t('title')}
</h1>
<p className="text-xs text-muted-foreground md:text-sm">
{t('description')}
</p>
</div>
</div>
</div>
</CardHeader>
</Card>
<div className="relative flex h-full min-h-0 w-full flex-col gap-4 overflow-hidden dashboard-grid-bg">
<div className="dash-ambient -z-10" aria-hidden />

<RevealItem index={0}>
<ToolPageHeader icon={IconJson} title={t('title')} description={t('description')} />
</RevealItem>

{/* Mobile tab switcher */}
{isMobile && (
<div className="flex shrink-0 rounded-lg border bg-muted/40 p-1 gap-1">
<button
onClick={() => setActivePane('left')}
className={`flex-1 rounded-md px-3 py-2 text-sm font-medium transition-all ${
activePane === 'left'
? 'bg-background text-foreground shadow-sm'
: 'text-muted-foreground hover:text-foreground'
}`}
>
Text
</button>
<button
onClick={() => setActivePane('right')}
className={`flex-1 rounded-md px-3 py-2 text-sm font-medium transition-all ${
activePane === 'right'
? 'bg-background text-foreground shadow-sm'
: 'text-muted-foreground hover:text-foreground'
}`}
>
Tree
</button>
</div>
<ToolMobileTabs
value={activePane}
onValueChange={setActivePane}
tabs={[
{ value: 'left', label: 'Text' },
{ value: 'right', label: 'Tree' },
]}
/>
)}

{isMobile ? (
Expand Down
Loading