Skip to content

Commit 23ef1ac

Browse files
Merge pull request #13 from BitcoinErrorLog/fix/phase4-technical-debt
fix: Phase 4 Technical Debt - ESLint, Recovery Files, Bundle Optimization
2 parents 320249b + 8140ee8 commit 23ef1ac

File tree

7 files changed

+373
-2
lines changed

7 files changed

+373
-2
lines changed

.eslintrc.json

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"root": true,
3+
"env": {
4+
"browser": true,
5+
"es2021": true,
6+
"webextensions": true
7+
},
8+
"extends": [
9+
"eslint:recommended",
10+
"plugin:@typescript-eslint/recommended",
11+
"plugin:react/recommended",
12+
"plugin:react-hooks/recommended"
13+
],
14+
"parser": "@typescript-eslint/parser",
15+
"parserOptions": {
16+
"ecmaVersion": "latest",
17+
"sourceType": "module",
18+
"ecmaFeatures": {
19+
"jsx": true
20+
}
21+
},
22+
"plugins": [
23+
"@typescript-eslint",
24+
"react",
25+
"react-hooks"
26+
],
27+
"rules": {
28+
"@typescript-eslint/no-explicit-any": "warn",
29+
"@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }],
30+
"react/react-in-jsx-scope": "off",
31+
"react/prop-types": "off",
32+
"no-console": ["warn", { "allow": ["warn", "error"] }]
33+
},
34+
"settings": {
35+
"react": {
36+
"version": "detect"
37+
}
38+
},
39+
"ignorePatterns": [
40+
"dist/",
41+
"node_modules/",
42+
"*.config.js",
43+
"*.config.ts"
44+
]
45+
}
46+

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,15 @@ npm run build
106106
| `Alt+D` | Toggle Drawing | Enable/disable drawing mode |
107107
| `Alt+S` | Toggle Sidebar | Open/close the side panel |
108108
| `Alt+A` | Open Annotations | Jump to annotations tab |
109+
| `Alt+Shift+A` | Toggle Annotations | Enable/disable annotation button |
110+
| `Shift+?` | Show Shortcuts | Display keyboard shortcuts help |
111+
| `Ctrl+Z` / `Cmd+Z` | Undo | Undo last action (in drawing mode) |
112+
| `Ctrl+Y` / `Cmd+Shift+Z` | Redo | Redo last action (in drawing mode) |
113+
| `Escape` | Close/Exit | Close modal or exit drawing mode |
109114

110-
*On Mac, use `Option` instead of `Alt`*
115+
*On Mac, use `Option` instead of `Alt` and `Cmd` instead of `Ctrl`*
116+
117+
**Tip:** Press `Shift+?` in the popup or sidepanel to see all available shortcuts!
111118

112119
## 📖 Usage Guide
113120

