- {previewUrl ? (
- // eslint-disable-next-line @next/next/no-img-element
-

- ) : (
-
fileInputRef.current?.click()}
- >
-
-
-
-
-
{t('dropHere')}
-
{t('placeholders.uploadImage')}
-
- {error && (
-
- )}
+ ) : null
+ }
+ >
+ {previewUrl ? (
+ // eslint-disable-next-line @next/next/no-img-element
+

+ ) : (
+
fileInputRef.current?.click()}
+ >
+
+
- )}
-
- {/* Overlay when dragging */}
- {isDragging && (
-
-
-
-
{t('dropHere')}
+
+
{t('dropHere')}
+
{t('placeholders.uploadImage')}
+
+ {error && (
+
+ )}
+
+ )}
+
+ {/* Overlay when dragging */}
+ {isDragging && (
+
+
+
+ {t('dropHere')}
- )}
-
-
+
+ )}
+
{/* Output */}
-
-
-
-
-
-
-
+
+
+ {t('panels.output')}
+ >
+ }
+ actions={
+ <>
{t('charCount', { count: outputCharCount.toLocaleString() })}
@@ -279,18 +272,12 @@ export function ImageToBase64Layout() {
)}
-
-
-
-
-
-
-
-
+ >
+ }
+ >
+
+
+
+
);
}
diff --git a/apps/desktop-ui/src/components/line-sort-dedupe/line-sort-dedupe-layout.tsx b/apps/desktop-ui/src/components/line-sort-dedupe/line-sort-dedupe-layout.tsx
index d081457d..0b45f14e 100644
--- a/apps/desktop-ui/src/components/line-sort-dedupe/line-sort-dedupe-layout.tsx
+++ b/apps/desktop-ui/src/components/line-sort-dedupe/line-sort-dedupe-layout.tsx
@@ -4,7 +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 { Switch } from '@/components/ui/switch'
import {
@@ -14,11 +13,10 @@ import {
SelectTrigger,
SelectValue,
} from '@/components/ui/select'
-import { Check, Copy, FileText, Trash2 } from 'lucide-react'
+import { Check, Copy, Trash2 } from 'lucide-react'
import { IconArrowsSort } 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'
type SortMode = 'none' | 'asc' | 'desc' | 'natural' | 'length' | 'reverse'
@@ -109,82 +107,69 @@ export function LineSortDedupeLayout() {
{ key: 'caseInsensitive', label: t('opts.caseInsensitive') },
]
+ const toolbar = (
+
+
+
+
+
+
+ {toggles.map((tg) => (
+
+ ))}
+
+
+ )
+
return (
-
-
-
-
-
-
-
-
-
-
-
-
-
- {toggles.map((tg) => (
-
- ))}
-
-
-
-
-
-
-
-
-
-
-
+
+
+
{t('lineCount', { count: inCount })}
-
-
-
-
-
-
-
-
-
-
-
-
-
+ >
+ }
+ >
+ setInput(e.target.value)}
+ placeholder={t('placeholder')}
+ autoComplete="off"
+ />
+
+
+
{t('lineCount', { count: outCount })}
: }
{copied ? t('copied') : t('copy')}
-
-
-
-
-
-
-
-
+ >
+ }
+ >
+
+
+
+
)
}
diff --git a/apps/desktop-ui/src/components/number-base-converter/number-base-converter-layout.tsx b/apps/desktop-ui/src/components/number-base-converter/number-base-converter-layout.tsx
index 5e2e308f..dfcab161 100644
--- a/apps/desktop-ui/src/components/number-base-converter/number-base-converter-layout.tsx
+++ b/apps/desktop-ui/src/components/number-base-converter/number-base-converter-layout.tsx
@@ -4,9 +4,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
import { useCopyToClipboard } from '@/hooks/use-copy-to-clipboard';
import { useTranslations } from 'next-intl';
import { useDebouncedCallback } from 'use-debounce';
-import { Card } from '@/components/ui/card';
import { Label } from '@/components/ui/label';
-import { Textarea } from '@/components/ui/textarea';
import { Button } from '@/components/ui/button';
import {
Select,
@@ -24,9 +22,8 @@ import {
type NumberBase,
} from '@/lib/number-base';
import { IconArrowsExchange } 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';
export function NumberBaseConverterLayout() {
const t = useTranslations('NumberBaseConverter');
@@ -77,96 +74,98 @@ export function NumberBaseConverterLayout() {
if (output) setInput(output);
};
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ const toolbar = (
+
+
+
+
+
-
+
-
-
-
-
-
+
+
+
+
+
+ );
-
-
+
+ {t('note')}
+
+
+
+
);
}
-
diff --git a/apps/desktop-ui/src/components/string-case-converter/string-case-converter-layout.tsx b/apps/desktop-ui/src/components/string-case-converter/string-case-converter-layout.tsx
index 25d02a37..cae1e217 100644
--- a/apps/desktop-ui/src/components/string-case-converter/string-case-converter-layout.tsx
+++ b/apps/desktop-ui/src/components/string-case-converter/string-case-converter-layout.tsx
@@ -4,13 +4,10 @@ import React, { useCallback, 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 { Check, Copy, FileText, Trash2 } from 'lucide-react'
+import { Check, Copy, Trash2 } from 'lucide-react'
import { IconLetterCase } 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'
/** Split arbitrary text into normalized words, honouring camelCase / ACRONYM / separators. */
function splitWords(input: string): string[] {
@@ -69,52 +66,31 @@ export function StringCaseConverterLayout() {
)
return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
{t('wordCount', { count: words.length })}
-
-
-
-
-
+ >
+ }
+ >
+
setInput(e.target.value)}
+ placeholder={t('placeholder')}
+ autoComplete="off"
+ />
+
-
-
-
-
-
-
+
+
{results.map((r) => (
@@ -129,8 +105,8 @@ export function StringCaseConverterLayout() {
))}
-
-
-
+
+
+
)
}
diff --git a/apps/desktop-ui/src/components/string-inspector/string-inspector-layout.tsx b/apps/desktop-ui/src/components/string-inspector/string-inspector-layout.tsx
index 09f9eb6a..e05a9f7c 100644
--- a/apps/desktop-ui/src/components/string-inspector/string-inspector-layout.tsx
+++ b/apps/desktop-ui/src/components/string-inspector/string-inspector-layout.tsx
@@ -2,14 +2,11 @@
import React, { useMemo, useState } from 'react'
import { useTranslations } from 'next-intl'
-import { Card } from '@/components/ui/card'
-import { Label } from '@/components/ui/label'
import { Button } from '@/components/ui/button'
-import { FileText, Trash2 } from 'lucide-react'
+import { Trash2 } from 'lucide-react'
import { IconTextScan2 } 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'
interface SpecialChar {
cp: number
@@ -109,42 +106,29 @@ export function StringInspectorLayout() {
]
return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+ setInput('')} disabled={!input} title={t('clear')}>
-
-
-
-
-
-
+ }
+ >
+ setInput(e.target.value)}
+ placeholder={t('placeholder')}
+ autoComplete="off"
+ />
+
+
+
{primary.map((p) => (
@@ -193,8 +177,8 @@ export function StringInspectorLayout() {
)}
-
-
-
+
+
+
)
}
diff --git a/apps/desktop-ui/src/components/timestamp-converter/timestamp-converter-layout.tsx b/apps/desktop-ui/src/components/timestamp-converter/timestamp-converter-layout.tsx
index 9363ddd3..2aea914a 100644
--- a/apps/desktop-ui/src/components/timestamp-converter/timestamp-converter-layout.tsx
+++ b/apps/desktop-ui/src/components/timestamp-converter/timestamp-converter-layout.tsx
@@ -3,16 +3,13 @@
import { useMemo, useState } from 'react';
import { useCopyToClipboard } from '@/hooks/use-copy-to-clipboard';
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 { formatTimestampAll, parseTimestampInput } from '@/lib/timestamp-convert';
import { Check, Copy } from 'lucide-react';
import { useTranslations } from 'next-intl';
import { IconCalendarTime } 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';
function CopyField({
label,
@@ -26,11 +23,11 @@ function CopyField({
const { isCopied: done, copyToClipboard } = useCopyToClipboard();
return (
-
+
{label}
-
-
+
+