From f088f6897108ebe8fdf47499dc484e4c1f7a6516 Mon Sep 17 00:00:00 2001 From: arsam Date: Fri, 18 Sep 2026 23:25:28 +0330 Subject: [PATCH] Import source: app/src/pages/NlpNormalizerPage Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality. --- app/src/pages/NlpNormalizerPage.jsx | 130 ++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 app/src/pages/NlpNormalizerPage.jsx diff --git a/app/src/pages/NlpNormalizerPage.jsx b/app/src/pages/NlpNormalizerPage.jsx new file mode 100644 index 0000000..fcccb79 --- /dev/null +++ b/app/src/pages/NlpNormalizerPage.jsx @@ -0,0 +1,130 @@ +import { useState } from 'react'; +import { useNav, useToast } from '../../teknav-ui.jsx'; +import { iconNode } from '../../src/lib/icon.jsx'; +import clipboardIcon from '../../src/assets/icons/clipboard-copy.svg'; +import { LegalPage, LegalSection } from './shared.jsx'; + +export function NlpNormalizerPage() { + const { navigate } = useNav(); + const toast = useToast(); + const [text, setText] = useState(''); + const [normalized, setNormalized] = useState(''); + const [options, setOptions] = useState({ + stripHtml: true, + normalizeLetters: true, + normalizeZwnj: true, + cleanWhitespace: true, + }); + + const toggle = (k) => setOptions((o) => ({ ...o, [k]: !o[k] })); + + const handleNormalize = () => { + let t = text || ''; + if (options.stripHtml) { + t = t.replace(/<[^>]*>/g, ' '); + } + if (options.normalizeLetters) { + t = t.replace(/ي/g, 'ی').replace(/ك/g, 'ک').replace(/ة/g, 'ه'); + } + if (options.normalizeZwnj) { + t = t.replace(/\s+(می|نمی)\s+/g, ' $1‌'); + t = t.replace(/\s+(تر|ترین|ها|های|ام|ات|اش|مان|تان|شان)\b/g, '‌$1'); + t = t.replace(/\b(می|نمی)\b\s+/g, '$1‌'); + } + if (options.cleanWhitespace) { + t = t.replace(/\s+/g, ' '); + t = t.replace(/\n\s*\n+/g, '\n\n'); + } + setNormalized(t.trim()); + toast('متن با موفقیت نرمال‌سازی شد', 'success'); + }; + + const copyToClipboard = (val) => { + if (!val) return; + navigator.clipboard.writeText(val); + toast('کپی شد!', 'success'); + }; + + const textareaStyle = { + width: '100%', + minHeight: 180, + background: '#FAF7F0', + border: '1px solid #D8D0C4', + borderRadius: 8, + padding: '12px 16px', + fontSize: 14, + fontFamily: 'Vazirmatn, sans-serif', + color: '#263238', + outline: 'none', + lineHeight: 1.7, + resize: 'vertical', + boxSizing: 'border-box', + }; + + return ( + +
+
+ +