docs/DEPENDENCY_UPGRADE_PLAN.md

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# Dependency Upgrade Plan
2+
3+
This document outlines the planned dependency upgrades for post-launch maintenance.
4+
5+
## Current Status
6+
7+
### Production Dependencies
8+
- `@synonymdev/pubky`: `^0.5.4` (pinned)
9+
- `dompurify`: `^3.3.1`
10+
- `react`: `^18.3.1`
11+
- `react-dom`: `^18.3.1`
12+
13+
### Dev Dependencies
14+
- `vite`: `^5.3.3`
15+
- `vitest`: `^1.3.1`
16+
- `typescript`: `^5.5.3`
17+
- `@playwright/test`: `^1.40.0`
18+
19+
## Upgrade Priorities
20+
21+
### High Priority (Post-Launch)
22+
23+
1. **@synonymdev/pubky** (when stable)
24+
- Current: `^0.5.4`
25+
- Target: Latest stable (check changelog for breaking changes)
26+
- Risk: Medium - Core SDK, test thoroughly
27+
- Timeline: After 1-2 months of stable production use
28+
29+
2. **React** (when 19.x stable)
30+
- Current: `^18.3.1`
31+
- Target: `^19.0.0` (when released)
32+
- Risk: Medium - May require code changes
33+
- Timeline: Wait for ecosystem adoption
34+
35+
### Medium Priority (Quarterly)
36+
37+
3. **Vite** (when 6.x stable)
38+
- Current: `^5.3.3`
39+
- Target: `^6.0.0` (when released)
40+
- Risk: Medium - Build tool, test build process
41+
- Timeline: After Vite 6 stable release
42+
43+
4. **TypeScript** (quarterly)
44+
- Current: `^5.5.3`
45+
- Target: Latest 5.x (avoid 6.x until stable)
46+
- Risk: Low - Usually backward compatible
47+
- Timeline: Every 3 months
48+
49+
5. **Playwright** (quarterly)
50+
- Current: `^1.40.0`
51+
- Target: Latest 1.x
52+
- Risk: Low - Test framework, update tests if needed
53+
- Timeline: Every 3 months
54+
55+
### Low Priority (As Needed)
56+
57+
6. **DOMPurify** (security updates)
58+
- Current: `^3.3.1`
59+
- Target: Latest 3.x
60+
- Risk: Low - Security library, patch updates only
61+
- Timeline: When security patches released
62+
63+
7. **Vitest** (quarterly)
64+
- Current: `^1.3.1`
65+
- Target: Latest 1.x
66+
- Risk: Low - Test framework
67+
- Timeline: Every 3 months
68+
69+
## Upgrade Process
70+
71+
1. **Create feature branch**: `chore/upgrade-{package}-{version}`
72+
2. **Update package.json**: Change version range
73+
3. **Run `npm install`**: Install new version
74+
4. **Run `npm run typecheck`**: Check for type errors
75+
5. **Run `npm run lint`**: Check for linting issues
76+
6. **Run `npm test`**: Run unit tests
77+
7. **Run `npm run test:e2e`**: Run E2E tests
78+
8. **Manual testing**: Test critical user flows
79+
9. **Update CHANGELOG.md**: Document upgrade
80+
10. **Create PR**: Get review before merging
81+
82+
## Breaking Changes Tracking
83+
84+
### @synonymdev/pubky
85+
- Monitor: https://github.com/synonymdev/pubky/releases
86+
- Watch for: API changes, Client constructor changes, auth flow changes
87+
88+
### React 19
89+
- Monitor: https://react.dev/blog
90+
- Watch for: Hook changes, component API changes, concurrent features
91+
92+
### Vite 6
93+
- Monitor: https://vitejs.dev/blog
94+
- Watch for: Config changes, plugin API changes, build output changes
95+
96+
## Security Updates
97+
98+
For security-related updates:
99+
1. Create hotfix branch immediately
100+
2. Test critical paths only
101+
3. Deploy to production ASAP
102+
4. Full testing in follow-up PR
103+
104+
## Notes
105+
106+
- Always test in development environment first
107+
- Keep backup of working package-lock.json
108+
- Document any breaking changes in code comments
109+
- Update type definitions if needed
110+
- Check peer dependency requirements
111+

