Skip to content

Commit 77b8ae2

Browse files
iYassrclaude
andcommitted
Version 1.3.15 - Enhanced PII detection and UX improvements
- Add comprehensive PII detection: DOB, MAC addresses, API keys, license plates, medical records, driver's licenses, GPS coordinates, VIN numbers, company codes - Add Arabic name detection with 150+ common names - Fix Mada card detection (bypass Luhn for official BIN prefixes) - Fix Gulf IBAN detection (SA, AE, BH, KW, OM, QA) - Add loading screen with progress bar on first launch - Add "Back to Document" button on export success screen - Set app name to "maskr" in Alt+Tab 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 89c399e commit 77b8ae2

File tree

8 files changed

+2600
-25
lines changed

8 files changed

+2600
-25
lines changed

electron/main.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,9 @@ function createWindow() {
228228
}
229229

230230
app.whenReady().then(() => {
231+
// Set app name for development mode (electron-builder sets it in production)
232+
app.name = 'maskr'
233+
231234
// Initialize logging
232235
logStartup()
233236

electron/services/detector.ts

Lines changed: 671 additions & 17 deletions
Large diffs are not rendered by default.

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "maskr",
3-
"version": "1.3.14",
3+
"version": "1.3.15",
44
"description": "A privacy-focused desktop app for detecting and masking sensitive information in documents before sharing with AI or other parties",
55
"main": "dist-electron/main.js",
66
"type": "module",

src/App.tsx

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { useState, useCallback } from 'react'
1+
import { useState, useCallback, useEffect } from 'react'
22
import { useDocumentStore } from './stores/documentStore'
33
import { UploadStep } from './components/UploadStep'
44
import { ReviewStep } from './components/ReviewStep'
55
import { ExportStep } from './components/ExportStep'
6-
import { Check } from './components/ui/icons'
6+
import { Check, Shield } from './components/ui/icons'
77
import { ThemeToggle } from './components/ui/theme-toggle'
88

99
type Step = 'upload' | 'review' | 'export'
@@ -14,10 +14,87 @@ const STEPS: { id: Step; label: string; number: number }[] = [
1414
{ id: 'export', label: 'Export', number: 3 }
1515
]
1616

17+
// Loading screen component
18+
function LoadingScreen({ progress }: { progress: number }) {
19+
return (
20+
<div className="flex flex-col items-center justify-center h-screen bg-background text-foreground">
21+
<div className="text-center">
22+
{/* Logo/Icon */}
23+
<div className="w-20 h-20 mx-auto mb-6 rounded-2xl bg-primary/10 flex items-center justify-center">
24+
<Shield className="w-10 h-10 text-primary" />
25+
</div>
26+
27+
{/* App name */}
28+
<h1 className="text-3xl font-bold text-foreground mb-2">maskr</h1>
29+
<p className="text-muted-foreground mb-8">Secure Document Sanitization</p>
30+
31+
{/* Loading bar */}
32+
<div className="w-64 h-1.5 bg-muted rounded-full overflow-hidden">
33+
<div
34+
className="h-full bg-primary rounded-full transition-all duration-300 ease-out"
35+
style={{ width: `${progress}%` }}
36+
/>
37+
</div>
38+
39+
{/* Loading text */}
40+
<p className="text-sm text-muted-foreground mt-4">
41+
{progress < 30 ? 'Initializing...' :
42+
progress < 60 ? 'Loading detection engine...' :
43+
progress < 90 ? 'Preparing workspace...' :
44+
'Almost ready...'}
45+
</p>
46+
</div>
47+
</div>
48+
)
49+
}
50+
1751
export default function App() {
52+
const [isLoading, setIsLoading] = useState(true)
53+
const [loadingProgress, setLoadingProgress] = useState(0)
1854
const [currentStep, setCurrentStep] = useState<Step>('upload')
1955
const { reset } = useDocumentStore()
2056

57+
// Simulate loading and preload resources
58+
useEffect(() => {
59+
let mounted = true
60+
61+
const preload = async () => {
62+
// Stage 1: Initial setup
63+
setLoadingProgress(10)
64+
await new Promise(r => setTimeout(r, 100))
65+
66+
// Stage 2: Preload the NLP/detection engine by making a dummy call
67+
if (mounted) setLoadingProgress(30)
68+
try {
69+
// This will trigger lazy loading of the compromise NLP module
70+
await window.api?.extractEntities('preload warmup text')
71+
} catch {
72+
// Ignore errors during preload
73+
}
74+
75+
if (mounted) setLoadingProgress(70)
76+
await new Promise(r => setTimeout(r, 100))
77+
78+
// Stage 3: Final setup
79+
if (mounted) setLoadingProgress(90)
80+
await new Promise(r => setTimeout(r, 100))
81+
82+
if (mounted) {
83+
setLoadingProgress(100)
84+
// Small delay before hiding loading screen
85+
setTimeout(() => {
86+
if (mounted) setIsLoading(false)
87+
}, 200)
88+
}
89+
}
90+
91+
preload()
92+
93+
return () => {
94+
mounted = false
95+
}
96+
}, [])
97+
2198
const handleFileUploaded = useCallback(() => {
2299
setCurrentStep('review')
23100
}, [])
@@ -42,6 +119,11 @@ export default function App() {
42119

43120
const getCurrentStepIndex = () => STEPS.findIndex(s => s.id === currentStep)
44121

122+
// Show loading screen during initial load
123+
if (isLoading) {
124+
return <LoadingScreen progress={loadingProgress} />
125+
}
126+
45127
return (
46128
<div className="flex flex-col h-screen bg-background text-foreground">
47129
{/* Header with step indicator */}

src/components/ExportStep.tsx

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,29 @@ export function ExportStep({ onBack, onReset }: ExportStepProps) {
262262
}
263263
}
264264

265+
const handleExportAsMarkdown = async () => {
266+
setIsExporting(true)
267+
setError(null)
268+
269+
try {
270+
const baseName = file?.fileName?.replace(/\.[^/.]+$/, '') || 'document'
271+
const defaultName = `${baseName}_sanitized.md`
272+
273+
// Export as plain text markdown
274+
const contentBase64 = btoa(unescape(encodeURIComponent(maskedContent)))
275+
const result = await window.api?.saveFile(contentBase64, defaultName, 'md')
276+
277+
if (result) {
278+
setExportSuccess(true)
279+
}
280+
} catch (err) {
281+
console.error('Export as MD error:', err)
282+
setError(err instanceof Error ? err.message : 'Failed to export as Markdown')
283+
} finally {
284+
setIsExporting(false)
285+
}
286+
}
287+
265288
if (exportSuccess) {
266289
return (
267290
<div className="flex flex-col items-center justify-center h-full">
@@ -276,9 +299,15 @@ export function ExportStep({ onBack, onReset }: ExportStepProps) {
276299
Your sanitized document has been saved successfully.
277300
</p>
278301

279-
<Button onClick={onReset}>
280-
Sanitize Another Document
281-
</Button>
302+
<div className="flex items-center gap-3">
303+
<Button variant="outline" onClick={() => { setExportSuccess(false); onBack(); }}>
304+
<ChevronLeft className="h-4 w-4 mr-1" />
305+
Back to Document
306+
</Button>
307+
<Button onClick={onReset}>
308+
Sanitize Another Document
309+
</Button>
310+
</div>
282311
</div>
283312
</div>
284313
)
@@ -423,6 +452,10 @@ export function ExportStep({ onBack, onReset }: ExportStepProps) {
423452
</>
424453
)}
425454
</Button>
455+
<Button variant="outline" onClick={handleExportAsMarkdown} disabled={isExporting || isGenerating}>
456+
<Download className="h-4 w-4 mr-1" />
457+
Export as MD
458+
</Button>
426459
<Button
427460
onClick={handleExport}
428461
disabled={isExporting || isGenerating}

0 commit comments

Comments
 (0)