diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 450e216..f09af5b 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,6 +1,8 @@ github: [adeeteya] -buy_me_a_coffee: adeeteya patreon: adeeteya -liberapay: adeeteya +open_collective: adeeteya ko_fi: adeeteya -open_collective: adeeteya \ No newline at end of file +liberapay: adeeteya +issuehunt: adeeteya +buy_me_a_coffee: adeeteya +thanks_dev: adeeteya diff --git a/.github/workflows/build-and-deploy.yaml b/.github/workflows/build-and-deploy.yaml index 1822404..8e1bd71 100644 --- a/.github/workflows/build-and-deploy.yaml +++ b/.github/workflows/build-and-deploy.yaml @@ -155,7 +155,7 @@ jobs: - name: Upload Windows Release Artifact to GitHub Release shell: cmd run: | - gh release upload ${{ inputs.version_number }} D:\a\FlutterMarkdownEditor\FlutterMarkdownEditor\MarkdownEditor-Windows.exe + gh release upload ${{ inputs.version_number }} "%GITHUB_WORKSPACE%\MarkdownEditor-Windows.exe" env: GH_TOKEN: ${{ secrets.GH_TOKEN }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 77a6711..6555cb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1,6 @@ -🖌️ updated dependencies \ No newline at end of file +Added LaTex support +Added Horizontal Swipe to Switch between Preview and Editing View in Single View Mode +Added Default Folder for Opening and Saving .md files +Added Print/Save as Pdf Option +Added the option to check/uncheck checkboxes in preview mode +Added Convenient way to add tables \ No newline at end of file diff --git a/README.md b/README.md index fdbd94c..f15b12c 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,11 @@ Please star⭐ the repo if you like what you see😊. Download .rpm +
+
+ + Get it from the Snap Store + @@ -96,6 +101,7 @@ Please star⭐ the repo if you like what you see😊. - [x] Horizontal Swipe to Switch between Preview and Editing View in Single View Mode - [x] Default Folder for Opening and Saving .md files - [x] Added Print/Save as Pdf Option +- [x] Added the option to check/uncheck checkboxes in preview mode ## 📸 Screenshots @@ -103,16 +109,25 @@ Please star⭐ the repo if you like what you see😊. ## 🔌 Plugins -| Name | Usage | -|-----------------------------------------------------------------------------|-------------------------------------------------------------| -| [**flutter_markdown**](https://pub.dev/packages/flutter_markdown) | To render markdown text | -| [**permission_handler**](https://pub.dev/packages/permission_handler) | To get storage permissions for opening and saving .md files | -| [**url_launcher**](https://pub.dev/packages/url_launcher) | To launch markdown links | -| [**file_picker**](https://pub.dev/packages/file_picker) | To open markdown files directly from the app | -| [**expandable**](https://pub.dev/packages/expandable) | To create expandable header buttons | -| [**flutter_localizations**](https://pub.dev/packages/flutter_localizations) | Internationalizing app | -| [**intl**](https://pub.dev/packages/intl) | Provides internationalization and localization facilities | -| [**flutter_lints**](https://pub.dev/packages/flutter_lints) | For linting | +| Name | Usage | +|---------------------------------------------------------------------------------------------|-------------------------------------------------------------| +| [**expandable**](https://pub.dev/packages/expandable) | To create expandable header buttons | +| [**file_picker**](https://pub.dev/packages/file_picker) | To open markdown files directly from the app | +| [**flutter_localizations**](https://pub.dev/packages/flutter_localizations) | Internationalizing app | +| [**flutter_math_fork**](https://pub.dev/packages/flutter_math_fork) | Help with LaTeX support | +| [**flutter_svg**](https://pub.dev/packages/flutter_svg) | To Display Svg Images | +| [**flutter_widget_from_html_core**](https://pub.dev/packages/flutter_widget_from_html_core) | To add html display support | +| [**html**](https://pub.dev/packages/html) | To help display html format | +| [**htmltopdfwidgets**](https://pub.dev/packages/htmltopdfwidgets) | To convert html to pdf for printing | +| [**intl**](https://pub.dev/packages/intl) | Provides internationalization and localization facilities | +| [**markdown**](https://pub.dev/packages/markdown) | To convert markdown to html format | +| [**markdown_widget**](https://pub.dev/packages/markdown_widget) | To help display markdown/custom markdown | +| [**pdf**](https://pub.dev/packages/pdf) | To help save/create a pdf | +| [**permission_handler**](https://pub.dev/packages/permission_handler) | To get storage permissions for opening and saving .md files | +| [**printing**](https://pub.dev/packages/printing) | To help printing a pdf | +| [**shared_preferences**](https://pub.dev/packages/shared_preferences) | To store device preferences for persistence | +| [**url_launcher**](https://pub.dev/packages/url_launcher) | To launch markdown links | +| [**flutter_lints**](https://pub.dev/packages/flutter_lints) | For linting | ## 🤓 Author diff --git a/SECURITY.md b/SECURITY.md index b123a2f..062c731 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -4,6 +4,7 @@ | Version | Supported | |---------|--------------------| +| 2.0.0 | :white_check_mark: | | 1.4.1 | :white_check_mark: | | 1.4.0 | :white_check_mark: | | 1.3.0 | :white_check_mark: | diff --git a/lib/home.dart b/lib/home.dart index 621f450..894027b 100644 --- a/lib/home.dart +++ b/lib/home.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'dart:math' as math; import 'package:file_picker/file_picker.dart'; import 'package:flutter/foundation.dart'; @@ -10,6 +11,7 @@ import 'package:htmltopdfwidgets/htmltopdfwidgets.dart' as html2pdf; import 'package:markdown/markdown.dart' as md; import 'package:markdown_editor/device_preference_notifier.dart'; import 'package:markdown_editor/l10n/generated/app_localizations.dart'; +import 'package:markdown_editor/widgets/MarkdownBody/custom_checkbox.dart'; import 'package:markdown_editor/widgets/MarkdownBody/custom_image_config.dart'; import 'package:markdown_editor/widgets/MarkdownBody/custom_text_node.dart'; import 'package:markdown_editor/widgets/MarkdownBody/latex_node.dart'; @@ -33,6 +35,9 @@ class _HomeState extends State { static const _methodChannel = MethodChannel( "com.adeeteya.markdown_editor/channel", ); + static final RegExp _taskListLinePattern = RegExp( + r'^(?:[-+*]|\d+[.)])\s+\[(?: |x|X)\]', + ); String _filePath = "/storage/emulated/0/Download"; String _fileName = 'Markdown'; bool _isPreview = false; @@ -263,6 +268,7 @@ class _HomeState extends State { final config = isDark ? MarkdownConfig.darkConfig : MarkdownConfig.defaultConfig; + int checkboxIndex = -1; return Card( child: SizedBox( height: double.infinity, @@ -283,7 +289,22 @@ class _HomeState extends State { CustomTextNode(node.textContent, config, visitor), richTextBuilder: Text.rich, ), - config: config.copy(configs: [CustomImgConfig()]), + config: config.copy( + configs: [ + CustomImgConfig(), + CheckBoxConfig( + builder: (checked) { + checkboxIndex++; + final currentIndex = checkboxIndex; + return CustomCheckbox( + key: ValueKey('markdown-task-checkbox-$currentIndex'), + checked: checked, + onChanged: () => _toggleTaskListCheckbox(currentIndex), + ); + }, + ), + ], + ), ), ), ), @@ -346,6 +367,70 @@ class _HomeState extends State { ); } + List _taskListMarkerPositions(String text) { + final positions = []; + final lines = text.split('\n'); + var offset = 0; + + for (final line in lines) { + var sanitizedLine = line; + if (sanitizedLine.endsWith('\r')) { + sanitizedLine = sanitizedLine.substring(0, sanitizedLine.length - 1); + } + sanitizedLine = sanitizedLine.trimLeft(); + while (sanitizedLine.startsWith('>')) { + sanitizedLine = sanitizedLine.substring(1).trimLeft(); + } + if (_taskListLinePattern.hasMatch(sanitizedLine)) { + final bracketIndex = line.indexOf('['); + if (bracketIndex != -1) { + positions.add(offset + bracketIndex); + } + } + offset += line.length + 1; + } + + return positions; + } + + void _toggleTaskListCheckbox(int index) { + final markerPositions = _taskListMarkerPositions(_inputText); + if (index < 0 || index >= markerPositions.length) { + return; + } + final markerStart = markerPositions[index]; + if (markerStart + 2 >= _inputText.length) { + return; + } + final currentState = _inputText[markerStart + 1]; + final newState = (currentState == 'x' || currentState == 'X') ? ' ' : 'x'; + final updatedText = _inputText.replaceRange( + markerStart, + markerStart + 3, + '[$newState]', + ); + final currentSelection = _textEditingController.selection; + final collapsedSelection = currentSelection.isValid + ? TextSelection( + baseOffset: math.min( + currentSelection.baseOffset, + updatedText.length, + ), + extentOffset: math.min( + currentSelection.extentOffset, + updatedText.length, + ), + ) + : TextSelection.collapsed(offset: updatedText.length); + setState(() { + _inputText = updatedText; + _textEditingController.value = TextEditingValue( + text: updatedText, + selection: collapsedSelection, + ); + }); + } + @override Widget build(BuildContext context) { return GestureDetector( diff --git a/lib/widgets/MarkdownBody/custom_checkbox.dart b/lib/widgets/MarkdownBody/custom_checkbox.dart new file mode 100644 index 0000000..33d433f --- /dev/null +++ b/lib/widgets/MarkdownBody/custom_checkbox.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; + +class CustomCheckbox extends StatelessWidget { + final bool checked; + final VoidCallback onChanged; + + const CustomCheckbox({ + super.key, + required this.checked, + required this.onChanged, + }); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 2), + child: Checkbox( + value: checked, + onChanged: (_) => onChanged(), + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + visualDensity: VisualDensity.compact, + ), + ); + } +} diff --git a/lib/widgets/MarkdownBody/custom_image_config.dart b/lib/widgets/MarkdownBody/custom_image_config.dart index 419f32d..42d8f5d 100644 --- a/lib/widgets/MarkdownBody/custom_image_config.dart +++ b/lib/widgets/MarkdownBody/custom_image_config.dart @@ -24,8 +24,12 @@ class CustomImgConfig extends ImgConfig { try { final w = attrs['width']; final h = attrs['height']; - if (w != null && w.trim().isNotEmpty) width = double.parse(w); - if (h != null && h.trim().isNotEmpty) height = double.parse(h); + if (w != null && w.trim().isNotEmpty) { + width = double.tryParse(w) ?? 0; + } + if (h != null && h.trim().isNotEmpty) { + height = double.tryParse(h) ?? 0; + } } catch (_) {} final alt = attrs['alt'] ?? ''; diff --git a/linux/markdown_editor.desktop b/linux/markdown_editor.desktop index eaa037b..d8c8b05 100644 --- a/linux/markdown_editor.desktop +++ b/linux/markdown_editor.desktop @@ -1,5 +1,5 @@ [Desktop Entry] -Version=1.4.1 +Version=2.0.0 Name=Markdown Editor Comment=Markdown Editor is a lightweight and intuitive app for creating and editing .md files with ease Exec=/usr/bin/markdown-editor diff --git a/pubspec.yaml b/pubspec.yaml index 822236e..e4e3352 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: A Flutter app to edit and save markdown files publish_to: 'none' -version: 1.4.1+7 +version: 2.0.0+8 environment: sdk: ^3.9.2 diff --git a/scripts/windows-setup.iss b/scripts/windows-setup.iss index 171351c..fda1a05 100644 --- a/scripts/windows-setup.iss +++ b/scripts/windows-setup.iss @@ -5,6 +5,7 @@ #define MyAppExeName "MarkdownEditor.exe" #define MyAppContact "adeeteya@gmail.com" #define MyAppCopyright "Copyright (C) 2025 Adeeteya" +#define Workspace GetEnv("GITHUB_WORKSPACE") [Setup] AppId={{B23EEE54-4907-4B4A-9739-A56313D52E7A} @@ -21,9 +22,9 @@ AppContact={#MyAppContact} AppCopyright={#MyAppCopyright} DefaultDirName={autopf}\{#MyAppName} DisableProgramGroupPage=yes -OutputDir=D:\a\FlutterMarkdownEditor\FlutterMarkdownEditor\ +OutputDir={#Workspace} OutputBaseFilename=MarkdownEditor-Windows -SetupIconFile=D:\a\FlutterMarkdownEditor\FlutterMarkdownEditor\windows\runner\resources\app_icon.ico +SetupIconFile={#Workspace}\windows\runner\resources\app_icon.ico Compression=lzma SolidCompression=yes WizardStyle=modern @@ -43,7 +44,7 @@ Name: "english"; MessagesFile: "compiler:Default.isl" Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked [Files] -Source: "D:\a\FlutterMarkdownEditor\FlutterMarkdownEditor\build\windows\x64\runner\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs +Source: "{#Workspace}\build\windows\x64\runner\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs [Icons] Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}" diff --git a/snap/gui/markdown-editor.desktop b/snap/gui/markdown-editor.desktop index ccee729..56bfe5b 100644 --- a/snap/gui/markdown-editor.desktop +++ b/snap/gui/markdown-editor.desktop @@ -1,5 +1,5 @@ [Desktop Entry] -Version=1.4.1 +Version=2.0.0 Name=Markdown Editor Comment=Markdown Editor is an app for creating and editing .md files with ease Exec=markdown-editor diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 25e66ab..2a7d364 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -1,5 +1,5 @@ name: markdown-editor -version: 1.4.1 +version: 2.0.0 summary: Markdown Editor is an app for creating and editing .md files with ease. description: Markdown Editor is a lightweight and intuitive app for creating and editing .md files with ease. icon: snap/gui/markdown-editor.png