Compare commits

...

35 Commits

Author SHA1 Message Date
266c6ee507 finalize the screenshots 2026-02-15 17:19:38 +00:00
2954cb8678 Incremented to version v0.1.17
All checks were successful
Release / release (push) Successful in 1m16s
2026-02-15 17:07:50 +00:00
4b75381cb9 make dropdown menus uniform between linux and windows 2026-02-15 17:07:11 +00:00
90ce361ea5 windows screenshot for readme 2026-02-15 16:47:53 +00:00
508a1c48e3 Incremented to version v0.1.16
All checks were successful
Release / release (push) Successful in 1m21s
2026-02-15 15:33:45 +00:00
f98471e4f4 fix broken word wrapping 2026-02-15 15:32:44 +00:00
209ede7a1a Incremented to version v0.1.15
All checks were successful
Release / release (push) Successful in 1m18s
2026-02-15 14:22:21 +00:00
245a56d270 one final. last. dead end. attempt 2026-02-15 14:22:06 +00:00
89f4b0aeba Incremented to version v0.1.14
All checks were successful
Release / release (push) Successful in 1m15s
2026-02-15 14:15:47 +00:00
962e3632bb one final. last. dead end. attempt 2026-02-15 14:15:23 +00:00
98264b2a3c Incremented to version v0.1.13
All checks were successful
Release / release (push) Successful in 1m17s
2026-02-15 14:09:08 +00:00
b832ba5f8f annotation message. not commit message 2026-02-15 14:08:31 +00:00
43281e047d Incremented to version v0.1.12
All checks were successful
Release / release (push) Successful in 1m14s
2026-02-15 13:59:41 +00:00
21c3a7c7c0 debugging the runner again 2026-02-15 13:58:50 +00:00
b0e2eda4bb Incremented to version v0.1.11
All checks were successful
Release / release (push) Successful in 1m19s
2026-02-15 13:54:17 +00:00
d652ec7c04 last ditch effort to fix the yaml 2026-02-15 13:53:36 +00:00
63bb7f7316 Incremented to version v0.1.10
All checks were successful
Release / release (push) Successful in 1m14s
2026-02-15 13:47:47 +00:00
12c615b62c fix gitea workflow bug 2026-02-15 13:46:20 +00:00
99c20fa8de Incremented to version v0.1.9
All checks were successful
Release / release (push) Successful in 1m18s
2026-02-15 13:37:03 +00:00
e519d87193 make sure release message is included in release notes 2026-02-15 13:36:22 +00:00
7601f829bc Incremented to version v0.1.8
All checks were successful
Release / release (push) Successful in 1m13s
2026-02-15 13:25:06 +00:00
84448f7d46 include monospace font fallback for linux 2026-02-15 13:24:29 +00:00
7f056036d0 Incremented to version v0.1.7
All checks were successful
Release / release (push) Successful in 1m16s
2026-02-15 13:00:24 +00:00
c8e70f41bc add command-line filename argument, so open-with works 2026-02-15 12:59:43 +00:00
a52468f03b Incremented to version v0.1.6
All checks were successful
Release / release (push) Successful in 1m18s
2026-02-15 12:15:40 +00:00
d0a66ae6b4 Incremented to version --help 2026-02-15 12:13:54 +00:00
c1e2993e70 fix selection focus bug 2026-02-15 12:11:50 +00:00
95f07d93a9 add release script 2026-02-14 22:39:12 +00:00
b82312d26f add dynamic versioning to build/release scripts;add fixed versioning to csproj file
All checks were successful
Release / release (push) Successful in 1m15s
2026-02-14 22:24:29 +00:00
afd13c660e add an application icon
All checks were successful
Release / release (push) Successful in 1m19s
2026-02-14 22:08:38 +00:00
b0a0b353cc rename the screenshot 2026-02-14 21:55:49 +00:00
cb96663152 Update README.md 2026-02-14 15:40:30 +00:00
5ed3433d13 add disclaimer to bottom of readme 2026-02-14 15:32:10 +00:00
d26aa15a7f add screenshot to readme 2026-02-14 15:28:09 +00:00
65b57fc0b7 add readme and unlicense 2026-02-14 15:20:19 +00:00
16 changed files with 655 additions and 38 deletions

View File

