2025-02-23 10:10:56 +02:00
|
|
|
|
import 'dart:async';
|
2025-02-21 22:51:56 +02:00
|
|
|
|
import 'dart:io';
|
|
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
|
import 'package:file_picker/file_picker.dart';
|
2025-02-22 19:11:10 +02:00
|
|
|
|
import 'package:flutter_svg/flutter_svg.dart';
|
2025-02-21 22:51:56 +02:00
|
|
|
|
import 'package:google_fonts/google_fonts.dart';
|
2025-02-22 14:29:01 +02:00
|
|
|
|
import 'package:open_filex/open_filex.dart';
|
2025-02-21 22:51:56 +02:00
|
|
|
|
import 'package:path/path.dart' as path;
|
2025-02-22 14:29:01 +02:00
|
|
|
|
import 'package:permission_handler/permission_handler.dart';
|
2025-02-21 22:51:56 +02:00
|
|
|
|
import 'package:path_provider/path_provider.dart';
|
|
|
|
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
|
|
import 'note_editor.dart';
|
2025-02-22 20:59:42 +02:00
|
|
|
|
import 'read_only.dart';
|
|
|
|
|
|
import 'texts.dart';
|
2025-02-21 22:51:56 +02:00
|
|
|
|
|
|
|
|
|
|
class NoteListScreen extends StatefulWidget {
|
|
|
|
|
|
final void Function(bool) toggleTheme;
|
|
|
|
|
|
final VoidCallback toggleTags;
|
|
|
|
|
|
final bool isDarkMode;
|
|
|
|
|
|
final bool showTags;
|
|
|
|
|
|
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
const NoteListScreen({
|
|
|
|
|
|
super.key,
|
2025-02-21 22:51:56 +02:00
|
|
|
|
required this.toggleTheme,
|
|
|
|
|
|
required this.toggleTags,
|
|
|
|
|
|
required this.isDarkMode,
|
|
|
|
|
|
required this.showTags,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
State<NoteListScreen> createState() => _NoteListScreenState();
|
2025-02-21 22:51:56 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class _NoteListScreenState extends State<NoteListScreen> {
|
|
|
|
|
|
TextEditingController searchController = TextEditingController();
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
final FocusNode searchFocusNode = FocusNode();
|
2025-02-21 22:51:56 +02:00
|
|
|
|
List<File> notes = [];
|
|
|
|
|
|
List<File> filteredNotes = [];
|
2025-02-23 10:10:56 +02:00
|
|
|
|
List<File> _visibleNotes = [];
|
|
|
|
|
|
int _visibleLimit = 30;
|
|
|
|
|
|
bool _isLoadingMore = false;
|
2025-02-22 16:57:29 +02:00
|
|
|
|
Map<File, Set<String>> noteTags = {};
|
2025-02-21 22:51:56 +02:00
|
|
|
|
String? notesDirectoryPath;
|
|
|
|
|
|
String? defaultDir;
|
2025-02-22 14:29:01 +02:00
|
|
|
|
bool _useExternalEditor = false;
|
2025-02-22 16:57:29 +02:00
|
|
|
|
bool _includeFileContent = false;
|
|
|
|
|
|
bool _includeSubdirectories = true;
|
2025-02-23 10:10:56 +02:00
|
|
|
|
Timer? _debounce;
|
2025-02-21 22:51:56 +02:00
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
|
void initState() {
|
|
|
|
|
|
super.initState();
|
|
|
|
|
|
_initializeApp(); // Call an async method to handle initialization
|
|
|
|
|
|
searchController.addListener(() {
|
2025-02-23 10:10:56 +02:00
|
|
|
|
_debouncedFilterNotes();
|
|
|
|
|
|
});
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
|
|
|
|
FocusScope.of(context).requestFocus(searchFocusNode);
|
|
|
|
|
|
});
|
2025-02-23 10:10:56 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void _debouncedFilterNotes() {
|
|
|
|
|
|
if (_debounce?.isActive ?? false) _debounce?.cancel();
|
|
|
|
|
|
_debounce = Timer(Duration(milliseconds: 300), () {
|
|
|
|
|
|
_filterNotes();
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
|
void dispose() {
|
|
|
|
|
|
_debounce?.cancel();
|
|
|
|
|
|
searchController.dispose();
|
|
|
|
|
|
super.dispose();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void _toggleIncludeFileContent(bool value) {
|
|
|
|
|
|
setState(() {
|
|
|
|
|
|
_includeFileContent = value;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// ✅ Save preference
|
|
|
|
|
|
SharedPreferences.getInstance().then((prefs) {
|
|
|
|
|
|
prefs.setBool('include_file_content', value);
|
2025-02-21 22:51:56 +02:00
|
|
|
|
});
|
2025-02-23 10:10:56 +02:00
|
|
|
|
|
|
|
|
|
|
// ✅ Immediately reapply search filter to update list
|
|
|
|
|
|
_filterNotes();
|
2025-02-21 22:51:56 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-23 10:10:56 +02:00
|
|
|
|
|
2025-02-22 14:38:38 +02:00
|
|
|
|
Future<void> _toggleExternalEditor(bool value) async {
|
|
|
|
|
|
setState(() {
|
|
|
|
|
|
_useExternalEditor = value;
|
|
|
|
|
|
});
|
|
|
|
|
|
SharedPreferences.getInstance().then((prefs) {
|
|
|
|
|
|
prefs.setBool('use_external_editor', value);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-22 14:29:01 +02:00
|
|
|
|
Future<void> _requestStoragePermission() async {
|
|
|
|
|
|
if (Platform.isAndroid) {
|
|
|
|
|
|
if (await Permission.storage.request().isGranted) {
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
debugPrint("✅ Basic storage permission granted.");
|
2025-02-22 14:29:01 +02:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (await Permission.manageExternalStorage.request().isGranted) {
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
debugPrint("✅ Full file access granted.");
|
2025-02-22 14:29:01 +02:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
debugPrint("❌ Storage permission denied. Opening settings...");
|
2025-02-22 14:29:01 +02:00
|
|
|
|
await openAppSettings(); // Open settings if denied
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-21 22:51:56 +02:00
|
|
|
|
Future<void> _initializeApp() async {
|
2025-02-22 14:29:01 +02:00
|
|
|
|
final prefs = await SharedPreferences.getInstance();
|
2025-02-21 22:51:56 +02:00
|
|
|
|
final dir = await getApplicationDocumentsDirectory();
|
2025-02-22 14:29:01 +02:00
|
|
|
|
|
2025-02-21 22:51:56 +02:00
|
|
|
|
setState(() {
|
2025-02-22 14:29:01 +02:00
|
|
|
|
defaultDir = dir.path;
|
|
|
|
|
|
_useExternalEditor = prefs.getBool('use_external_editor') ?? false;
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
_includeSubdirectories = prefs.getBool('include_subdirectories') ?? true;
|
2025-02-21 22:51:56 +02:00
|
|
|
|
});
|
2025-02-22 14:29:01 +02:00
|
|
|
|
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
notesDirectoryPath = prefs.getString('notes_directory') ?? defaultDir;
|
|
|
|
|
|
|
|
|
|
|
|
// 🚀 Check if it's the first start OR if the directory is empty
|
|
|
|
|
|
final directory = Directory(notesDirectoryPath!);
|
|
|
|
|
|
if (prefs.getBool('initialized') == null ||
|
|
|
|
|
|
!directory.listSync().any((entity) => entity is File && entity.path.endsWith('.md'))) {
|
|
|
|
|
|
await _createDefaultNotes();
|
|
|
|
|
|
await prefs.setBool('initialized', true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-22 14:29:01 +02:00
|
|
|
|
await _loadNotes();
|
2025-02-21 22:51:56 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
Future<void> _createDefaultNotes() async {
|
|
|
|
|
|
final quickStartNote = File(path.join(notesDirectoryPath!, "QuickStart.md"));
|
|
|
|
|
|
|
|
|
|
|
|
if (!quickStartNote.existsSync()) {
|
|
|
|
|
|
quickStartNote.writeAsStringSync("""
|
|
|
|
|
|
# Quick Start Guide
|
|
|
|
|
|
|
|
|
|
|
|
Welcome to *Muzzle Velocity*.
|
|
|
|
|
|
This is your fast and minimal note-taking app.
|
|
|
|
|
|
|
|
|
|
|
|
1. ✍️ Create Notes quickly: just type a title in the search bar and press enter.
|
|
|
|
|
|
2. 🔍 Type keywords to filter notes instantly.
|
|
|
|
|
|
3. 💻 Type commands like :help to get more information.
|
|
|
|
|
|
4. 🏷️ Use hashtags like #quickstart, context tags like @help, or project tags like +muzzle in your notes for categorization.
|
|
|
|
|
|
5. 🔫 Tap the blaster at the top right for more settings or to clear the search field.
|
|
|
|
|
|
|
|
|
|
|
|
*Muzzle Velocity* is built for speed and simplicity! 🚀
|
|
|
|
|
|
|
|
|
|
|
|
Happy note taking! ✨
|
|
|
|
|
|
""");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-02-23 10:10:56 +02:00
|
|
|
|
void _toggleIncludeSubdirectories(bool value) {
|
2025-02-22 16:57:29 +02:00
|
|
|
|
setState(() {
|
|
|
|
|
|
_includeSubdirectories = value;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2025-02-23 10:10:56 +02:00
|
|
|
|
// ✅ Save preference
|
|
|
|
|
|
SharedPreferences.getInstance().then((prefs) {
|
|
|
|
|
|
prefs.setBool('include_subdirectories', value);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// ✅ Reload notes and reapply search immediately
|
|
|
|
|
|
_loadNotes().then((_) {
|
|
|
|
|
|
_filterNotes();
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2025-02-22 16:57:29 +02:00
|
|
|
|
|
2025-02-21 22:51:56 +02:00
|
|
|
|
void _showDirectoryChoiceDialog(String defaultPath) {
|
|
|
|
|
|
showDialog(
|
|
|
|
|
|
context: context,
|
|
|
|
|
|
barrierDismissible: false, // Force user to make a choice
|
|
|
|
|
|
builder: (BuildContext context) {
|
|
|
|
|
|
return AlertDialog(
|
|
|
|
|
|
title: Text("Choose Notes Directory"),
|
|
|
|
|
|
content: Text(
|
|
|
|
|
|
"Your notes will be saved in:\n\n📂 $defaultPath\n\nWould you like to use a different folder?"),
|
|
|
|
|
|
actions: [
|
|
|
|
|
|
TextButton(
|
|
|
|
|
|
onPressed: () async {
|
|
|
|
|
|
// Reset to default directory
|
|
|
|
|
|
final prefs = await SharedPreferences.getInstance();
|
|
|
|
|
|
await prefs.remove(
|
|
|
|
|
|
'notes_directory'); // Remove custom directory from preferences
|
|
|
|
|
|
setState(() {
|
|
|
|
|
|
notesDirectoryPath = defaultDir; // Reset to default directory
|
|
|
|
|
|
});
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
if (context.mounted) Navigator.pop(context);
|
2025-02-21 22:51:56 +02:00
|
|
|
|
_loadNotes(); // Reload notes from the default directory
|
|
|
|
|
|
},
|
|
|
|
|
|
child: Text("Use Default"),
|
|
|
|
|
|
),
|
|
|
|
|
|
TextButton(
|
|
|
|
|
|
onPressed: () async {
|
2025-02-22 14:29:01 +02:00
|
|
|
|
await _requestStoragePermission();
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
if (context.mounted) Navigator.pop(context);
|
2025-02-21 22:51:56 +02:00
|
|
|
|
await _selectNotesDirectory(); // Allow user to pick a folder
|
|
|
|
|
|
},
|
|
|
|
|
|
child: Text("Choose Folder"),
|
|
|
|
|
|
),
|
|
|
|
|
|
],
|
|
|
|
|
|
);
|
|
|
|
|
|
},
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Future<void> _selectNotesDirectory() async {
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
String? selectedDirectory = await FilePicker.getDirectoryPath();
|
2025-02-21 22:51:56 +02:00
|
|
|
|
if (selectedDirectory != null) {
|
|
|
|
|
|
final prefs = await SharedPreferences.getInstance();
|
|
|
|
|
|
await prefs.setString(
|
|
|
|
|
|
'notes_directory', selectedDirectory); // Save custom directory
|
|
|
|
|
|
setState(() {
|
|
|
|
|
|
notesDirectoryPath = selectedDirectory; // Update state
|
|
|
|
|
|
});
|
|
|
|
|
|
_loadNotes(); // Reload notes from the new directory
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Future<void> _loadNotes() async {
|
|
|
|
|
|
final prefs = await SharedPreferences.getInstance();
|
2025-02-22 15:45:04 +02:00
|
|
|
|
notesDirectoryPath = prefs.getString('notes_directory') ?? defaultDir;
|
2025-02-21 22:51:56 +02:00
|
|
|
|
final notesDir = Directory(notesDirectoryPath!);
|
2025-02-22 16:57:29 +02:00
|
|
|
|
|
2025-02-21 22:51:56 +02:00
|
|
|
|
if (!notesDir.existsSync()) {
|
|
|
|
|
|
notesDir.createSync(recursive: true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-22 15:45:04 +02:00
|
|
|
|
List<File> allNotes = [];
|
2025-02-22 16:57:29 +02:00
|
|
|
|
Map<String, DateTime> folderTimestamps = {};
|
2025-02-23 10:10:56 +02:00
|
|
|
|
Map<File, Set<String>> extractedTags = {}; // ✅ Store extracted tags here
|
2025-02-22 15:45:04 +02:00
|
|
|
|
|
2025-02-23 10:10:56 +02:00
|
|
|
|
void fetchNotes(Directory dir) {
|
|
|
|
|
|
final entries = dir.listSync(recursive: _includeSubdirectories);
|
2025-02-22 15:45:04 +02:00
|
|
|
|
|
|
|
|
|
|
for (var entry in entries) {
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
if (entry is File && _isValidNoteFile(entry)) {
|
2025-02-23 10:10:56 +02:00
|
|
|
|
|
|
|
|
|
|
if (path.basename(path.dirname(entry.path)) == "trash") {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-22 15:45:04 +02:00
|
|
|
|
allNotes.add(entry);
|
|
|
|
|
|
final folder = path.dirname(entry.path);
|
2025-02-23 10:10:56 +02:00
|
|
|
|
|
2025-02-22 15:45:04 +02:00
|
|
|
|
if (!folderTimestamps.containsKey(folder) ||
|
|
|
|
|
|
entry.lastModifiedSync().isAfter(folderTimestamps[folder]!)) {
|
|
|
|
|
|
folderTimestamps[folder] = entry.lastModifiedSync();
|
|
|
|
|
|
}
|
2025-02-23 10:10:56 +02:00
|
|
|
|
|
|
|
|
|
|
final content = entry.readAsStringSync();
|
|
|
|
|
|
extractedTags[entry] = _extractTags(content);
|
2025-02-22 15:45:04 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-23 10:10:56 +02:00
|
|
|
|
fetchNotes(notesDir);
|
2025-02-22 15:45:04 +02:00
|
|
|
|
|
|
|
|
|
|
allNotes.sort((a, b) {
|
|
|
|
|
|
String folderA = path.dirname(a.path);
|
|
|
|
|
|
String folderB = path.dirname(b.path);
|
|
|
|
|
|
DateTime timeA = folderTimestamps[folderA] ?? DateTime(0);
|
|
|
|
|
|
DateTime timeB = folderTimestamps[folderB] ?? DateTime(0);
|
2025-02-23 10:10:56 +02:00
|
|
|
|
|
|
|
|
|
|
// ✅ Prioritize files in the root directory over subdirectories
|
|
|
|
|
|
bool isRootA = folderA == notesDirectoryPath;
|
|
|
|
|
|
bool isRootB = folderB == notesDirectoryPath;
|
|
|
|
|
|
|
|
|
|
|
|
if (isRootA && !isRootB) return -1; // Root files come first
|
|
|
|
|
|
if (!isRootA && isRootB) return 1; // Subdir files come after root files
|
|
|
|
|
|
|
|
|
|
|
|
return timeB.compareTo(timeA); // Otherwise, sort by last modified time
|
2025-02-22 15:45:04 +02:00
|
|
|
|
});
|
|
|
|
|
|
|
2025-02-21 22:51:56 +02:00
|
|
|
|
setState(() {
|
2025-02-22 15:45:04 +02:00
|
|
|
|
notes = allNotes;
|
|
|
|
|
|
filteredNotes = allNotes;
|
2025-02-23 10:10:56 +02:00
|
|
|
|
noteTags = extractedTags; // ✅ Update stored tags
|
2025-02-21 22:51:56 +02:00
|
|
|
|
});
|
2025-02-23 10:10:56 +02:00
|
|
|
|
|
|
|
|
|
|
_filterNotes(); // ✅ Apply search immediately if a term is active
|
2025-02-21 22:51:56 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-22 16:57:29 +02:00
|
|
|
|
/// Extracts unique tags sorted by +, @, #
|
|
|
|
|
|
Set<String> _extractTags(String content) {
|
2026-06-19 17:32:48 +03:00
|
|
|
|
final tagPattern = RegExp(r'(?<!\S)[#@+][a-zA-Z][a-zA-Z0-9_]*');
|
2025-02-22 16:57:29 +02:00
|
|
|
|
final matches = tagPattern.allMatches(content).map((m) => m.group(0)!).toSet();
|
|
|
|
|
|
|
|
|
|
|
|
// Sort the tags in order: +tags first, @contexts second, #hashtags last
|
|
|
|
|
|
final sortedTags = matches.toList()
|
|
|
|
|
|
..sort((a, b) {
|
|
|
|
|
|
const order = {'+': 0, '@': 1, '#': 2};
|
|
|
|
|
|
return (order[a[0]] ?? 3).compareTo(order[b[0]] ?? 3);
|
2025-02-22 15:58:17 +02:00
|
|
|
|
});
|
2025-02-22 15:45:04 +02:00
|
|
|
|
|
2025-02-22 16:57:29 +02:00
|
|
|
|
return sortedTags.toSet(); // Convert back to a set after sorting
|
|
|
|
|
|
}
|
2025-02-22 15:58:17 +02:00
|
|
|
|
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
bool _isValidNoteFile(File file) {
|
|
|
|
|
|
final validExtensions = ['.md', '.txt', '.markdown'];
|
|
|
|
|
|
return validExtensions.any((ext) => file.path.toLowerCase().endsWith(ext));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-22 16:57:29 +02:00
|
|
|
|
void _filterNotes() {
|
|
|
|
|
|
final query = searchController.text.trim().toLowerCase();
|
2025-02-22 15:58:17 +02:00
|
|
|
|
|
|
|
|
|
|
setState(() {
|
2025-02-22 16:57:29 +02:00
|
|
|
|
if (query.isEmpty) {
|
|
|
|
|
|
filteredNotes = List.from(notes);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
final terms = query.split(' '); // Split search into words
|
|
|
|
|
|
final searchTags = terms.where((t) => t.startsWith(RegExp(r'[#@+]'))).toSet();
|
|
|
|
|
|
final searchText = terms.where((t) => !t.startsWith(RegExp(r'[#@+]'))).join(' ');
|
|
|
|
|
|
|
2026-06-19 17:32:48 +03:00
|
|
|
|
// Strip leading '/' so "/subdir" and "subdir" match the same notes
|
|
|
|
|
|
final normalizedSearch = searchText.replaceFirst(RegExp(r'^/'), '');
|
|
|
|
|
|
|
2025-02-22 15:58:17 +02:00
|
|
|
|
filteredNotes = notes.where((note) {
|
2026-06-19 17:32:48 +03:00
|
|
|
|
final relativePath = path.relative(note.path, from: notesDirectoryPath!).toLowerCase();
|
2025-02-23 10:10:56 +02:00
|
|
|
|
final noteTags = _extractTags(note.readAsStringSync());
|
|
|
|
|
|
|
|
|
|
|
|
// ✅ Read file content ONLY if `Include File Content` is enabled
|
|
|
|
|
|
final content = _includeFileContent ? note.readAsStringSync().toLowerCase() : '';
|
2025-02-22 15:58:17 +02:00
|
|
|
|
|
2026-06-19 17:39:38 +03:00
|
|
|
|
// ✅ Match partial tags instead of requiring full matches (case-insensitive)
|
2025-02-22 16:57:29 +02:00
|
|
|
|
final matchesTags = searchTags.every((tag) =>
|
2026-06-19 17:39:38 +03:00
|
|
|
|
noteTags.any((noteTag) => noteTag.toLowerCase().startsWith(tag)));
|
2025-02-22 15:58:17 +02:00
|
|
|
|
|
2025-02-22 16:57:29 +02:00
|
|
|
|
if (!_includeFileContent) {
|
2026-06-19 17:32:48 +03:00
|
|
|
|
return relativePath.contains(normalizedSearch) && matchesTags;
|
2025-02-22 16:57:29 +02:00
|
|
|
|
} else {
|
2026-06-19 17:32:48 +03:00
|
|
|
|
return (relativePath.contains(normalizedSearch) || content.contains(normalizedSearch)) && matchesTags;
|
2025-02-22 16:57:29 +02:00
|
|
|
|
}
|
2025-02-22 15:58:17 +02:00
|
|
|
|
}).toList();
|
2025-02-21 22:51:56 +02:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-23 10:10:56 +02:00
|
|
|
|
void _loadMoreNotes() {
|
|
|
|
|
|
if (_isLoadingMore || _visibleNotes.length >= filteredNotes.length) return;
|
|
|
|
|
|
|
|
|
|
|
|
setState(() => _isLoadingMore = true);
|
|
|
|
|
|
|
|
|
|
|
|
Future.delayed(Duration(milliseconds: 200), () {
|
|
|
|
|
|
setState(() {
|
|
|
|
|
|
_visibleLimit += 20; // Load 20 more each time
|
|
|
|
|
|
_visibleNotes = filteredNotes.take(_visibleLimit).toList();
|
|
|
|
|
|
_isLoadingMore = false;
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-21 22:51:56 +02:00
|
|
|
|
List<InlineSpan> _formatTags(String content) {
|
2026-06-19 17:32:48 +03:00
|
|
|
|
final tagPattern = RegExp(r'(?<!\S)[#@+][a-zA-Z][a-zA-Z0-9_]*');
|
2025-02-22 15:58:17 +02:00
|
|
|
|
final Set<String> tagSet = {}; // Avoid duplicate tags
|
|
|
|
|
|
|
2025-02-21 22:51:56 +02:00
|
|
|
|
for (final match in tagPattern.allMatches(content)) {
|
2025-02-22 15:58:17 +02:00
|
|
|
|
tagSet.add(match.group(0)!);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Sort tags: +tags before @tags before #tags
|
|
|
|
|
|
final sortedTags = tagSet.toList()
|
|
|
|
|
|
..sort((a, b) {
|
|
|
|
|
|
if (a.startsWith('+') && !b.startsWith('+')) return -1;
|
|
|
|
|
|
if (b.startsWith('+') && !a.startsWith('+')) return 1;
|
|
|
|
|
|
if (a.startsWith('@') && !b.startsWith('@')) return -1;
|
|
|
|
|
|
if (b.startsWith('@') && !a.startsWith('@')) return 1;
|
|
|
|
|
|
return a.compareTo(b); // Default alphabetical order
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return sortedTags.map((tag) {
|
2025-02-21 22:51:56 +02:00
|
|
|
|
Color tagColor = Colors.white;
|
|
|
|
|
|
if (tag.startsWith('#')) tagColor = Colors.pinkAccent;
|
|
|
|
|
|
if (tag.startsWith('+')) tagColor = Colors.cyan;
|
|
|
|
|
|
if (tag.startsWith('@')) tagColor = Colors.orange;
|
|
|
|
|
|
|
2025-02-22 15:58:17 +02:00
|
|
|
|
return TextSpan(
|
|
|
|
|
|
text: "$tag ",
|
|
|
|
|
|
style: TextStyle(color: tagColor, fontWeight: FontWeight.bold),
|
|
|
|
|
|
);
|
|
|
|
|
|
}).toList();
|
2025-02-21 22:51:56 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void _createNewNote(String title) async {
|
|
|
|
|
|
try {
|
2025-02-22 15:45:04 +02:00
|
|
|
|
String notePath = title.trim();
|
|
|
|
|
|
String? subfolder;
|
|
|
|
|
|
String safeTitle;
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
String extension = '.md'; // Default extension
|
|
|
|
|
|
|
|
|
|
|
|
// ✅ Check if user specified an extension manually
|
|
|
|
|
|
if (title.contains('.')) {
|
|
|
|
|
|
final ext = path.extension(title);
|
|
|
|
|
|
if (['.txt', '.markdown', '.md'].contains(ext)) {
|
|
|
|
|
|
extension = ext;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-02-22 15:45:04 +02:00
|
|
|
|
|
2026-06-19 17:32:48 +03:00
|
|
|
|
if (notePath.contains('/')) {
|
2025-02-22 15:45:04 +02:00
|
|
|
|
final parts = notePath.split('/');
|
|
|
|
|
|
parts.removeWhere((element) => element.isEmpty);
|
|
|
|
|
|
if (parts.length > 1) {
|
|
|
|
|
|
subfolder = parts.sublist(0, parts.length - 1).join('/');
|
|
|
|
|
|
}
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
safeTitle = _sanitizeFilename(parts.last.replaceAll(extension, '')); // Remove extension from input
|
2025-02-22 15:45:04 +02:00
|
|
|
|
} else {
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
safeTitle = _sanitizeFilename(title.replaceAll(extension, ''));
|
2025-02-22 15:45:04 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Construct the final file path
|
|
|
|
|
|
String fullPath = subfolder != null
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
? path.join(notesDirectoryPath!, subfolder, '$safeTitle$extension')
|
|
|
|
|
|
: path.join(notesDirectoryPath!, '$safeTitle$extension');
|
2025-02-22 15:45:04 +02:00
|
|
|
|
|
|
|
|
|
|
final newNote = File(fullPath);
|
|
|
|
|
|
|
|
|
|
|
|
// Prevent duplicate note creation
|
|
|
|
|
|
if (notes.any((note) => note.path == newNote.path)) {
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
debugPrint("❌ Note already exists at: $fullPath");
|
2025-02-22 15:45:04 +02:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Ensure subfolder exists
|
|
|
|
|
|
final parentDir = newNote.parent;
|
|
|
|
|
|
if (!parentDir.existsSync()) {
|
|
|
|
|
|
parentDir.createSync(recursive: true);
|
|
|
|
|
|
}
|
2025-02-21 22:51:56 +02:00
|
|
|
|
|
|
|
|
|
|
if (!newNote.existsSync()) {
|
2025-02-22 15:45:04 +02:00
|
|
|
|
newNote.writeAsStringSync('# $safeTitle\n\n');
|
2025-02-21 22:51:56 +02:00
|
|
|
|
setState(() {
|
|
|
|
|
|
notes.add(newNote);
|
|
|
|
|
|
filteredNotes.add(newNote);
|
|
|
|
|
|
});
|
2025-02-22 15:45:04 +02:00
|
|
|
|
|
2025-02-21 22:51:56 +02:00
|
|
|
|
Navigator.push(
|
|
|
|
|
|
context,
|
|
|
|
|
|
MaterialPageRoute(
|
|
|
|
|
|
builder: (context) => NoteEditorScreen(
|
|
|
|
|
|
note: newNote,
|
|
|
|
|
|
isDarkMode: widget.isDarkMode,
|
|
|
|
|
|
notesDirectoryPath: notesDirectoryPath,
|
2026-06-19 17:21:00 +03:00
|
|
|
|
initialMode: NoteMode.edit,
|
2025-02-21 22:51:56 +02:00
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
|
|
|
|
SnackBar(
|
|
|
|
|
|
content: Text('Failed to create note: $e'),
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool _isNewNote(String title) {
|
2025-02-22 15:45:04 +02:00
|
|
|
|
String notePath = title.trim();
|
|
|
|
|
|
String? subfolder;
|
|
|
|
|
|
String safeTitle;
|
|
|
|
|
|
|
|
|
|
|
|
if (notePath.startsWith('/')) {
|
|
|
|
|
|
final parts = notePath.split('/');
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
parts.removeWhere((element) => element.isEmpty);
|
2025-02-22 15:45:04 +02:00
|
|
|
|
if (parts.length > 1) {
|
|
|
|
|
|
subfolder = parts.sublist(0, parts.length - 1).join('/');
|
|
|
|
|
|
}
|
|
|
|
|
|
safeTitle = _sanitizeFilename(parts.last);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
safeTitle = _sanitizeFilename(title);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
// ✅ Allow multiple file extensions
|
|
|
|
|
|
for (var ext in ['.md', '.txt', '.markdown']) {
|
|
|
|
|
|
String fullPath = subfolder != null
|
|
|
|
|
|
? path.join(notesDirectoryPath!, subfolder, '$safeTitle$ext')
|
|
|
|
|
|
: path.join(notesDirectoryPath!, '$safeTitle$ext');
|
|
|
|
|
|
|
|
|
|
|
|
if (notes.any((note) => note.path == fullPath)) {
|
|
|
|
|
|
return false; // A note with this name already exists
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-02-22 15:45:04 +02:00
|
|
|
|
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
return true;
|
2025-02-21 22:51:56 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
String _sanitizeFilename(String title) {
|
|
|
|
|
|
return title
|
|
|
|
|
|
.replaceAll(RegExp(r'[<>:"/\\|?*]'), '') // Remove invalid characters
|
|
|
|
|
|
.trim();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void _deleteNote(File note) {
|
2025-02-22 15:45:04 +02:00
|
|
|
|
final rootTrashDir = Directory(path.join(notesDirectoryPath!, 'trash'));
|
|
|
|
|
|
|
|
|
|
|
|
if (!rootTrashDir.existsSync()) {
|
|
|
|
|
|
rootTrashDir.createSync(recursive: true);
|
2025-02-21 22:51:56 +02:00
|
|
|
|
}
|
2025-02-22 15:45:04 +02:00
|
|
|
|
|
|
|
|
|
|
// Define the base trashed file path
|
|
|
|
|
|
String trashedPath = path.join(rootTrashDir.path, note.uri.pathSegments.last);
|
|
|
|
|
|
File trashedNote = File(trashedPath);
|
|
|
|
|
|
|
|
|
|
|
|
// If the file already exists in trash, append a timestamp to avoid conflicts
|
|
|
|
|
|
if (trashedNote.existsSync()) {
|
|
|
|
|
|
final timestamp = DateTime.now().millisecondsSinceEpoch;
|
|
|
|
|
|
final newFileName = '${path.basenameWithoutExtension(note.path)}_$timestamp.md';
|
|
|
|
|
|
trashedNote = File(path.join(rootTrashDir.path, newFileName));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Move the file to trash
|
2025-02-21 22:51:56 +02:00
|
|
|
|
note.renameSync(trashedNote.path);
|
|
|
|
|
|
|
2025-02-22 15:45:04 +02:00
|
|
|
|
// Check if the original directory is now empty and delete it if needed
|
|
|
|
|
|
_deleteEmptyParentFolders(note.parent);
|
|
|
|
|
|
|
2025-02-21 22:51:56 +02:00
|
|
|
|
setState(() {
|
|
|
|
|
|
notes.remove(note);
|
|
|
|
|
|
filteredNotes.remove(note);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2025-02-23 10:10:56 +02:00
|
|
|
|
// ✅ Ensure we clear previous Snackbars
|
|
|
|
|
|
ScaffoldMessenger.of(context).clearSnackBars();
|
|
|
|
|
|
|
|
|
|
|
|
// ✅ Show Snackbar with automatic dismissal
|
|
|
|
|
|
final snackBar = SnackBar(
|
|
|
|
|
|
duration: const Duration(seconds: 3), // ✅ Auto-dismiss after 3 seconds
|
|
|
|
|
|
content: Text('Note moved to trash'),
|
|
|
|
|
|
action: SnackBarAction(
|
|
|
|
|
|
label: 'Undo',
|
|
|
|
|
|
onPressed: () {
|
|
|
|
|
|
trashedNote.renameSync(note.path);
|
|
|
|
|
|
setState(() {
|
|
|
|
|
|
notes.add(note);
|
|
|
|
|
|
filteredNotes.add(note);
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
2025-02-21 22:51:56 +02:00
|
|
|
|
),
|
|
|
|
|
|
);
|
2025-02-23 10:10:56 +02:00
|
|
|
|
|
|
|
|
|
|
// ✅ Show the Snackbar
|
|
|
|
|
|
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
|
|
|
|
|
|
|
|
|
|
|
// 🔥 Force dismiss after timeout in case it gets stuck
|
|
|
|
|
|
Future.delayed(const Duration(seconds: 2), () {
|
|
|
|
|
|
if (mounted) {
|
|
|
|
|
|
ScaffoldMessenger.of(context).hideCurrentSnackBar();
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2025-02-21 22:51:56 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-22 15:45:04 +02:00
|
|
|
|
// ✅ Recursively deletes empty parent folders up to the notes directory
|
|
|
|
|
|
void _deleteEmptyParentFolders(Directory folder) {
|
|
|
|
|
|
if (folder.path == notesDirectoryPath) return; // Don't delete the root folder
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
if (folder.existsSync() && folder.listSync().isEmpty) {
|
|
|
|
|
|
folder.deleteSync();
|
|
|
|
|
|
_deleteEmptyParentFolders(folder.parent); // Recursively check parent
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
debugPrint("❌ Error deleting empty folder: $e");
|
2025-02-22 15:45:04 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-21 22:51:56 +02:00
|
|
|
|
Future<void> _emptyTrash() async {
|
|
|
|
|
|
final prefs = await SharedPreferences.getInstance();
|
|
|
|
|
|
final notesPath = prefs.getString('notes_directory') ??
|
|
|
|
|
|
(await getApplicationDocumentsDirectory()).path;
|
|
|
|
|
|
final trashDir = Directory('$notesPath/trash');
|
|
|
|
|
|
if (trashDir.existsSync()) {
|
|
|
|
|
|
trashDir.deleteSync(recursive: true);
|
|
|
|
|
|
trashDir.createSync();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void _confirmEmptyTrash() {
|
|
|
|
|
|
showDialog(
|
|
|
|
|
|
context: context,
|
|
|
|
|
|
builder: (context) {
|
|
|
|
|
|
return AlertDialog(
|
|
|
|
|
|
title: Text("Empty Trash"),
|
|
|
|
|
|
content: Text(
|
|
|
|
|
|
"Are you sure you want to permanently delete all trashed notes?"),
|
|
|
|
|
|
actions: [
|
|
|
|
|
|
TextButton(
|
|
|
|
|
|
onPressed: () => Navigator.pop(context),
|
|
|
|
|
|
child: Text("Cancel"),
|
|
|
|
|
|
),
|
|
|
|
|
|
TextButton(
|
|
|
|
|
|
onPressed: () {
|
|
|
|
|
|
_emptyTrash();
|
|
|
|
|
|
Navigator.pop(context);
|
|
|
|
|
|
},
|
|
|
|
|
|
child: Text("Empty Trash"),
|
|
|
|
|
|
),
|
|
|
|
|
|
],
|
|
|
|
|
|
);
|
|
|
|
|
|
},
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void _handleCommand(String text) {
|
|
|
|
|
|
switch (text) {
|
|
|
|
|
|
case ':tags':
|
|
|
|
|
|
widget.toggleTags();
|
|
|
|
|
|
break;
|
|
|
|
|
|
case ':notags':
|
|
|
|
|
|
widget.toggleTags();
|
|
|
|
|
|
break;
|
|
|
|
|
|
case ':day':
|
|
|
|
|
|
widget.toggleTheme(false);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case ':night':
|
|
|
|
|
|
widget.toggleTheme(true);
|
|
|
|
|
|
break;
|
2025-02-22 14:38:38 +02:00
|
|
|
|
case ':extern':
|
|
|
|
|
|
_toggleExternalEditor(true);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case ':intern':
|
|
|
|
|
|
_toggleExternalEditor(false);
|
|
|
|
|
|
break;
|
2025-02-21 22:51:56 +02:00
|
|
|
|
case ':emptytrash':
|
|
|
|
|
|
_confirmEmptyTrash();
|
|
|
|
|
|
break;
|
2025-02-22 20:59:42 +02:00
|
|
|
|
case ':about':
|
|
|
|
|
|
_showInfoPage(context, "About", aboutContent);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case ':help':
|
|
|
|
|
|
_showInfoPage(context, "Help", helpContent);
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
break;
|
2025-02-21 22:51:56 +02:00
|
|
|
|
default:
|
|
|
|
|
|
return; // Do nothing for unrecognized commands
|
|
|
|
|
|
}
|
|
|
|
|
|
searchController.clear();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
void _showInfoPage(BuildContext context, String title, String content) {
|
2025-02-22 20:59:42 +02:00
|
|
|
|
Navigator.push(
|
|
|
|
|
|
context,
|
|
|
|
|
|
MaterialPageRoute(
|
|
|
|
|
|
builder: (context) => ReadOnlyPage(
|
|
|
|
|
|
title: title,
|
|
|
|
|
|
content: content,
|
|
|
|
|
|
isDarkMode: widget.isDarkMode,
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-21 22:51:56 +02:00
|
|
|
|
@override
|
|
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
|
|
return Scaffold(
|
|
|
|
|
|
appBar: AppBar(
|
|
|
|
|
|
title: Padding(
|
2025-02-22 20:59:42 +02:00
|
|
|
|
padding: EdgeInsets.symmetric(horizontal: 10),
|
2025-02-21 22:51:56 +02:00
|
|
|
|
child: TextField(
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
focusNode: searchFocusNode,
|
2025-02-21 22:51:56 +02:00
|
|
|
|
enableInteractiveSelection: true,
|
|
|
|
|
|
autocorrect: false,
|
|
|
|
|
|
textInputAction: TextInputAction.done,
|
|
|
|
|
|
controller: searchController,
|
2025-02-22 19:11:10 +02:00
|
|
|
|
cursorColor: Colors.orange,
|
2025-02-21 22:51:56 +02:00
|
|
|
|
decoration: InputDecoration(
|
|
|
|
|
|
hintText: 'Search or enter a command...',
|
|
|
|
|
|
border: InputBorder.none,
|
|
|
|
|
|
),
|
|
|
|
|
|
style: GoogleFonts.ibmPlexMono(color: Colors.grey[300]),
|
|
|
|
|
|
onSubmitted: (text) {
|
|
|
|
|
|
final trimmedText = text.trim();
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
if (trimmedText.startsWith(':')) {
|
|
|
|
|
|
_handleCommand(trimmedText);
|
|
|
|
|
|
} else if (trimmedText.isNotEmpty && _isNewNote(trimmedText)) {
|
2025-02-21 22:51:56 +02:00
|
|
|
|
_createNewNote(trimmedText);
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
searchController.clear();
|
2025-02-21 22:51:56 +02:00
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
actions: [
|
|
|
|
|
|
Padding(
|
|
|
|
|
|
padding: EdgeInsets.symmetric(horizontal: 15),
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
child: GestureDetector(
|
|
|
|
|
|
onTap: () {
|
|
|
|
|
|
if (searchController.text.isNotEmpty) {
|
|
|
|
|
|
searchController.clear();
|
|
|
|
|
|
}
|
2025-02-21 22:51:56 +02:00
|
|
|
|
},
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
child: PopupMenuButton<String>(
|
|
|
|
|
|
enabled: searchController.text.isEmpty,
|
|
|
|
|
|
icon: SvgPicture.asset(
|
|
|
|
|
|
"assets/blaster.svg",
|
|
|
|
|
|
width: 30,
|
|
|
|
|
|
colorFilter: const ColorFilter.mode(Colors.orange, BlendMode.srcIn),
|
2025-02-21 22:51:56 +02:00
|
|
|
|
),
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
onSelected: (String value) {
|
|
|
|
|
|
switch (value) {
|
|
|
|
|
|
case 'Toggle Tags':
|
|
|
|
|
|
widget.toggleTags();
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'Toggle Theme':
|
|
|
|
|
|
setState(() {
|
|
|
|
|
|
widget.toggleTheme(!widget.isDarkMode);
|
|
|
|
|
|
});
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'External Editor':
|
|
|
|
|
|
_toggleExternalEditor(!_useExternalEditor);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'Select Folder':
|
|
|
|
|
|
_showDirectoryChoiceDialog(
|
|
|
|
|
|
notesDirectoryPath ?? ''); // Re-trigger popup
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'Empty Trash':
|
|
|
|
|
|
_confirmEmptyTrash();
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'Help':
|
|
|
|
|
|
_showInfoPage(context, "Help", helpContent);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'About':
|
|
|
|
|
|
_showInfoPage(context, "About", aboutContent);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
itemBuilder: (BuildContext context) => [
|
|
|
|
|
|
PopupMenuItem<String>(
|
|
|
|
|
|
value: 'Toggle Tags',
|
|
|
|
|
|
child: ListTile(
|
|
|
|
|
|
leading:
|
|
|
|
|
|
Icon(widget.showTags ? Icons.short_text : Icons.tag),
|
|
|
|
|
|
title: Text(widget.showTags ? 'Hide Tags' : 'Show Tags'),
|
2025-02-21 22:51:56 +02:00
|
|
|
|
),
|
|
|
|
|
|
),
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
PopupMenuItem<String>(
|
|
|
|
|
|
value: 'Toggle Theme',
|
|
|
|
|
|
child: ListTile(
|
|
|
|
|
|
leading: Icon(
|
|
|
|
|
|
Theme.of(context).brightness == Brightness.dark
|
|
|
|
|
|
? Icons.light_mode
|
|
|
|
|
|
: Icons.dark_mode,
|
|
|
|
|
|
),
|
|
|
|
|
|
title: Text(
|
|
|
|
|
|
Theme.of(context).brightness == Brightness.dark
|
|
|
|
|
|
? 'Switch to Light Mode'
|
|
|
|
|
|
: 'Switch to Dark Mode',
|
|
|
|
|
|
),
|
2025-02-22 16:57:29 +02:00
|
|
|
|
),
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
),
|
|
|
|
|
|
PopupMenuItem<String>(
|
|
|
|
|
|
value: 'Include Subdirectories',
|
|
|
|
|
|
child: ListTile(
|
|
|
|
|
|
leading: Icon(_includeSubdirectories ? Icons.file_copy_outlined : Icons.folder_copy_outlined),
|
|
|
|
|
|
title: Text(_includeSubdirectories ? 'Exclude Subdirectories' : 'Search Subdirectories'),
|
|
|
|
|
|
onTap: () {
|
|
|
|
|
|
_toggleIncludeSubdirectories(!_includeSubdirectories);
|
|
|
|
|
|
Navigator.pop(context); // Close menu after toggle
|
|
|
|
|
|
},
|
2025-02-22 16:57:29 +02:00
|
|
|
|
),
|
|
|
|
|
|
),
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
PopupMenuItem<String>(
|
|
|
|
|
|
value: 'Include File Content',
|
|
|
|
|
|
child: ListTile(
|
|
|
|
|
|
leading: Icon(
|
|
|
|
|
|
_includeFileContent ? Icons.highlight_off : Icons.file_open_outlined,
|
|
|
|
|
|
),
|
|
|
|
|
|
title: Text(
|
|
|
|
|
|
_includeFileContent ? 'Exclude File Content' : 'Search File Content',
|
|
|
|
|
|
),
|
|
|
|
|
|
onTap: () {
|
|
|
|
|
|
_toggleIncludeFileContent(!_includeFileContent);
|
|
|
|
|
|
Navigator.pop(context);
|
|
|
|
|
|
},
|
2025-02-22 14:29:01 +02:00
|
|
|
|
),
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
),
|
|
|
|
|
|
PopupMenuItem<String>(
|
|
|
|
|
|
value: 'External Editor',
|
|
|
|
|
|
child: ListTile(
|
|
|
|
|
|
leading: Icon(
|
|
|
|
|
|
_useExternalEditor
|
|
|
|
|
|
? Icons.open_in_new_off
|
|
|
|
|
|
: Icons.open_in_new,
|
|
|
|
|
|
),
|
|
|
|
|
|
title: Text(
|
|
|
|
|
|
!_useExternalEditor
|
|
|
|
|
|
? 'Use External Editor'
|
|
|
|
|
|
: 'Use Internal Editor',
|
|
|
|
|
|
),
|
2025-02-22 14:29:01 +02:00
|
|
|
|
),
|
|
|
|
|
|
),
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
PopupMenuItem<String>(
|
|
|
|
|
|
value: 'Select Folder',
|
|
|
|
|
|
child: ListTile(
|
|
|
|
|
|
leading: Icon(Icons.folder_shared_outlined),
|
|
|
|
|
|
title: Text('Set Notes Directory'),
|
|
|
|
|
|
),
|
2025-02-21 22:51:56 +02:00
|
|
|
|
),
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
PopupMenuDivider(),
|
|
|
|
|
|
PopupMenuItem<String>(
|
|
|
|
|
|
value: 'Empty Trash',
|
|
|
|
|
|
child: ListTile(
|
|
|
|
|
|
leading: Icon(Icons.delete, color: Colors.redAccent),
|
|
|
|
|
|
title: Text('Empty Trash',
|
|
|
|
|
|
style: TextStyle(color: Colors.redAccent)),
|
|
|
|
|
|
),
|
2025-02-21 22:51:56 +02:00
|
|
|
|
),
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
PopupMenuDivider(),
|
|
|
|
|
|
PopupMenuItem<String>(
|
|
|
|
|
|
value: 'Help',
|
|
|
|
|
|
child: ListTile(
|
|
|
|
|
|
leading: Icon(Icons.help_outline),
|
|
|
|
|
|
title: Text('Help'),
|
|
|
|
|
|
),
|
2025-02-22 20:59:42 +02:00
|
|
|
|
),
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
PopupMenuItem<String>(
|
|
|
|
|
|
value: 'About',
|
|
|
|
|
|
child: ListTile(
|
|
|
|
|
|
leading: Icon(Icons.info_outline),
|
|
|
|
|
|
title: Text('About'),
|
|
|
|
|
|
),
|
2025-02-22 20:59:42 +02:00
|
|
|
|
),
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
],
|
|
|
|
|
|
),
|
2025-02-21 22:51:56 +02:00
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
body: Padding(
|
2025-02-22 20:59:42 +02:00
|
|
|
|
padding: EdgeInsets.only(left: 10),
|
2025-02-23 10:10:56 +02:00
|
|
|
|
child: NotificationListener<ScrollNotification>(
|
|
|
|
|
|
onNotification: (ScrollNotification scrollInfo) {
|
|
|
|
|
|
if (!_isLoadingMore && scrollInfo.metrics.pixels >= scrollInfo.metrics.maxScrollExtent - 100) {
|
|
|
|
|
|
_loadMoreNotes(); // ✅ Lazy load when user scrolls
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
},
|
|
|
|
|
|
child: ListView.builder(
|
|
|
|
|
|
itemCount: filteredNotes.length > _visibleLimit ? _visibleLimit + 1 : filteredNotes.length,
|
|
|
|
|
|
itemBuilder: (context, index) {
|
|
|
|
|
|
if (index == _visibleLimit && filteredNotes.length > _visibleLimit) {
|
|
|
|
|
|
return Center(
|
|
|
|
|
|
child: Padding(
|
|
|
|
|
|
padding: EdgeInsets.all(10),
|
|
|
|
|
|
child: CircularProgressIndicator(), // ✅ Show loading indicator
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
final noteFile = filteredNotes[index];
|
|
|
|
|
|
return Dismissible(
|
|
|
|
|
|
key: Key(noteFile.path),
|
|
|
|
|
|
direction: DismissDirection.endToStart,
|
|
|
|
|
|
background: Container(
|
|
|
|
|
|
color: Colors.redAccent,
|
|
|
|
|
|
alignment: Alignment.centerRight,
|
|
|
|
|
|
padding: EdgeInsets.symmetric(horizontal: 20),
|
|
|
|
|
|
child: Icon(Icons.delete, color: Colors.white),
|
|
|
|
|
|
),
|
|
|
|
|
|
onDismissed: (direction) => _deleteNote(noteFile),
|
|
|
|
|
|
child: ListTile(
|
|
|
|
|
|
title: Text(noteFile.uri.pathSegments.last.replaceAll('.md', '')),
|
2025-02-22 15:58:17 +02:00
|
|
|
|
subtitle: LayoutBuilder(
|
|
|
|
|
|
builder: (context, constraints) {
|
2025-02-23 10:10:56 +02:00
|
|
|
|
final bool hasEnoughSpace = constraints.maxWidth > 200; // Adjust threshold
|
2025-02-22 15:58:17 +02:00
|
|
|
|
|
|
|
|
|
|
return Row(
|
|
|
|
|
|
children: [
|
|
|
|
|
|
if (path.relative(path.dirname(noteFile.path), from: notesDirectoryPath!) != ".")
|
|
|
|
|
|
Padding(
|
|
|
|
|
|
padding: EdgeInsets.only(right: 8),
|
|
|
|
|
|
child: Text(
|
|
|
|
|
|
path.relative(path.dirname(noteFile.path), from: notesDirectoryPath!),
|
|
|
|
|
|
style: TextStyle(color: Colors.blueGrey),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
2025-02-23 10:10:56 +02:00
|
|
|
|
if (widget.showTags && hasEnoughSpace && noteTags[noteFile]?.isNotEmpty == true)
|
2025-02-22 15:58:17 +02:00
|
|
|
|
Expanded(
|
|
|
|
|
|
child: RichText(
|
|
|
|
|
|
text: TextSpan(
|
|
|
|
|
|
style: DefaultTextStyle.of(context).style,
|
2025-02-23 10:10:56 +02:00
|
|
|
|
children: _formatTags(noteTags[noteFile]!.join(' ')), // ✅ Use stored tags
|
2025-02-22 15:58:17 +02:00
|
|
|
|
),
|
2025-02-23 10:10:56 +02:00
|
|
|
|
overflow: TextOverflow.ellipsis,
|
2025-02-22 15:58:17 +02:00
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
],
|
|
|
|
|
|
);
|
|
|
|
|
|
},
|
|
|
|
|
|
),
|
2025-02-22 15:45:04 +02:00
|
|
|
|
onTap: () async {
|
|
|
|
|
|
if (_useExternalEditor) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
if (Platform.isLinux) {
|
|
|
|
|
|
final result = await Process.run('which', ['xdg-open']);
|
|
|
|
|
|
if (result.stdout.toString().trim().isNotEmpty) {
|
2025-02-23 10:10:56 +02:00
|
|
|
|
await Process.run('xdg-open', [noteFile.path]);
|
2025-02-22 15:45:04 +02:00
|
|
|
|
} else {
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
debugPrint("❌ `xdg-open` is not available.");
|
2025-02-22 15:45:04 +02:00
|
|
|
|
}
|
|
|
|
|
|
} else if (Platform.isAndroid) {
|
|
|
|
|
|
await _requestStoragePermission();
|
|
|
|
|
|
final result = await OpenFilex.open(
|
2025-02-23 10:10:56 +02:00
|
|
|
|
noteFile.path,
|
2025-02-22 15:45:04 +02:00
|
|
|
|
type: "text/markdown",
|
|
|
|
|
|
);
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
debugPrint("✅ OpenFilex result: ${result.type}, message: ${result.message}");
|
2025-02-22 14:29:01 +02:00
|
|
|
|
}
|
2025-02-22 15:45:04 +02:00
|
|
|
|
} catch (e) {
|
devbox setup, dep upgrades, lint fixes, Android build fixes
- Add devbox.json pinning Flutter 3.35.7, Android SDK (Nix flake),
openjdk17, just, openssl
- Add android/nix-android-sdk/ Nix flake + flake.lock + Android Studio
config script
- Add justfile with build/get/upgrade/icons/analyze/rebuild recipes
- flutter pub upgrade --major-versions: file_picker ^11, google_fonts ^8,
permission_handler ^12, share_plus ^12, flutter_lints ^6 (+71 transitive)
- Fix file_picker v11 API: FilePicker.platform → FilePicker.getDirectoryPath()
- Fix all 37 analyzer issues: deprecated APIs (window, WillPopScope, Share,
SvgPicture color), async BuildContext guards, key constructors, private
createState() return types, unused locals, print → debugPrint, etc.
- Hook _handleCommand into onSubmitted for : prefix commands
- Android: Gradle 8.3→8.11.1, AGP 8.2.2→8.9.1, KGP 1.8.22→2.2.20,
compileSdk/targetSdk 34→36, namespace auto-derivation for library modules,
kotlin.compiler.execution.strategy=in-process
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:08:29 +03:00
|
|
|
|
debugPrint("❌ Failed to open external editor: $e");
|
2025-02-22 14:29:01 +02:00
|
|
|
|
}
|
2025-02-22 15:45:04 +02:00
|
|
|
|
} else {
|
|
|
|
|
|
await Navigator.push(
|
|
|
|
|
|
context,
|
|
|
|
|
|
MaterialPageRoute(
|
|
|
|
|
|
builder: (context) => NoteEditorScreen(
|
2025-02-23 10:10:56 +02:00
|
|
|
|
note: noteFile,
|
2025-02-22 15:45:04 +02:00
|
|
|
|
isDarkMode: widget.isDarkMode,
|
|
|
|
|
|
notesDirectoryPath: notesDirectoryPath,
|
|
|
|
|
|
),
|
2025-02-22 14:29:01 +02:00
|
|
|
|
),
|
2025-02-22 15:45:04 +02:00
|
|
|
|
);
|
2025-02-23 10:10:56 +02:00
|
|
|
|
_loadNotes(); // ✅ Reload notes after returning
|
|
|
|
|
|
setState(() {}); // ✅ Force UI refresh
|
2025-02-22 15:45:04 +02:00
|
|
|
|
}
|
2025-02-23 10:10:56 +02:00
|
|
|
|
},
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
|
|
|
|
|
},
|
|
|
|
|
|
),
|
2025-02-21 22:51:56 +02:00
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
2025-02-22 20:59:42 +02:00
|
|
|
|
}
|