package.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@
1818
"test:ui": "vitest --ui",
1919
"test:e2e": "playwright test",
2020
"test:e2e:ui": "playwright test --ui",
21-
"test:e2e:errors": "node run-e2e-with-error-capture.js"
21+
"test:e2e:errors": "node run-e2e-with-error-capture.js",
22+
"typecheck": "tsc --noEmit",
23+
"lint": "eslint src --ext .ts,.tsx",
24+
"lint:fix": "eslint src --ext .ts,.tsx --fix"
2225
},
2326
"dependencies": {
2427
"@synonymdev/pubky": "^0.5.4",
@@ -41,10 +44,15 @@
4144
"@types/qrcode": "^1.5.5",
4245
"@types/react": "^18.3.3",
4346
"@types/react-dom": "^18.3.0",
47+
"@typescript-eslint/eslint-plugin": "^8.49.0",
48+
"@typescript-eslint/parser": "^8.49.0",
4449
"@vitejs/plugin-react": "^4.3.1",
4550
"@vitest/coverage-v8": "^1.3.1",
4651
"@vitest/ui": "^1.3.1",
4752
"autoprefixer": "^10.4.19",
53+
"eslint": "^9.39.1",
54+
"eslint-plugin-react": "^7.37.5",
55+
"eslint-plugin-react-hooks": "^7.0.1",
4856
"jsdom": "^24.0.0",
4957
"postcss": "^8.4.39",
5058
"tailwindcss": "^3.4.4",

src/popup/components/ProfileEditor.tsx

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { storage, ProfileData } from '../../utils/storage';
33
import { profileManager } from '../../utils/profile-manager';
44
import { logger } from '../../utils/logger';
55
import { validateProfile, VALIDATION_LIMITS } from '../../utils/validation';
6+
import { exportRecoveryFile, validatePassphrase } from '../../utils/recovery-file';
67
import ProgressBar from './ProgressBar';
78
import ImageCropper from './ImageCropper';
89

@@ -538,8 +539,121 @@ export function ProfileEditor() {
538539
<p className="text-xs text-gray-400">
539540
* Your profile will be saved as profile.json and a generated index.html on your homeserver
540541
</p>
542+
543+
{/* Recovery File Export */}
544+
<div className="border-t border-gray-700 pt-4 mt-4">
545+
<h3 className="text-sm font-medium text-gray-300 mb-2">Key Backup</h3>
546+
<p className="text-xs text-gray-400 mb-3">
547+
Export a recovery file to backup your keys. Store it securely - you'll need it if you lose access to your device.
548+
</p>
549+
<button
550+
onClick={() => setShowRecoveryModal(true)}
551+
className="w-full px-4 py-2 bg-yellow-600/20 hover:bg-yellow-600/30 text-yellow-400 border border-yellow-600/50 rounded-lg transition focus:outline-none focus:ring-2 focus:ring-yellow-500"
552+
aria-label="Export recovery file"
553+
>
554+
🔐 Export Recovery File
555+
</button>
556+
</div>
541557
</div>
542558

559+
{/* Recovery File Modal */}
560+
{showRecoveryModal && (
561+
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
562+
<div className="bg-[#1F1F1F] border border-[#3F3F3F] rounded-lg p-6 max-w-md w-full">
563+
<h3 className="text-lg font-bold text-white mb-4">Export Recovery File</h3>
564+
<p className="text-sm text-gray-400 mb-4">
565+
Enter a strong passphrase to encrypt your recovery file. You'll need this passphrase to restore your keys.
566+
</p>
567+
568+
<div className="space-y-3">
569+
<div>
570+
<label className="block text-sm font-medium text-gray-300 mb-1">
571+
Passphrase
572+
</label>
573+
<input
574+
type="password"
575+
value={recoveryPassphrase}
576+
onChange={(e) => {
577+
setRecoveryPassphrase(e.target.value);
578+
setRecoveryError(null);
579+
}}
580+
className="w-full px-3 py-2 bg-gray-700 border border-gray-600 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-yellow-500"
581+
placeholder="Enter passphrase (min 8 characters)"
582+
/>
583+
</div>
584+
585+
<div>
586+
<label className="block text-sm font-medium text-gray-300 mb-1">
587+
Confirm Passphrase
588+
</label>
589+
<input
590+
type="password"
591+
value={recoveryPassphraseConfirm}
592+
onChange={(e) => {
593+
setRecoveryPassphraseConfirm(e.target.value);
594+
setRecoveryError(null);
595+
}}
596+
className="w-full px-3 py-2 bg-gray-700 border border-gray-600 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-yellow-500"
597+
placeholder="Confirm passphrase"
598+
/>
599+
</div>
600+
601+
{recoveryError && (
602+
<div className="text-sm text-red-400">{recoveryError}</div>
603+
)}
604+
605+
<div className="flex gap-2 mt-4">
606+
<button
607+
onClick={async () => {
608+
setRecoveryError(null);
609+
610+
// Validate passphrase
611+
const validation = validatePassphrase(recoveryPassphrase);
612+
if (!validation.isValid) {
613+
setRecoveryError(validation.error || 'Invalid passphrase');
614+
return;
615+
}
616+
617+
if (recoveryPassphrase !== recoveryPassphraseConfirm) {
618+
setRecoveryError('Passphrases do not match');
619+
return;
620+
}
621+
622+
try {
623+
setIsExportingRecovery(true);
624+
await exportRecoveryFile(recoveryPassphrase);
625+
showMessage('success', 'Recovery file exported successfully');
626+
setShowRecoveryModal(false);
627+
setRecoveryPassphrase('');
628+
setRecoveryPassphraseConfirm('');
629+
} catch (error) {
630+
setRecoveryError((error as Error).message || 'Failed to export recovery file');
631+
} finally {
632+
setIsExportingRecovery(false);
633+
}
634+
}}
635+
disabled={isExportingRecovery || !recoveryPassphrase || !recoveryPassphraseConfirm}
636+
className="flex-1 px-4 py-2 bg-yellow-600 hover:bg-yellow-700 disabled:bg-gray-600 text-white rounded-lg transition focus:outline-none focus:ring-2 focus:ring-yellow-500"
637+
>
638+
{isExportingRecovery ? 'Exporting...' : 'Export'}
639+
</button>
640+
<button
641+
onClick={() => {
642+
setShowRecoveryModal(false);
643+
setRecoveryPassphrase('');
644+
setRecoveryPassphraseConfirm('');
645+
setRecoveryError(null);
646+
}}
647+
className="px-4 py-2 bg-gray-700 hover:bg-gray-600 text-white rounded-lg transition focus:outline-none focus:ring-2 focus:ring-gray-500"
648+
>
649+
Cancel
650+
</button>
651+
</div>
652+
</div>
653+
</div>
654+
</div>
655+
)}
656+
543657
{/* Image Cropper Modal */}
544658
{showCropper && imageFileToCrop && (
545659
<ImageCropper

0 commit comments

Comments
 (0)