@ -15,6 +15,9 @@ jobs:
run: echo "${LOCIP} gitea.comnenos" >> /etc/hosts run: echo "${LOCIP} gitea.comnenos" >> /etc/hosts
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true
- name: Install Build Dependencies - name: Install Build Dependencies
run: | run: |
@ -59,16 +62,17 @@ jobs:
- name: Create Release Archive - name: Create Release Archive
run: | run: |
VERSION=${GITHUB_REF#refs/tags/} VERSION=${GITHUB_REF#refs/tags/}
VERSION_NUM=${VERSION#v} # Remove 'v' prefix (e.g., v0.1.4 -> 0.1.4)
mkdir -p release mkdir -p release
# Copy AppImage (if it exists) # Copy AppImage (if it exists) - use version from build
if [ -f publish/appimage/NotePad-0.1.0-x86_64.AppImage ]; then if [ -f publish/appimage/NotePad-${VERSION_NUM}-x86_64.AppImage ]; then
cp publish/appimage/NotePad-0.1.0-x86_64.AppImage \ cp publish/appimage/NotePad-${VERSION_NUM}-x86_64.AppImage \
release/NotePad-${VERSION}-x86_64.AppImage release/NotePad-${VERSION}-x86_64.AppImage
fi fi
# Copy Linux Tarball # Copy Linux Tarball
cp publish/tarball/notepad-0.1.0-linux-x64.tar.gz \ cp publish/tarball/notepad-${VERSION_NUM}-linux-x64.tar.gz \
release/notepad-${VERSION}-linux-x64.tar.gz release/notepad-${VERSION}-linux-x64.tar.gz
# Create Windows zip # Create Windows zip
@ -91,6 +95,21 @@ jobs:
run: | run: |
VERSION=${GITHUB_REF#refs/tags/} VERSION=${GITHUB_REF#refs/tags/}
# Explicitly fetch the tag with annotation
echo "Fetching tag ${VERSION} with annotation..."
git fetch origin tag ${VERSION} --force
# Get the tag annotation message (not the commit message)
echo "Extracting tag annotation..."
TAG_MESSAGE=$(git cat-file tag ${VERSION} | sed '1,/^$/d')
echo "Tag annotation: ${TAG_MESSAGE}"
# Create release body
RELEASE_BODY=$(printf "%s\n\n## Downloads\n\n### Linux\n- **AppImage**: Portable, single-file executable (no installation needed)\n- **Tarball**: Extract and run \`sudo ./install.sh\` to install\n\n### Windows\n- **Zip**: Extract and run \`NotePad.exe\`" "$TAG_MESSAGE")
# Escape the full body for JSON
RELEASE_BODY_JSON=$(printf '%s' "$RELEASE_BODY" | sed 's/"/\\"/g' | sed ':a;N;$!ba;s/\n/\\n/g')
# Create release # Create release
curl -X POST "https://repos.gmgauthier.com/api/v1/repos/${GITHUB_REPOSITORY}/releases" \ curl -X POST "https://repos.gmgauthier.com/api/v1/repos/${GITHUB_REPOSITORY}/releases" \
-H "Authorization: token ${GITEA_TOKEN}" \ -H "Authorization: token ${GITEA_TOKEN}" \
@ -98,7 +117,7 @@ jobs:
-d "{ -d "{
\"tag_name\": \"${VERSION}\", \"tag_name\": \"${VERSION}\",
\"name\": \"NotePad ${VERSION}\", \"name\": \"NotePad ${VERSION}\",
\"body\": \"Release ${VERSION}\n\n## Downloads\n\n### Linux\n- **AppImage**: Portable, single-file executable (no installation needed)\n- **Tarball**: Extract and run \`sudo ./install.sh\` to install\n\n### Windows\n- **Zip**: Extract and run \`NotePad.exe\`\" \"body\": \"${RELEASE_BODY_JSON}\"
}" > release_response.json }" > release_response.json
RELEASE_ID=$(cat release_response.json | grep -o '"id":[0-9]*' | head -1 | cut -d':' -f2) RELEASE_ID=$(cat release_response.json | grep -o '"id":[0-9]*' | head -1 | cut -d':' -f2)

3
.gitignore vendored
View File

@ -6,3 +6,6 @@ publish/
riderModule.iml riderModule.iml
/_ReSharper.Caches/ /_ReSharper.Caches/
poetry.lock poetry.lock
# ImageMagick artifacts
!_*

View File

@ -15,7 +15,12 @@ public partial class App : Application
{ {
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{ {
desktop.MainWindow = new MainWindow(); string? fileToOpen = null;
if (desktop.Args?.Length > 0)
{
fileToOpen = desktop.Args[0];
}
desktop.MainWindow = new MainWindow(fileToOpen);
} }
base.OnFrameworkInitializationCompleted(); base.OnFrameworkInitializationCompleted();

View File

@ -4,6 +4,27 @@
Title="Untitled - Notepad" Title="Untitled - Notepad"
Width="600" Height="400"> Width="600" Height="400">
<Window.Styles>
<Style Selector="Menu">
<Setter Property="Background" Value="#D3D3D3"/>
</Style>
<Style Selector="MenuItem">
<Setter Property="Background" Value="#D3D3D3"/>
<Setter Property="Foreground" Value="Black"/>
</Style>
<Style Selector="MenuItem:pointerover /template/ Border#PART_LayoutRoot">
<Setter Property="Background" Value="#A9A9A9"/>
</Style>
<Style Selector="MenuItem:open /template/ Popup#PART_Popup > Border">
<Setter Property="Background" Value="#D3D3D3"/>
<Setter Property="BorderBrush" Value="#A9A9A9"/>
<Setter Property="BorderThickness" Value="1"/>
</Style>
<Style Selector="Separator">
<Setter Property="Background" Value="#A9A9A9"/>
</Style>
</Window.Styles>
<DockPanel> <DockPanel>
<!-- Menu Bar --> <!-- Menu Bar -->
<Menu DockPanel.Dock="Top"> <Menu DockPanel.Dock="Top">
@ -15,7 +36,7 @@
<Separator/> <Separator/>
<MenuItem Header="E_xit" Click="OnExitClick"/> <MenuItem Header="E_xit" Click="OnExitClick"/>
</MenuItem> </MenuItem>
<MenuItem Header="_Edit"> <MenuItem Header="_Edit" x:Name="EditMenuItem">
<MenuItem Header="_Undo" Click="OnUndoClick" InputGesture="Ctrl+Z"/> <MenuItem Header="_Undo" Click="OnUndoClick" InputGesture="Ctrl+Z"/>
<Separator/> <Separator/>
<MenuItem Header="Cu_t" Click="OnCutClick" InputGesture="Ctrl+X"/> <MenuItem Header="Cu_t" Click="OnCutClick" InputGesture="Ctrl+X"/>
@ -29,7 +50,7 @@
<MenuItem Header="F_ormat"> <MenuItem Header="F_ormat">
<MenuItem Header="_Word Wrap" x:Name="WordWrapMenuItem" Click="OnWordWrapToggle"> <MenuItem Header="_Word Wrap" x:Name="WordWrapMenuItem" Click="OnWordWrapToggle">
<MenuItem.Icon> <MenuItem.Icon>
<CheckBox x:Name="WordWrapCheckBox" IsChecked="True" IsHitTestVisible="False"/> <CheckBox x:Name="WordWrapCheckBox" IsChecked="False" IsHitTestVisible="False"/>
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
</MenuItem> </MenuItem>
@ -43,7 +64,7 @@
<TextBox x:Name="EditorTextBox" <TextBox x:Name="EditorTextBox"
AcceptsReturn="True" AcceptsReturn="True"
AcceptsTab="True" AcceptsTab="True"
TextWrapping="Wrap" TextWrapping="NoWrap"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Auto"
@ -52,8 +73,9 @@
Background="White" Background="White"
Foreground="Black" Foreground="Black"
CaretBrush="Black" CaretBrush="Black"
FontFamily="Consolas,Courier New,monospace" FontFamily="Courier New,Liberation Mono,Courier,monospace"
FontSize="12"> FontSize="13"
ClearSelectionOnLostFocus="False">
<TextBox.Styles> <TextBox.Styles>
<Style Selector="TextBox"> <Style Selector="TextBox">
<Setter Property="Background" Value="White"/> <Setter Property="Background" Value="White"/>

View File

@ -17,7 +17,11 @@ namespace NotePad
private bool _isModified; private bool _isModified;
private string? _originalText; private string? _originalText;
public MainWindow() public MainWindow() : this(null)
{
}
public MainWindow(string? fileToOpen)
{ {
InitializeComponent(); InitializeComponent();
UpdateTitle(); UpdateTitle();
@ -26,6 +30,30 @@ namespace NotePad
textBox.TextChanged += OnTextChanged; textBox.TextChanged += OnTextChanged;
Closing += OnWindowClosing; Closing += OnWindowClosing;
if (!string.IsNullOrEmpty(fileToOpen) && File.Exists(fileToOpen))
{
_ = LoadFileAsync(fileToOpen);
}
}
private async Task LoadFileAsync(string filePath)
{
try
{
var text = await File.ReadAllTextAsync(filePath);
_currentFilePath = filePath;
_currentFileName = Path.GetFileName(filePath);
this.FindControl<TextBox>("EditorTextBox")!.Text = text;
_originalText = text;
_isModified = false;
UpdateTitle();
}
catch (Exception)
{
// Silently ignore file loading errors on startup
}
} }
private void UpdateTitle() private void UpdateTitle()
@ -266,7 +294,17 @@ namespace NotePad
var textBox = this.FindControl<TextBox>("EditorTextBox")!; var textBox = this.FindControl<TextBox>("EditorTextBox")!;
checkBox.IsChecked = !checkBox.IsChecked; checkBox.IsChecked = !checkBox.IsChecked;
textBox.TextWrapping = checkBox.IsChecked == true ? TextWrapping.Wrap : TextWrapping.NoWrap;
if (checkBox.IsChecked == true)
{
textBox.TextWrapping = TextWrapping.Wrap;
ScrollViewer.SetHorizontalScrollBarVisibility(textBox, Avalonia.Controls.Primitives.ScrollBarVisibility.Disabled);
}
else
{
textBox.TextWrapping = TextWrapping.NoWrap;
ScrollViewer.SetHorizontalScrollBarVisibility(textBox, Avalonia.Controls.Primitives.ScrollBarVisibility.Auto);
}
} }
// Edit Menu // Edit Menu

View File

@ -1,18 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk"> <?xml version='1.0' encoding='utf-8'?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ApplicationManifest>app.manifest</ApplicationManifest> <ApplicationManifest>app.manifest</ApplicationManifest>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault> <AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
<ApplicationIcon>img\notepad-icon.ico</ApplicationIcon>
<Version>0.1.17</Version>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Avalonia" Version="11.3.12"/> <PackageReference Include="Avalonia" Version="11.3.12" />
<PackageReference Include="Avalonia.Desktop" Version="11.3.12"/> <PackageReference Include="Avalonia.Desktop" Version="11.3.12" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.12"/> <PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.12" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.12"/> <PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.12" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Include="Avalonia.Diagnostics" Version="11.3.12"> <PackageReference Include="Avalonia.Diagnostics" Version="11.3.12">
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets> <IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets> <PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>

View File

@ -5,13 +5,14 @@
set -e set -e
APP_NAME="NotePad" APP_NAME="NotePad"
VERSION="0.1.0" VERSION="${1:-0.1.0}" # Use first argument or default to 0.1.0
ARCH="x86_64" ARCH="x86_64"
# Directories # Directories
PUBLISH_DIR="../publish/linux-x64" PUBLISH_DIR="../publish/linux-x64"
APPDIR="NotePad.AppDir" APPDIR="NotePad.AppDir"
OUTPUT_DIR="../publish/appimage" OUTPUT_DIR="../publish/appimage"
ICON_SOURCE="img/notepad-icon.svg"
echo "============================================================" echo "============================================================"
echo "Building AppImage for $APP_NAME v$VERSION" echo "Building AppImage for $APP_NAME v$VERSION"
@ -47,15 +48,14 @@ Categories=Utility;TextEditor;
Terminal=false Terminal=false
EOF EOF
# Create a simple icon (using text as placeholder - replace with actual icon) # Copy application icon
echo "Creating icon placeholder..." echo "Copying application icon..."
# This creates a simple SVG icon - you should replace with an actual icon if [ ! -f "$ICON_SOURCE" ]; then
cat > "$APPDIR/usr/share/icons/hicolor/256x256/apps/notepad.svg" << EOF echo "❌ ERROR: Icon not found at $ICON_SOURCE"
<svg width="256" height="256" xmlns="http://www.w3.org/2000/svg"> exit 1
<rect width="256" height="256" fill="#4a90e2"/> fi
<text x="128" y="140" font-size="120" fill="white" text-anchor="middle" font-family="sans-serif">N</text> mkdir -p "$APPDIR/usr/share/icons/hicolor/scalable/apps"
</svg> cp "$ICON_SOURCE" "$APPDIR/usr/share/icons/hicolor/scalable/apps/notepad.svg"
EOF
# Create AppRun script # Create AppRun script
echo "Creating AppRun script..." echo "Creating AppRun script..."
@ -71,7 +71,7 @@ chmod +x "$APPDIR/AppRun"
# Copy desktop file and icon to root of AppDir # Copy desktop file and icon to root of AppDir
cp "$APPDIR/usr/share/applications/notepad.desktop" "$APPDIR/" cp "$APPDIR/usr/share/applications/notepad.desktop" "$APPDIR/"
cp "$APPDIR/usr/share/icons/hicolor/256x256/apps/notepad.svg" "$APPDIR/notepad.svg" cp "$APPDIR/usr/share/icons/hicolor/scalable/apps/notepad.svg" "$APPDIR/notepad.svg"
# Check for appimagetool # Check for appimagetool
if ! command -v appimagetool &> /dev/null; then if ! command -v appimagetool &> /dev/null; then

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 KiB

View File

@ -0,0 +1,213 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.1"
width="72pt"
height="72pt"
id="svg604">
<metadata
id="metadata45">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs606">
<linearGradient
id="linearGradient612">
<stop
id="stop613"
style="stop-color:#7fa8e3;stop-opacity:0.74117601"
offset="0" />
<stop
id="stop614"
style="stop-color:#ffffff;stop-opacity:0"
offset="1" />
</linearGradient>
<linearGradient
x1="50.643887"
y1="5.3679295"
x2="35.752094"
y2="68.044357"
id="linearGradient615"
xlink:href="#linearGradient612"
gradientUnits="userSpaceOnUse"
spreadMethod="pad" />
</defs>
<path
d="M 12.8163,74.753 C 10.3856,73.6481 7.07107,70.1126 8.83884,65.2512 L 78.6656,20.8361 c 1.9888,1.7678 3.5356,3.2041 1.9888,7.0711 -1.1048,1.9887 -14.2527,46.6248 -14.4736,49.4975 -0.8839,3.0936 -3.425,6.8501 -7.6235,6.6291 -4.1984,-0.221 -42.6474,-8.6179 -45.741,-9.2808 z"
transform="matrix(1.114903,0,0,1.114903,-5.305332,-3.889256)"
id="path794"
style="fill-opacity:0.30147125;fill-rule:evenodd" />
<path
d="M 12.8163,74.753 C 10.3856,73.6481 7.07107,70.1126 8.83884,65.2512 9.72267,59.948 25.6326,13.1021 29.1682,10.8924 31.3778,8.90368 34.582,8.13026 39.1119,9.34561 c 3.7565,0.88389 37.565,9.72279 39.5537,11.49049 1.9888,1.7678 3.5356,3.2041 1.9888,7.0711 -1.1048,1.9887 -14.2527,46.6248 -14.4736,49.4975 -0.8839,3.0936 -3.425,6.8501 -7.6235,6.6291 -4.1984,-0.221 -42.6474,-8.6179 -45.741,-9.2808 z"
transform="matrix(1.114903,0,0,1.114903,-8.162471,-8.460684)"
id="path609"
style="fill:#3d4e67;fill-rule:evenodd" />
<path
d="M 17.8987,70.9965 C 15.3576,70.3336 11.7114,69.6706 13.0373,65.6932 15.468,55.9705 27.8424,18.6264 33.1456,13.986 c 1.2154,-1.1049 5.3033,-1.5468 13.0373,0.8839 8.8389,2.6516 26.7375,8.6178 28.5053,9.5017 1.7677,0.8839 2.4307,0.8839 1.1048,4.8614 -1.9887,3.9775 -10.8275,38.007 -13.0373,43.7522 -1.5467,4.088 -1.8782,5.9662 -4.5299,5.9662"
transform="matrix(1.114903,0,0,1.114903,-8.162471,-8.460684)"
id="path608"
style="fill:#ffffff;fill-rule:evenodd" />
<path
d="m 28.75,13.75 c 0,0 3.125,-6.25005 10.9375,-4.37505 C 47.5,11.25 78.125,19.375 77.8125,21.875 77.5,24.375 71.875,39.0625 71.5625,40.3125 71.25,41.5625 42.5,40.9375 31.5625,47.5 20.625,54.0625 12.5,59.6875 12.5,59.6875 c 0,0 11.25,-35 16.25,-45.9375 z"
transform="matrix(0.94678,0,0,0.94678,-0.480566,0.409517)"
id="path636"
style="fill:url(#linearGradient615);fill-rule:evenodd" />
<path
d="M 29.2429,15.8991 69.8423,26.6877"
transform="translate(1.135645,0.851736)"
id="path671"
style="fill:none;stroke:#42667e;stroke-width:1pt" />
<path
d="M 27.8233,19.306 67.8549,30.9464"
transform="matrix(0.999855,-0.01703691,0.01703691,0.999855,-0.705041,5.077353)"
id="path672"
style="fill:none;stroke:#42667e;stroke-width:1pt" />
<path
d="M 29.2429,15.8991 69.8423,26.6877"
transform="translate(-5.394323,17.0347)"
id="path673"
style="fill:none;stroke:#42667e;stroke-width:1pt" />
<path
d="M 29.2429,15.8991 69.8423,26.6877"
transform="translate(-11.35647,34.0694)"
id="path675"
style="fill:none;stroke:#42667e;stroke-width:1pt" />
<path
d="M 27.8233,19.306 67.8549,30.9464"
transform="matrix(0.999855,-0.01703691,0.01703691,0.999855,-13.19715,38.29502)"
id="path676"
style="fill:none;stroke:#42667e;stroke-width:1pt" />
<path
d="M 27.8233,19.306 67.8549,30.9464"
transform="matrix(0.999855,-0.01703691,0.01703691,0.999855,-7.235008,21.26032)"
id="path674"
style="fill:none;stroke:#42667e;stroke-width:1pt" />
<g
id="g666">
<path
d="m 44.8581,15.0473 c -3.4478,0 -5.1105,-2.5484 -5.1105,-6.38799 0,-3.83962 2.7983,-6.95584 6.2461,-6.95584 3.4478,0 4.5425,1.12884 4.5425,4.96845"
transform="matrix(0.948718,0,0,1,4.011168,0.567827)"
id="path667"
style="fill:none;stroke:#4f4f4f;stroke-width:2.875;stroke-linecap:round" />
<path
d="m 44.8581,14.7634 c -3.4478,0 -5.3944,-2.2645 -5.3944,-6.10409 0,-3.83962 2.7983,-7.807574 6.2461,-7.807574 3.4478,0 4.8264,1.980574 4.8264,5.820184"
transform="matrix(0.948718,0,0,1,4.011168,0.567827)"
id="path668"
style="fill:none;stroke:#c2c2c2;stroke-width:0.625;stroke-linecap:round" />
</g>
<use
transform="translate(10.788642,1.987381)"
id="use669"
x="0"
y="0"
width="90"
height="90"
xlink:href="#g666" />
<g
transform="matrix(0.69841,0,0,0.69841,31.4038,0.671206)"
id="g677">
<defs
id="defs611">
<linearGradient
id="linearGradient626">
<stop
id="stop627"
style="stop-color:#b5772d;stop-opacity:1"
offset="0" />
<stop
id="stop628"
style="stop-color:#ffffff;stop-opacity:0"
offset="1" />
</linearGradient>
<linearGradient
id="linearGradient619">
<stop
id="stop620"
style="stop-color:#770000;stop-opacity:1"
offset="0" />
<stop
id="stop621"
style="stop-color:#ffffff;stop-opacity:0"
offset="1" />
</linearGradient>
<linearGradient
x1="66.331696"
y1="23.212469"
x2="63.465496"
y2="20.962799"
id="linearGradient622"
xlink:href="#linearGradient619"
gradientUnits="userSpaceOnUse"
gradientTransform="scale(1.004516,0.995504)"
spreadMethod="pad" />
<linearGradient
x1="56.327614"
y1="43.377247"
x2="51.654526"
y2="39.762909"
id="linearGradient625"
xlink:href="#linearGradient626"
gradientUnits="userSpaceOnUse"
gradientTransform="scale(0.912664,1.095694)"
spreadMethod="pad" />
</defs>
<path
d="M 9.33685,82.1156 C 17.9129,79.7641 30.9112,77.5637 40.1097,71.8924 56.9851,62.0714 66.0118,41.6332 76.4551,25.5877 77.285,12.3086 64.1128,5.96872 59.1331,8.87345 46.4766,25.4722 37.5548,43.5235 23.4458,55.9725 19.0886,66.5542 9.12934,82.738 9.33685,82.1156 z"
transform="translate(3.814559,2.934275)"
id="path632"
style="fill-opacity:0.28318601;fill-rule:evenodd" />
<path
d="M 9.33685,82.1156 C 17.9129,79.7641 28.5638,74.9228 37.7623,69.2516 54.6377,59.4306 64.2512,38.4055 74.6945,22.36 75.5244,9.08092 64.1128,5.96872 59.1331,8.87345 46.4766,25.4722 37.5548,43.5235 23.4458,55.9725 19.0886,66.5542 9.12934,82.738 9.33685,82.1156 z"
id="path617"
style="fill-rule:evenodd" />
<path
d="m 71.9972,20.9076 c -1.729,2.4898 -3.4581,4.9796 -5.1871,7.4694 -2.2823,-5.3946 -4.9796,-8.0919 -11.8266,-8.7143 2.2132,-2.6973 4.4263,-5.3946 6.6395,-8.0919 5.5329,-0.6225 9.8209,4.1497 10.3742,9.3368 z"
id="path613"
style="fill:#be6d6d;fill-rule:evenodd" />
<path
d="m 54.776,20.9076 c 5.3946,-0.3458 9.1293,3.6656 11.2041,8.2994 -8.4377,13.6248 -15.423,27.0422 -29.6703,37.7622 0,0 -3.5272,-10.1668 -8.7143,-8.9218 6.432,-7.262 21.3709,-28.0104 27.1805,-37.1398 z"
id="path614"
style="fill:#ffbc24;fill-rule:evenodd" />
<path
d="m 27.0767,58.6698 c 4.6684,-0.2421 8.6106,5.3255 7.5732,8.7144 -2.0057,1.5215 -5.2562,3.0431 -7.2619,4.5646 -0.3458,-2.7664 -2.144,-4.9104 -6.0171,-4.5646 1.729,-2.974 3.9768,-5.7404 5.7058,-8.7144 z"
id="path615"
style="fill:#e9dbb9;fill-rule:evenodd" />
<path
d="m 21.7859,66.9692 c 3.5618,-0.8299 5.0488,2.6973 5.8096,4.7722 l -13.2791,6.017 7.4695,-10.7892 z"
id="path616"
style="fill:#586f93;fill-rule:evenodd" />
<path
d="m 71.9972,20.9076 c -1.729,2.4898 -3.4581,4.9796 -5.1871,7.4694 -2.2823,-5.3946 -4.9796,-8.0919 -11.8266,-8.7143 2.2132,-2.6973 4.4263,-5.3946 6.6395,-8.0919 5.5329,-0.6225 9.8209,4.1497 10.3742,9.3368 z"
id="path618"
style="fill:url(#linearGradient622);fill-rule:evenodd" />
<path
d="m 62.8679,12.1932 -5.3946,6.847 2.9048,1.0374 5.3946,-6.432 -2.9048,-1.4524 z"
id="path623"
style="fill:#ffffff;fill-rule:evenodd" />
<path
d="m 54.776,20.9076 c 5.3946,-0.3458 9.1293,3.6656 11.2041,8.2994 -8.4377,13.6248 -15.423,27.0422 -29.6703,37.7622 0,0 -3.5272,-10.1668 -8.7143,-8.9218 6.432,-7.262 21.3709,-28.0104 27.1805,-37.1398 z"
id="path624"
style="fill:url(#linearGradient625);fill-rule:evenodd" />
<path
d="m 29.6703,56.18 c 1.2449,0.7608 2.4898,1.5215 3.7347,2.2823 C 42.5344,47.6039 46.6841,36.5381 58.9257,24.6423 57.7499,23.8815 56.5742,23.1208 55.3984,22.36 48.0673,35.0857 39.6987,47.3965 29.6703,56.18 z"
id="path630"
style="fill:#ffffff;fill-rule:evenodd" />
<path
d="m 34.7538,67.2805 c 0.2074,-2.1786 -0.3113,-3.7348 -1.4525,-5.4984 -1.5215,3.3889 -3.873,5.8441 -7.1582,6.6395 -0.7262,0.6224 1.2449,1.9711 1.4524,3.216 1.8674,-1.1412 7.1583,-3.7347 7.1583,-4.3571 z"
id="path631"
style="fill:#9c7850;fill-rule:evenodd" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -8,6 +8,8 @@ APP_NAME="NotePad"
INSTALL_DIR="/opt/notepad" INSTALL_DIR="/opt/notepad"
BIN_LINK="/usr/local/bin/notepad" BIN_LINK="/usr/local/bin/notepad"
DESKTOP_FILE="/usr/share/applications/notepad.desktop" DESKTOP_FILE="/usr/share/applications/notepad.desktop"
ICON_DIR="/usr/share/icons/hicolor/scalable/apps"
ICON_FILE="$ICON_DIR/notepad.svg"
echo "============================================================" echo "============================================================"
echo "$APP_NAME Installer" echo "$APP_NAME Installer"
@ -41,6 +43,11 @@ chmod +x "$INSTALL_DIR/NotePad"
echo "→ Creating symlink: $BIN_LINK" echo "→ Creating symlink: $BIN_LINK"
ln -sf "$INSTALL_DIR/NotePad" "$BIN_LINK" ln -sf "$INSTALL_DIR/NotePad" "$BIN_LINK"
# Install icon
echo "→ Installing application icon..."
mkdir -p "$ICON_DIR"
cp "$SCRIPT_DIR/img/notepad-icon.svg" "$ICON_FILE"
# Create desktop entry # Create desktop entry
echo "→ Creating desktop entry..." echo "→ Creating desktop entry..."
cat > "$DESKTOP_FILE" << EOF cat > "$DESKTOP_FILE" << EOF
@ -48,13 +55,19 @@ cat > "$DESKTOP_FILE" << EOF
Name=NotePad Name=NotePad
Comment=Simple text editor Comment=Simple text editor
Exec=notepad %F Exec=notepad %F
Icon=text-editor Icon=notepad
Type=Application Type=Application
Categories=Utility;TextEditor; Categories=Utility;TextEditor;
Terminal=false Terminal=false
MimeType=text/plain; MimeType=text/plain;
EOF EOF
# Update icon cache if available
if command -v gtk-update-icon-cache &> /dev/null; then
echo "→ Updating icon cache..."
gtk-update-icon-cache -f -t /usr/share/icons/hicolor 2>/dev/null || true
fi
# Update desktop database if available # Update desktop database if available
if command -v update-desktop-database &> /dev/null; then if command -v update-desktop-database &> /dev/null; then
echo "→ Updating desktop database..." echo "→ Updating desktop database..."

View File

@ -9,9 +9,30 @@ import sys
import os import os
import shutil import shutil
import tarfile import tarfile
import xml.etree.ElementTree as ET
from pathlib import Path from pathlib import Path
def get_version():
"""Extract version from .csproj file"""
csproj_path = Path("NotePad.csproj")
if not csproj_path.exists():
print("⚠️ WARNING: NotePad.csproj not found. Using default version 0.1.0")
return "0.1.0"
try:
tree = ET.parse(csproj_path)
root = tree.getroot()
version_elem = root.find(".//Version")
if version_elem is not None and version_elem.text:
return version_elem.text
print("⚠️ WARNING: Version not found in .csproj. Using default version 0.1.0")
return "0.1.0"
except Exception as e:
print(f"⚠️ WARNING: Failed to parse .csproj: {e}. Using default version 0.1.0")
return "0.1.0"
def run_command(cmd, description): def run_command(cmd, description):
"""Run a shell command and print status""" """Run a shell command and print status"""
print(f"\n{'=' * 60}") print(f"\n{'=' * 60}")
@ -69,7 +90,7 @@ def build_windows_installer():
print(f"\n⚠️ WARNING: Failed to create installer: {e}") print(f"\n⚠️ WARNING: Failed to create installer: {e}")
def build_linux_tarball(): def build_linux_tarball(version):
"""Create tar.gz archive with install script""" """Create tar.gz archive with install script"""
print(f"\n{'=' * 60}") print(f"\n{'=' * 60}")
print("Creating Linux tar.gz archive") print("Creating Linux tar.gz archive")
@ -100,8 +121,13 @@ def build_linux_tarball():
shutil.copy(script_path, temp_dir / "notepad" / script) shutil.copy(script_path, temp_dir / "notepad" / script)
os.chmod(temp_dir / "notepad" / script, 0o755) os.chmod(temp_dir / "notepad" / script, 0o755)
# Copy img directory (contains icon)
img_dir = Path("img")
if img_dir.exists():
shutil.copytree(img_dir, temp_dir / "notepad" / "img")
# Create tar.gz # Create tar.gz
tarball_path = tarball_dir / "notepad-0.1.0-linux-x64.tar.gz" tarball_path = tarball_dir / f"notepad-{version}-linux-x64.tar.gz"
with tarfile.open(tarball_path, "w:gz") as tar: with tarfile.open(tarball_path, "w:gz") as tar:
tar.add(temp_dir / "notepad", arcname="notepad") tar.add(temp_dir / "notepad", arcname="notepad")
@ -116,7 +142,7 @@ def build_linux_tarball():
print(f" sudo ./install.sh") print(f" sudo ./install.sh")
def build_linux_appimage(): def build_linux_appimage(version):
"""Build Linux AppImage""" """Build Linux AppImage"""
build_script = Path("build-appimage.sh") build_script = Path("build-appimage.sh")
if not build_script.exists(): if not build_script.exists():
@ -125,7 +151,7 @@ def build_linux_appimage():
try: try:
result = subprocess.run( result = subprocess.run(
["bash", str(build_script)], ["bash", str(build_script), version],
capture_output=True, capture_output=True,
text=True text=True
) )
@ -190,10 +216,11 @@ def publish_platform(platform, linux_package='both'):
# Build Linux packages # Build Linux packages
if platform == 'linux': if platform == 'linux':
version = get_version()
if linux_package in ['appimage', 'both']: if linux_package in ['appimage', 'both']:
build_linux_appimage() build_linux_appimage(version)
if linux_package in ['tarball', 'both']: if linux_package in ['tarball', 'both']:
build_linux_tarball() build_linux_tarball(version)
def main(): def main():

137
README.md Normal file
View File

@ -0,0 +1,137 @@
# NotePad
A simple cross-platform text editor built with Avalonia UI and .NET 8.
| Windows | Linux |
|-----------------------------------------------------------------| ----- |
| ![Windows Screenshot](NotePad/img/notepad-screenshot-win11.png) | ![Linux Screenshot](NotePad/img/notepad-screenshot-linux.png) |
## Installation
### Linux
Download the latest release from the [releases page](https://repos.gmgauthier.com/gmgauthier/notepad/releases).
#### Option 1: AppImage (Recommended)
The AppImage is a portable, single-file executable that requires no installation.
```bash
# Download the AppImage
wget https://repos.gmgauthier.com/gmgauthier/notepad/releases/download/v0.1.3/NotePad-v0.1.3-x86_64.AppImage
# Make it executable
chmod +x NotePad-v0.1.3-x86_64.AppImage
# Run it
./NotePad-v0.1.3-x86_64.AppImage
```
#### Option 2: System Installation (Tarball)
For a traditional system-wide installation:
```bash
# Download and extract
wget https://repos.gmgauthier.com/gmgauthier/notepad/releases/download/v0.1.3/notepad-v0.1.3-linux-x64.tar.gz
tar -xzf notepad-v0.1.3-linux-x64.tar.gz
cd notepad
# Install (requires sudo)
sudo ./install.sh
```
This installs NotePad to `/opt/notepad` and creates:
- Command-line shortcut: `notepad`
- Desktop menu entry
To uninstall:
```bash
sudo /opt/notepad/uninstall.sh
```
### Windows
Download the latest release from the [releases page](https://repos.gmgauthier.com/gmgauthier/notepad/releases).
```powershell
# Download the zip file
# Extract notepad-v0.1.3-win-x64.zip
# Run NotePad.exe from the extracted folder
```
No installation required - just extract and run!
## Building from Source
### Prerequisites
- [.NET 8 SDK](https://dotnet.microsoft.com/download/dotnet/8.0)
- Python 3.12+
- (Linux only) `appimagetool` for AppImage builds
### Build Commands
```bash
cd NotePad
# Build for Linux only
python3 publish.py linux
# Build for Windows only
python3 publish.py windows
# Build for both platforms
python3 publish.py both
# Linux-specific package options
python3 publish.py linux appimage # AppImage only
python3 publish.py linux tarball # Tarball only
python3 publish.py linux both # Both packages
```
Build outputs are located in `../publish/`:
- Linux AppImage: `publish/appimage/`
- Linux Tarball: `publish/tarball/`
- Windows: `publish/win-x64/`
## Development
### Project Structure
```
NotePad/
├── NotePad/ # Main application
│ ├── App.axaml # Application XAML
│ ├── MainWindow.axaml # Main window UI
│ ├── Program.cs # Entry point
│ └── publish.py # Build script
├── .gitea/
│ └── workflows/
│ └── release.yaml # CI/CD workflow
└── README.md
```
### Running in Development
```bash
cd NotePad/NotePad
dotnet run
```
## License
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means.
For more information, please refer to <https://unlicense.org>
## Author
Greg Gauthier
## Acknowledgements
Okay, I'll admit it. This application was build with the assisance of Grok, and the Claude agent built into Jetbrains Rider.

24
UNLICENSE Normal file
View File

@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org>

113
make-release.py Executable file
View File

@ -0,0 +1,113 @@
#!/usr/bin/env python3
"""
Release script for NotePad
Automates version bumping, committing, tagging, and pushing
"""
import sys
import subprocess
import xml.etree.ElementTree as ET
from pathlib import Path
def run_command(cmd, description):
"""Run a command and handle errors"""
print(f"\n{description}...")
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
print(f"❌ ERROR: {description} failed!")
print(f" {result.stderr}")
sys.exit(1)
if result.stdout.strip():
print(f" {result.stdout.strip()}")
return result
def update_version_in_csproj(version):
"""Update the version in NotePad.csproj"""
csproj_path = Path("NotePad/NotePad.csproj")
if not csproj_path.exists():
print(f"❌ ERROR: {csproj_path} not found!")
sys.exit(1)
print(f"\n→ Updating version to {version} in {csproj_path}...")
try:
tree = ET.parse(csproj_path)
root = tree.getroot()
version_elem = root.find(".//Version")
if version_elem is not None:
version_elem.text = version
else:
# Add Version element if it doesn't exist
property_group = root.find(".//PropertyGroup")
if property_group is not None:
version_elem = ET.SubElement(property_group, "Version")
version_elem.text = version
else:
print("❌ ERROR: Could not find PropertyGroup in .csproj")
sys.exit(1)
tree.write(csproj_path, encoding='utf-8', xml_declaration=True)
print(f" ✅ Version updated to {version}")
except Exception as e:
print(f"❌ ERROR: Failed to update .csproj: {e}")
sys.exit(1)
def main():
if len(sys.argv) < 2:
print("Usage: python3 make-release.py <version> [tag message]")
print("")
print("Examples:")
print(" python3 make-release.py v0.1.6")
print(" python3 make-release.py v0.1.6 'Added fireworks to launch animations'")
sys.exit(1)
version_tag = sys.argv[1]
# Remove 'v' prefix for .csproj version
version_num = version_tag.lstrip('v')
# Get tag message from remaining arguments
tag_message = ' '.join(sys.argv[2:]) if len(sys.argv) > 2 else f"Release {version_tag}"
print("=" * 60)
print(f"NotePad Release Script")
print("=" * 60)
print(f"Version: {version_tag}")
print(f"Tag message: {tag_message}")
print("=" * 60)
# Step 1: Update version in .csproj
update_version_in_csproj(version_num)
# Step 2: Commit the version update
commit_message = f"Incremented to version {version_tag}"
run_command(["git", "add", "NotePad/NotePad.csproj"], "Staging .csproj")
run_command(["git", "commit", "-m", commit_message], "Committing version update")
# Step 3: Push to origin master
run_command(["git", "push", "origin", "master"], "Pushing to origin master")
# Step 4: Create git tag
run_command(["git", "tag", "-a", version_tag, "-m", tag_message], f"Creating tag {version_tag}")
# Step 5: Push tag to origin
run_command(["git", "push", "origin", version_tag], f"Pushing tag {version_tag}")
print("\n" + "=" * 60)
print("✅ Release completed successfully!")
print("=" * 60)
print(f"\nThe CI/CD workflow should now build and publish {version_tag}")
print(f"Check your Gitea repository for the release.")
if __name__ == "__main__":
main()