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😊.
+
+
+
+
+
@@ -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