diff --git a/pages/diff.tsx b/pages/diff.tsx
new file mode 100644
index 000000000..a406e2b76
--- /dev/null
+++ b/pages/diff.tsx
@@ -0,0 +1,10 @@
+import * as React from 'react';
+
+import { AppDiff } from '../src/apps/diff/AppDiff';
+
+import { withLayout } from '~/common/layout/withLayout';
+
+
+export default function DiffPage() {
+ return withLayout({ type: 'optima' }, );
+}
\ No newline at end of file
diff --git a/src/apps/AppSmallContainer.tsx b/src/apps/AppSmallContainer.tsx
index e4d70aa56..b5a307fe5 100644
--- a/src/apps/AppSmallContainer.tsx
+++ b/src/apps/AppSmallContainer.tsx
@@ -5,7 +5,7 @@ import { Box, Container, Typography } from '@mui/joy';
export function AppSmallContainer({ title, description, children }: {
title: string;
- description: string;
+ description: React.ReactNode;
children: React.ReactNode;
}) {
return (
diff --git a/src/apps/diff/AppDiff.tsx b/src/apps/diff/AppDiff.tsx
new file mode 100644
index 000000000..662f6c25c
--- /dev/null
+++ b/src/apps/diff/AppDiff.tsx
@@ -0,0 +1,178 @@
+import * as React from 'react';
+
+import type { SxProps } from '@mui/joy/styles/types';
+import { Box, Card, Divider, IconButton, Textarea, Tooltip, Typography } from '@mui/joy';
+import SwapHorizIcon from '@mui/icons-material/SwapHoriz';
+
+import { RenderTextDiff, useSanityTextDiffs } from '~/modules/blocks/textdiff/RenderTextDiff';
+
+import { themeScalingMap } from '~/common/app.theme';
+import { useIsMobile } from '~/common/components/useMatchMedia';
+import { useUIPreferencesStore } from '~/common/state/store-ui';
+
+import { AppSmallContainer } from '../AppSmallContainer';
+import { GlobalShortcutDefinition, useGlobalShortcuts } from '~/common/components/useGlobalShortcuts';
+import { countWords } from '~/common/util/textUtils';
+
+
+export function AppDiff() {
+
+ // state
+ const [text1, setText1] = React.useState('This is the Original text...');
+ const [text2, setText2] = React.useState('This is the Modified text...');
+ const [isSwapping, setIsSwapping] = React.useState(false);
+
+
+ // external state
+ const isMobile = useIsMobile();
+ const contentScaling = useUIPreferencesStore(state => state.contentScaling);
+ const diffs = useSanityTextDiffs(text2 || '', text1 || '', true);
+
+ // memos
+ const handleSwap = React.useCallback(() => {
+ setIsSwapping(true);
+ setTimeout(() => {
+ const temp = text1;
+ setText1(text2);
+ setText2(temp);
+ setIsSwapping(false);
+ }, 200); // sync this with the transition duration
+ }, [text1, text2]);
+
+ const scaledTypographySx: SxProps = React.useMemo(() => ({
+ fontSize: themeScalingMap[contentScaling]?.blockFontSize ?? undefined,
+ lineHeight: themeScalingMap[contentScaling]?.blockLineHeight ?? 1.75,
+ }), [contentScaling]);
+
+ // this is a repetition.. shall move this to a shared root component
+ const shortcuts = React.useMemo((): GlobalShortcutDefinition[] => [
+ ['+', true, true, false, useUIPreferencesStore.getState().increaseContentScaling],
+ ['-', true, true, false, useUIPreferencesStore.getState().decreaseContentScaling],
+ ], []);
+ useGlobalShortcuts(shortcuts);
+
+ const c1 = text1?.length || 0;
+ const c2 = text2?.length || 0;
+ const w1 = countWords(text1);
+ const w2 = countWords(text2);
+
+
+ return (
+
+
+
+
+
+
+
+
+ {diffs?.length ? (
+
+
+
+
+ Differences
+
+
+
+
+
+
+
+
+ Red: Deleted, Green: Added.
+
+
+
+ ) : (
+
+ Enter or paste your texts and the differences will be highlighted here.
+
+ )}
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/common/app.nav.ts b/src/common/app.nav.ts
index dd2fafdc2..b640886fa 100644
--- a/src/common/app.nav.ts
+++ b/src/common/app.nav.ts
@@ -164,7 +164,7 @@ export const navItems: {
hideBar: true,
},
{
- name: 'Compare',
+ name: 'Compare Text',
barTitle: 'Comparison',
icon: DifferenceOutlinedIcon,
type: 'app',
@@ -172,7 +172,7 @@ export const navItems: {
hideDrawer: true,
},
{
- name: 'Tokenize',
+ name: 'Tokenize Text',
barTitle: 'Tokenization',
icon: GrainIcon,
type: 'app',
diff --git a/src/common/util/textUtils.ts b/src/common/util/textUtils.ts
index 6637ea723..7470cb381 100644
--- a/src/common/util/textUtils.ts
+++ b/src/common/util/textUtils.ts
@@ -2,6 +2,13 @@ export function capitalizeFirstLetter(string: string) {
return string?.length ? (string.charAt(0).toUpperCase() + string.slice(1)) : string;
}
+
+export function countWords(text: string) {
+ const trimmedText = text.trim();
+ if (!trimmedText) return 0;
+ return trimmedText.split(/\s+/).length;
+}
+
/**
* Convert a string (e.g., a web URL or file name) to a human-readable hyphenated format.
* This function: