From 79757281e174e92bf02f03a36fbbd2d988b51035 Mon Sep 17 00:00:00 2001 From: Kumara Krishnan <5687650+Kumara-Krishnan@users.noreply.github.com> Date: Sat, 17 Jan 2026 12:51:57 +0530 Subject: [PATCH 1/9] refactor: improve project structure and logging setup in MainWindow --- .DS_Store | Bin 0 -> 10244 bytes .vscode/tasks.json | 2 +- Bookmarkly.App/Bookmarkly.App.csproj | 12 ++++++-- Bookmarkly.App/MainWindow.xaml | 6 ++-- Bookmarkly.App/MainWindow.xaml.cs | 40 +++++++++++++++++++++++---- Bookmarkly.slnx | 25 ++++++++++++----- 6 files changed, 66 insertions(+), 19 deletions(-) create mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..6093abbf9de71157d1c08c45b44fe6681dcce1d5 GIT binary patch literal 10244 zcmeHMU2GIp6h3F#!b}I+0a{wz30+e}NJ}Y@P)Jd>?Jl*@(n?$UBU*QNhIZ)e?7B1C z9}v?AjmE_ION@yRD*n6~V}dauNYE&n;EN$@eegjOHSt9g(MLS@&a~Ox?xrQi1moPz z+%xyiIp?1H-E-#LI|~5Vp3!yz1OY&#i&13_RRs!r&aNdz@OlD7k>mjq5QQ{Mf(f%r zxcv?v0UrS$0UrS$0Uv?e0RhU{tcbEp{n|&sN5Ds55drpo2-3x9$fI*E<*yE^_z{3; zDRuKg_jC^6GC_}qJUZu6E=oGx(*t^<=p8Z86{qJx#!fWk(K(m8;()F=pf@slClutP zlU$Iq1BP7c*FFM10!tBKXZIRtha~+bbI$Kg&;~KE!J!j<6yih;;<3Q)GL!L`nT(Mn z3${+WT5j?}cK{VdwWT1HCwt{qWXz5lVkqn1DJH?Bg=e}!s=4#vW zGTJT6q$aZLa$irIieL~Y1U1lM2Tao)`%+>c4T2xK*F@AI$ZxR3C2>UVLQ#u^C*;*maSSXO6Bqz z^?3P^JTe-c8PI2(e&e)0rc~M!$?2|m(&|g=Q-(f06i=F&gk_H@>*7%}o-w12-e#JN znflf(+0OR+|eY<1CxfODK;~KM0f9z)j=~F(@jN_+;Lj= z$?DLQi;{B0>pZYQR)^`OmY^xxUSFt9RUcx*c1LZeH>F!-(&ygT5qdUiPejvt@3f_- zz1}YB{o^YA`ggwNm_d=1~jkMI-x48Oqd z@E0OVxE`yp8aHAcHewU*#Ae)&5j=#Q*oT8Sgu^(3F&sw&PvImwcp4wYv-lW3iO=Hm z_yWF&ui{(yHok-J;bpvnALA!@4Zp!}aUOrbKkx?ri8lpFC>La*N~jiUgj%6NXcX=d z+Jz3+a>0E&1B@`H;<`2H@xl!m%Mo)@baTAH*@7Aora_6D^#^8Jxv4_&7d6F8mZeO%D7Lp2L^%HGCb<@WB-P{IMx%&6mNawpmD83dNmiYN8M5LOZBmw`GnPJcWJ*1myo4;!#v+HWoRTVS zNhwK2`9>;5wxtWC$}h4~PV1RkNSaG16kkwVFv&o^6Q z1KFdKMQlrE5n60hHbeF$D4jD)XNfMck|U6T|0~esv-k-32>e$Oz|wGkxSgzDI3DFsgnerd()BQ1tT4VgmvRxR z7z^v?pW~^)KF4qK`WDWO?0LxRn{z1_r+zL@)cuyKXX8@{2$N%e*+{5 B&*cCB literal 0 HcmV?d00001 diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 13c1722..be2419c 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -6,7 +6,7 @@ "type": "shell", "command": "msbuild", "args": [ - "${workspaceFolder}/${config:solutionFile}", + "${workspaceFolder}/${config:solutionFile}", "/p:Configuration=${config:dotnet.defaultSolution.configuration}", "/p:Platform=${config:dotnet.defaultSolution.platform}" ], diff --git a/Bookmarkly.App/Bookmarkly.App.csproj b/Bookmarkly.App/Bookmarkly.App.csproj index 7863261..a149e34 100644 --- a/Bookmarkly.App/Bookmarkly.App.csproj +++ b/Bookmarkly.App/Bookmarkly.App.csproj @@ -29,19 +29,27 @@ Tools extension to be activated for this project even if the Windows App SDK Nuget package has not yet been restored. --> - + + + + + + - + true diff --git a/Bookmarkly.App/MainWindow.xaml b/Bookmarkly.App/MainWindow.xaml index 53e7eb5..7fddeea 100644 --- a/Bookmarkly.App/MainWindow.xaml +++ b/Bookmarkly.App/MainWindow.xaml @@ -10,10 +10,10 @@ mc:Ignorable="d"> - + - - + + diff --git a/Bookmarkly.App/MainWindow.xaml.cs b/Bookmarkly.App/MainWindow.xaml.cs index ca4eb6d..d8b03c1 100644 --- a/Bookmarkly.App/MainWindow.xaml.cs +++ b/Bookmarkly.App/MainWindow.xaml.cs @@ -1,3 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Cyclotron.Telemetry.DependencyInjection; +using Cyclotron.Telemetry.Logging; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls.Primitives; @@ -5,14 +15,10 @@ using Microsoft.UI.Xaml.Input; using Microsoft.UI.Xaml.Media; using Microsoft.UI.Xaml.Navigation; -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; using Windows.Foundation; using Windows.Foundation.Collections; +using Windows.Storage; + // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. @@ -38,5 +44,27 @@ public MainWindow() datas.Add(1); InitializeComponent(); } + + private void OnWindowLoaded(object sender, RoutedEventArgs e) + { + var services = new ServiceCollection(); + + services.AddCyclotronTelemetry(options => + { + options.ServiceName = "Bookmarkly"; + options.ServiceVersion = "1.0.0"; + options.DefaultModule = "main"; + + options.Logging.MinimumLevel = LogLevel.Debug; + options.Logging.File.Path = "{LocalAppData}/Bookmarkly/logs/app-.log"; + options.Logging.File.RetainedFileCountLimit = 3; // Already set as default + options.Logging.File.RollingInterval = Serilog.RollingInterval.Day; + }); + + var provider = services.BuildServiceProvider(); + var logger = provider.GetRequiredService().ForModule("Instapaper"); + + logger.LogInformation("Starting operation"); // Caller info auto-captured! + } } } diff --git a/Bookmarkly.slnx b/Bookmarkly.slnx index b6707c3..9694faf 100644 --- a/Bookmarkly.slnx +++ b/Bookmarkly.slnx @@ -6,19 +6,29 @@ - - + + + + + + + - - + + - + @@ -26,6 +36,7 @@ - + - + \ No newline at end of file From 7f5638d781a08402edf6accf077022465644a974 Mon Sep 17 00:00:00 2001 From: Kumara Krishnan <5687650+Kumara-Krishnan@users.noreply.github.com> Date: Fri, 23 Jan 2026 01:09:31 +0530 Subject: [PATCH 2/9] fix: update version handling in update-version.ps1 to prevent extra newlines in manifest --- Bookmarkly.App/Package.appxmanifest | 52 +---------------------------- build/update-version.ps1 | 3 +- 2 files changed, 3 insertions(+), 52 deletions(-) diff --git a/Bookmarkly.App/Package.appxmanifest b/Bookmarkly.App/Package.appxmanifest index 2fdfeb3..9f0b635 100644 --- a/Bookmarkly.App/Package.appxmanifest +++ b/Bookmarkly.App/Package.appxmanifest @@ -48,54 +48,4 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/build/update-version.ps1 b/build/update-version.ps1 index 9b987f3..0bea9dc 100644 --- a/build/update-version.ps1 +++ b/build/update-version.ps1 @@ -80,7 +80,8 @@ if (Test-Path $manifestPath) { # Use a more specific regex to target only the Identity element's Version attribute $manifest = $manifest -replace '(]*Version=")[^"]+(")', "`${1}$fullVersion`${2}" - Set-Content $manifestPath $manifest + # Use System.IO.File to avoid adding extra newlines + [System.IO.File]::WriteAllText($manifestPath, $manifest, [System.Text.Encoding]::UTF8) Write-Host "Updated $manifestPath with version $fullVersion" } else { Write-Warning "Manifest file not found: $manifestPath" From 2f0d3ccb8908683343f8ac0fc8048905149f108e Mon Sep 17 00:00:00 2001 From: Kumara Krishnan <5687650+Kumara-Krishnan@users.noreply.github.com> Date: Fri, 23 Jan 2026 01:10:30 +0530 Subject: [PATCH 3/9] chore: update Cyclotron submodule to latest --- Cyclotron | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cyclotron b/Cyclotron index 2918190..a0ab1e4 160000 --- a/Cyclotron +++ b/Cyclotron @@ -1 +1 @@ -Subproject commit 2918190fb943fe52354852a1360ff1cdec500121 +Subproject commit a0ab1e418fbcc92deff7fee9900c9a9be63879d5 From c4dee72730be84e8b995ca526fe04ff4f53ab14e Mon Sep 17 00:00:00 2001 From: Kumara Krishnan <5687650+Kumara-Krishnan@users.noreply.github.com> Date: Tue, 27 Jan 2026 03:49:27 +0530 Subject: [PATCH 4/9] feat: update landing page title and styles - Updated landing page title to "Bookmarkly - Your Universal Reading Companion" - Added new color scheme for light mode - Adjusted background colors, text colors, and gradients for better readability and aesthetics - Improved button styles for primary and secondary CTAs - Enhanced visual effects like glow, hover animations, and box shadows - Refactored CSS classes for consistency and clarity # BREAKING CHANGE: Updated landing page title to provide a clearer description of the application. --- .github/landing-page/index.html | 794 +++++++++++++++++++++++++++++--- 1 file changed, 737 insertions(+), 57 deletions(-) diff --git a/.github/landing-page/index.html b/.github/landing-page/index.html index 5310f06..9b9c3be 100644 --- a/.github/landing-page/index.html +++ b/.github/landing-page/index.html @@ -1,10 +1,11 @@ + - Bookmarkly - Download + Bookmarkly - Your Universal Reading Companion - -
- -
- Bookmarkly Icon -

Bookmarkly

-

A modern app for your bookmarks

-
- {{VERSION}} - SHA: {{ZIP_SHA}} + + + + + + +
+
+ +
+ +
+

+ Bookmarkly +

+

+ A modern, all-in-one reading companion designed to help you save, organize, and enjoy articles from + across the web and your favorite RSS feeds. +

+ + + + +
+ {{VERSION}} + + {{ZIP_SHA}} +
-
- - -
-
+ + +
+
+
+ Features +

Read smarter, not harder.

+

+ Bookmarkly combines the best of Instapaper and RSS readers into one powerful app, designed for the + way you read today. +

+
+ + +
+
+ Reading Mode +
+

Distraction-free reading mode

+

+ Enjoy a clean, customizable reading experience. Adjust fonts, colors, and spacing to match + your preference. Read comfortably, day or night. +

+
+ Dark mode + Custom fonts + Text-to-speech +
+
+
-

- Use App Installer for automatic updates, or download the ZIP for manual installation. -

-
- - -
-

- - Release Notes -

-
+
+
+ + +
+
+
+ Capabilities +

It already does that.

+

+ Everything you need to save, organize, and read content from anywhere on the web. +

+
+ +
+ +
+
+ + + +
+

Save for later

+

+ Instantly save articles and web pages with our browser extension or share functionality. +

+
+ + +
+
+ + + +
+

RSS feed reader

+

+ Subscribe to unlimited feeds and stay updated with your favorite blogs and news sources. +

+
+ + +
+
+ + + +
+

Highlight & annotate

+

+ Mark important passages, add notes, and create a personal knowledge base. +

+
+ + +
+
+ + + +
+

Smart organization

+

+ Organize with tags, folders, and favorites. Find anything instantly with powerful search. +

+
+ + +
+
+ + + +
+

Cross-device sync

+

+ Seamlessly sync your library, reading progress, and preferences across all devices. +

+
+ + +
+
+ + + +
+

Light & dark themes

+

+ Multiple color schemes including auto-switching dark mode for comfortable reading anytime. +

+
+ + +
+
+ + + +
+

Export & archive

+

+ Export your articles and highlights to Markdown, HTML, or PDF. Own your data. +

+
+ + +
+
+ + + +
+

Keyboard shortcuts

+

+ Navigate efficiently with comprehensive keyboard shortcuts for power users. +

+
+ + +
+
+ + + +
+

Advanced filters

+

+ Filter feeds by keywords, reading time, publication date, and custom rules. +

+
+
+
+
+ + +
+
+
+ Privacy + First +

Your data stays yours.

+

+ Everything is stored locally on your device. No cloud dependency, no tracking, complete control. +

+
+ +
+ +
+
+ + + +
+

100% Local

+

+ All your articles, notes, and reading data stay on your device. No servers, no sync issues. +

+
+ + +
+
+ + + +
+

Smart Reminders

+

+ Gentle nudges to help you stay on top of your reading list. Build a consistent reading habit. +

+
+ + +
+
+ + + +
+

Smart Feeds

+

+ AI-powered filtering learns from your reading patterns to surface the most relevant content. +

+
+
+
+
+ + +
+
+
+ Latest + Release +

What's new

+
+ +
{{RELEASE_NOTES}}
-
- - - -
+ + +
+ +

Windows 10/11 • Free • Open Source

+
+ + + + + + - + + \ No newline at end of file From be19ca19564fe5fb334c28f5e93498ea99aa6950 Mon Sep 17 00:00:00 2001 From: Kumara Krishnan <5687650+Kumara-Krishnan@users.noreply.github.com> Date: Tue, 27 Jan 2026 04:07:56 +0530 Subject: [PATCH 5/9] chore: add .playwright-mcp/ to .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 9aeed70..498eee6 100644 --- a/.gitignore +++ b/.gitignore @@ -421,3 +421,5 @@ FodyWeavers.xsd _site/ api/ .manifest + +.playwright-mcp/ From c50f49b9bca5536815606ed012c29ac885ebdd45 Mon Sep 17 00:00:00 2001 From: Kumara Krishnan <5687650+Kumara-Krishnan@users.noreply.github.com> Date: Tue, 27 Jan 2026 04:17:50 +0530 Subject: [PATCH 6/9] chore: remove DocFX workflow and integrate documentation build into MSIX workflow --- .github/workflows/docfx.yml | 86 --- .github/workflows/msix-build-release.yml | 676 ++++++++++++----------- 2 files changed, 360 insertions(+), 402 deletions(-) delete mode 100644 .github/workflows/docfx.yml diff --git a/.github/workflows/docfx.yml b/.github/workflows/docfx.yml deleted file mode 100644 index 473a7a7..0000000 --- a/.github/workflows/docfx.yml +++ /dev/null @@ -1,86 +0,0 @@ -name: DocFX Documentation - -on: - push: - branches: - - main - - ci - pull_request: - branches: - - main - - ci - - dev - -permissions: - contents: read - pages: write - id-token: write - -# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. -# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. -concurrency: - group: "pages" - cancel-in-progress: false - -jobs: - build-docs: - name: Build Documentation - runs-on: windows-latest - - steps: - - name: Checkout code - uses: actions/checkout@v6 - with: - token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }} - fetch-depth: 0 - submodules: recursive - - - name: Setup .NET - uses: actions/setup-dotnet@v5 - with: - dotnet-version: '10.x' - - - name: Setup DocFX - run: dotnet tool update -g docfx - - - name: Restore dependencies - run: dotnet restore ${{ github.workspace }}/Bookmarkly.slnx - - - name: Build documentation - run: docfx docfx.json - continue-on-error: false - - - name: Upload documentation artifact - uses: actions/upload-artifact@v6 - with: - name: documentation - path: _site - retention-days: 7 - - deploy-docs: - name: Deploy to GitHub Pages - runs-on: ubuntu-latest - needs: build-docs - if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/ci') - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - - steps: - - name: Download documentation artifact - uses: actions/download-artifact@v7 - with: - name: documentation - path: _site - - - name: Setup Pages - uses: actions/configure-pages@v5 - - - name: Upload to GitHub Pages - uses: actions/upload-pages-artifact@v3 - with: - path: _site - - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 diff --git a/.github/workflows/msix-build-release.yml b/.github/workflows/msix-build-release.yml index 95c20dc..0a68b17 100644 --- a/.github/workflows/msix-build-release.yml +++ b/.github/workflows/msix-build-release.yml @@ -14,8 +14,10 @@ env: PLATFORM: x64 permissions: - contents: write # Required for creating releases - deployments: write # Required for Cloudflare Pages deployment + contents: write # Required for creating releases + deployments: write # Required for Cloudflare Pages deployment + pages: write # Required for GitHub Pages deployment + id-token: write # Required for GitHub Pages deployment jobs: bump-version: @@ -23,163 +25,177 @@ jobs: runs-on: windows-latest outputs: version: ${{ steps.version.outputs.version }} - + steps: - - name: Checkout code - uses: actions/checkout@v6 - with: - token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }} - fetch-depth: 0 - submodules: recursive - - - name: Update version - id: version - run: | - ./build/update-version.ps1 - shell: pwsh - - - name: Commit version changes - if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/ci') - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - git add build/version.json Bookmarkly.App/Package.appxmanifest - git diff --staged --quiet || git commit -m "chore: update version to ${{ steps.version.outputs.version }} [skip ci]" - git push - shell: pwsh + - name: Checkout code + uses: actions/checkout@v6 + with: + token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }} + fetch-depth: 0 + submodules: recursive + + - name: Update version + id: version + run: | + ./build/update-version.ps1 + shell: pwsh + + - name: Commit version changes + if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/ci') + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add build/version.json Bookmarkly.App/Package.appxmanifest + git diff --staged --quiet || git commit -m "chore: update version to ${{ steps.version.outputs.version }} [skip ci]" + git push + shell: pwsh build: name: Build Solution runs-on: windows-latest needs: bump-version - + steps: - - name: Checkout code - uses: actions/checkout@v6 - with: - ref: ${{ github.ref_name }} - token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }} - fetch-depth: 1 - submodules: recursive - - - name: Setup .NET - uses: actions/setup-dotnet@v5 - with: - dotnet-version: '10.x' - - - name: Setup MSBuild - uses: microsoft/setup-msbuild@v2 - with: - vs-version: '[17.9,)' - - - name: Restore NuGet packages - run: dotnet restore ${{ env.SOLUTION_FILE_PATH }} - - - name: Create temporary signing certificate - run: | - $cert = New-SelfSignedCertificate -Type Custom -Subject "CN=Bookmarkly-Temp-Cert" -KeyUsage DigitalSignature -FriendlyName "Bookmarkly Temp Signing Cert" -CertStoreLocation "Cert:\CurrentUser\My" -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.3", "2.5.29.19={text}") - $password = ConvertTo-SecureString -String "${{ secrets.CERT_PASSWORD || 'TempPassword123!' }}" -Force -AsPlainText - Export-PfxCertificate -Cert "Cert:\CurrentUser\My\$($cert.Thumbprint)" -FilePath GitHubActionsWorkflow.pfx -Password $password - echo "Certificate created successfully" - shell: pwsh - - - name: Build MSIX package - run: | - msbuild ${{ env.PROJECT_PATH }} ` - /p:Configuration=${{ env.CONFIGURATION }} ` - /p:Platform=${{ env.PLATFORM }} ` - /p:PublishTrimmed=false ` - /p:UapAppxPackageBuildMode=SideloadOnly ` - /p:AppxBundle=Always ` - /p:RuntimeIdentifier=win-x64 ` - /p:AppxBundlePlatforms=${{ env.PLATFORM }} ` - /p:AppxPackageDir="${{ github.workspace }}\AppPackages\" ` - /p:AppxPackageSigningEnabled=true ` - /p:PackageCertificateKeyFile="${{ github.workspace }}\GitHubActionsWorkflow.pfx" ` - /p:PackageCertificatePassword="${{ secrets.CERT_PASSWORD || 'TempPassword123!' }}" ` - /t:_GenerateAppxPackage ` - /restore - shell: pwsh - - - name: Remove certificate file - run: | - if (Test-Path -Path GitHubActionsWorkflow.pfx) { - Remove-Item -Path GitHubActionsWorkflow.pfx -Force - Write-Host "Certificate file removed successfully" - } else { - Write-Host "Certificate file not found, skipping removal" - } - shell: pwsh - if: always() - - - name: Upload build output - uses: actions/upload-artifact@v6 - with: - name: build-output - path: AppPackages/ - retention-days: 1 + - name: Checkout code + uses: actions/checkout@v6 + with: + ref: ${{ github.ref_name }} + token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }} + fetch-depth: 1 + submodules: recursive + + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: "10.x" + + - name: Setup MSBuild + uses: microsoft/setup-msbuild@v2 + with: + vs-version: "[17.9,)" + + - name: Setup DocFX + run: dotnet tool update -g docfx + + - name: Restore NuGet packages + run: dotnet restore ${{ env.SOLUTION_FILE_PATH }} + + - name: Create temporary signing certificate + run: | + $cert = New-SelfSignedCertificate -Type Custom -Subject "CN=Bookmarkly-Temp-Cert" -KeyUsage DigitalSignature -FriendlyName "Bookmarkly Temp Signing Cert" -CertStoreLocation "Cert:\CurrentUser\My" -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.3", "2.5.29.19={text}") + $password = ConvertTo-SecureString -String "${{ secrets.CERT_PASSWORD || 'TempPassword123!' }}" -Force -AsPlainText + Export-PfxCertificate -Cert "Cert:\CurrentUser\My\$($cert.Thumbprint)" -FilePath GitHubActionsWorkflow.pfx -Password $password + echo "Certificate created successfully" + shell: pwsh + + - name: Build MSIX package + run: | + msbuild ${{ env.PROJECT_PATH }} ` + /p:Configuration=${{ env.CONFIGURATION }} ` + /p:Platform=${{ env.PLATFORM }} ` + /p:PublishTrimmed=false ` + /p:UapAppxPackageBuildMode=SideloadOnly ` + /p:AppxBundle=Always ` + /p:RuntimeIdentifier=win-x64 ` + /p:AppxBundlePlatforms=${{ env.PLATFORM }} ` + /p:AppxPackageDir="${{ github.workspace }}\AppPackages\" ` + /p:AppxPackageSigningEnabled=true ` + /p:PackageCertificateKeyFile="${{ github.workspace }}\GitHubActionsWorkflow.pfx" ` + /p:PackageCertificatePassword="${{ secrets.CERT_PASSWORD || 'TempPassword123!' }}" ` + /t:_GenerateAppxPackage ` + /restore + shell: pwsh + + - name: Remove certificate file + run: | + if (Test-Path -Path GitHubActionsWorkflow.pfx) { + Remove-Item -Path GitHubActionsWorkflow.pfx -Force + Write-Host "Certificate file removed successfully" + } else { + Write-Host "Certificate file not found, skipping removal" + } + shell: pwsh + if: always() + + - name: Build documentation + run: docfx docfx.json + continue-on-error: false + + - name: Upload documentation artifact + uses: actions/upload-artifact@v6 + with: + name: documentation + path: _site + retention-days: 7 + + - name: Upload build output + uses: actions/upload-artifact@v6 + with: + name: build-output + path: AppPackages/ + retention-days: 1 package: name: Create Package runs-on: windows-latest needs: [bump-version, build] - + steps: - - name: Download build output - uses: actions/download-artifact@v7 - with: - name: build-output - path: AppPackages/ - - - name: Package into zip - run: | - $zipName = "Bookmarkly_${{ needs.bump-version.outputs.version }}.zip" - if (-not (Test-Path -Path "AppPackages")) { - Write-Error "AppPackages folder not found. Failing so you can inspect logs." - exit 1 - } - Compress-Archive -Path "AppPackages\*" -DestinationPath $zipName -Force - Write-Host "Created zip: $zipName" - shell: pwsh - - - name: Find and copy appinstaller file - run: | - # Find the .appinstaller file in the AppPackages directory - $appInstallerFile = Get-ChildItem -Path "AppPackages" -Filter "*.appinstaller" -Recurse | Select-Object -First 1 - if ($appInstallerFile) { - Copy-Item -Path $appInstallerFile.FullName -Destination "Bookmarkly.appinstaller" - Write-Host "Found and copied appinstaller: $($appInstallerFile.FullName)" - } else { - Write-Host "No .appinstaller file found, creating placeholder" - # Create a placeholder appinstaller file if not found - # Note: Update the domain if deployment location changes - $version = "${{ needs.bump-version.outputs.version }}" - $content = @" - - - - - - - - "@ - $content | Out-File -FilePath "Bookmarkly.appinstaller" -Encoding utf8 - } - shell: pwsh - - - name: Upload package artifact - uses: actions/upload-artifact@v6 - with: - name: Bookmarkly_${{ needs.bump-version.outputs.version }} - path: Bookmarkly_${{ needs.bump-version.outputs.version }}.zip - if-no-files-found: error - - - name: Upload appinstaller artifact - uses: actions/upload-artifact@v6 - with: - name: Bookmarkly_appinstaller - path: Bookmarkly.appinstaller - if-no-files-found: error + - name: Download build output + uses: actions/download-artifact@v7 + with: + name: build-output + path: AppPackages/ + + - name: Package into zip + run: | + $zipName = "Bookmarkly_${{ needs.bump-version.outputs.version }}.zip" + if (-not (Test-Path -Path "AppPackages")) { + Write-Error "AppPackages folder not found. Failing so you can inspect logs." + exit 1 + } + Compress-Archive -Path "AppPackages\*" -DestinationPath $zipName -Force + Write-Host "Created zip: $zipName" + shell: pwsh + + - name: Find and copy appinstaller file + run: | + # Find the .appinstaller file in the AppPackages directory + $appInstallerFile = Get-ChildItem -Path "AppPackages" -Filter "*.appinstaller" -Recurse | Select-Object -First 1 + if ($appInstallerFile) { + Copy-Item -Path $appInstallerFile.FullName -Destination "Bookmarkly.appinstaller" + Write-Host "Found and copied appinstaller: $($appInstallerFile.FullName)" + } else { + Write-Host "No .appinstaller file found, creating placeholder" + # Create a placeholder appinstaller file if not found + # Note: Update the domain if deployment location changes + $version = "${{ needs.bump-version.outputs.version }}" + $content = @" + + + + + + + + "@ + $content | Out-File -FilePath "Bookmarkly.appinstaller" -Encoding utf8 + } + shell: pwsh + + - name: Upload package artifact + uses: actions/upload-artifact@v6 + with: + name: Bookmarkly_${{ needs.bump-version.outputs.version }} + path: Bookmarkly_${{ needs.bump-version.outputs.version }}.zip + if-no-files-found: error + + - name: Upload appinstaller artifact + uses: actions/upload-artifact@v6 + with: + name: Bookmarkly_appinstaller + path: Bookmarkly.appinstaller + if-no-files-found: error release: name: Create Release @@ -188,194 +204,222 @@ jobs: if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/ci') outputs: release_url: ${{ steps.create_release.outputs.url }} - + steps: - - name: Download package artifact - uses: actions/download-artifact@v7 - with: - name: Bookmarkly_${{ needs.bump-version.outputs.version }} - path: . - - - name: Create Release - id: create_release - uses: softprops/action-gh-release@v2 - with: - tag_name: v${{ needs.bump-version.outputs.version }} - name: Release v${{ needs.bump-version.outputs.version }} - files: Bookmarkly_${{ needs.bump-version.outputs.version }}.zip - generate_release_notes: true - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Download package artifact + uses: actions/download-artifact@v7 + with: + name: Bookmarkly_${{ needs.bump-version.outputs.version }} + path: . + + - name: Create Release + id: create_release + uses: softprops/action-gh-release@v2 + with: + tag_name: v${{ needs.bump-version.outputs.version }} + name: Release v${{ needs.bump-version.outputs.version }} + files: Bookmarkly_${{ needs.bump-version.outputs.version }}.zip + generate_release_notes: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} deploy-to-cloudflare: name: Deploy to Cloudflare Pages runs-on: ubuntu-latest needs: [bump-version, package, release] if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/ci') - + steps: - - name: Checkout code - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - - name: Download package artifact (ZIP) - uses: actions/download-artifact@v7 - with: - name: Bookmarkly_${{ needs.bump-version.outputs.version }} - path: ./package - - - name: Download appinstaller artifact - uses: actions/download-artifact@v7 - with: - name: Bookmarkly_appinstaller - path: ./package - - - name: Prepare landing page - run: | - # Create deployment directory - mkdir -p deploy/assets deploy/downloads - - # Copy landing page template - cp .github/landing-page/index.html deploy/ - - # Copy app icon - cp Bookmarkly.App/Assets/StoreLogo.png deploy/assets/app-icon.png - - # Copy the release ZIP and appinstaller to downloads with standardized names - cp package/Bookmarkly_${{ needs.bump-version.outputs.version }}.zip deploy/downloads/Bookmarkly.zip - cp package/Bookmarkly.appinstaller deploy/downloads/Bookmarkly.appinstaller - - # Calculate SHA256 hash of the ZIP file - ZIP_SHA256=$(sha256sum package/Bookmarkly_${{ needs.bump-version.outputs.version }}.zip | cut -d ' ' -f 1) - SHORT_ZIP_SHA="${ZIP_SHA256:0:16}" - - # Set URLs for downloads - ZIP_URL="./downloads/Bookmarkly.zip" - APPINSTALLER_URL="./downloads/Bookmarkly.appinstaller" - - # Generate release notes from tags using Conventional Commits - generate_release_notes() { - local RELEASE_HTML="" - local COMMIT_TYPES=("feat" "fix" "refactor" "docs" "style" "perf" "test" "build" "ci") - local COMMIT_TYPE_LABELS=("Feat" "Fix" "Refactor" "Docs" "Style" "Perf" "Test" "Build" "CI") - - # Get up to 10 most recent tags - local TAGS=$(git tag --sort=-version:refname | head -10) - - if [ -z "$TAGS" ]; then - echo '

No releases found.

' - return - fi - - local PREV_TAG="" - local TAG_COUNT=0 - - while IFS= read -r TAG; do - if [ -z "$TAG" ]; then - continue - fi - - TAG_COUNT=$((TAG_COUNT + 1)) + - name: Checkout code + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Download package artifact (ZIP) + uses: actions/download-artifact@v7 + with: + name: Bookmarkly_${{ needs.bump-version.outputs.version }} + path: ./package + + - name: Download appinstaller artifact + uses: actions/download-artifact@v7 + with: + name: Bookmarkly_appinstaller + path: ./package + + - name: Prepare landing page + run: | + # Create deployment directory + mkdir -p deploy/assets deploy/downloads + + # Copy landing page template + cp .github/landing-page/index.html deploy/ + + # Copy app icon + cp Bookmarkly.App/Assets/StoreLogo.png deploy/assets/app-icon.png + + # Copy the release ZIP and appinstaller to downloads with standardized names + cp package/Bookmarkly_${{ needs.bump-version.outputs.version }}.zip deploy/downloads/Bookmarkly.zip + cp package/Bookmarkly.appinstaller deploy/downloads/Bookmarkly.appinstaller + + # Calculate SHA256 hash of the ZIP file + ZIP_SHA256=$(sha256sum package/Bookmarkly_${{ needs.bump-version.outputs.version }}.zip | cut -d ' ' -f 1) + SHORT_ZIP_SHA="${ZIP_SHA256:0:16}" + + # Set URLs for downloads + ZIP_URL="./downloads/Bookmarkly.zip" + APPINSTALLER_URL="./downloads/Bookmarkly.appinstaller" + + # Generate release notes from tags using Conventional Commits + generate_release_notes() { + local RELEASE_HTML="" + local COMMIT_TYPES=("feat" "fix" "refactor" "docs" "style" "perf" "test" "build" "ci") + local COMMIT_TYPE_LABELS=("Feat" "Fix" "Refactor" "Docs" "Style" "Perf" "Test" "Build" "CI") - # Get the tag date - local TAG_DATE=$(git log -1 --format=%ad --date=short "$TAG" 2>/dev/null || echo "Unknown date") + # Get up to 10 most recent tags + local TAGS=$(git tag --sort=-version:refname | head -10) - # Get commits between this tag and the previous (older) one - # Tags are sorted newest first, so PREV_TAG (if set) is actually newer - local COMMITS="" - if [ -n "$PREV_TAG" ]; then - # PREV_TAG is newer, TAG is older, so get commits: TAG..PREV_TAG - COMMITS=$(git log --pretty=format:"%s" "$TAG".."$PREV_TAG" 2>/dev/null) - else - # This is the newest tag, find the next older tag to get commits for this release - local OLDER_TAG=$(git tag --sort=-version:refname | grep -A1 "^${TAG}$" | tail -1) - if [ -n "$OLDER_TAG" ] && [ "$OLDER_TAG" != "$TAG" ]; then - COMMITS=$(git log --pretty=format:"%s" "$OLDER_TAG".."$TAG" 2>/dev/null) - else - # Only one tag or no older tag, get last 20 commits for initial release - # Limit of 20 provides reasonable context without overwhelming the changelog - COMMITS=$(git log --pretty=format:"%s" -20 "$TAG" 2>/dev/null) - fi + if [ -z "$TAGS" ]; then + echo '

No releases found.

' + return fi - # Start building this release's HTML - local RELEASE_CONTENT="" - local HAS_CONVENTIONAL=false + local PREV_TAG="" + local TAG_COUNT=0 - # Group commits by type - for i in "${!COMMIT_TYPES[@]}"; do - local TYPE="${COMMIT_TYPES[$i]}" - local LABEL="${COMMIT_TYPE_LABELS[$i]}" - local TYPE_COMMITS="" + while IFS= read -r TAG; do + if [ -z "$TAG" ]; then + continue + fi + + TAG_COUNT=$((TAG_COUNT + 1)) - while IFS= read -r COMMIT; do - if [ -z "$COMMIT" ]; then - continue + # Get the tag date + local TAG_DATE=$(git log -1 --format=%ad --date=short "$TAG" 2>/dev/null || echo "Unknown date") + + # Get commits between this tag and the previous (older) one + # Tags are sorted newest first, so PREV_TAG (if set) is actually newer + local COMMITS="" + if [ -n "$PREV_TAG" ]; then + # PREV_TAG is newer, TAG is older, so get commits: TAG..PREV_TAG + COMMITS=$(git log --pretty=format:"%s" "$TAG".."$PREV_TAG" 2>/dev/null) + else + # This is the newest tag, find the next older tag to get commits for this release + local OLDER_TAG=$(git tag --sort=-version:refname | grep -A1 "^${TAG}$" | tail -1) + if [ -n "$OLDER_TAG" ] && [ "$OLDER_TAG" != "$TAG" ]; then + COMMITS=$(git log --pretty=format:"%s" "$OLDER_TAG".."$TAG" 2>/dev/null) + else + # Only one tag or no older tag, get last 20 commits for initial release + # Limit of 20 provides reasonable context without overwhelming the changelog + COMMITS=$(git log --pretty=format:"%s" -20 "$TAG" 2>/dev/null) fi + fi + + # Start building this release's HTML + local RELEASE_CONTENT="" + local HAS_CONVENTIONAL=false + + # Group commits by type + for i in "${!COMMIT_TYPES[@]}"; do + local TYPE="${COMMIT_TYPES[$i]}" + local LABEL="${COMMIT_TYPE_LABELS[$i]}" + local TYPE_COMMITS="" - # Skip chore commits - if [[ "$COMMIT" =~ ^chore(\(.+\))?:.*$ ]]; then - continue - fi + while IFS= read -r COMMIT; do + if [ -z "$COMMIT" ]; then + continue + fi + + # Skip chore commits + if [[ "$COMMIT" =~ ^chore(\(.+\))?:.*$ ]]; then + continue + fi + + # Check if commit matches this type + if [[ "$COMMIT" =~ ^${TYPE}(\(.+\))?:(.*)$ ]]; then + local MSG="${BASH_REMATCH[2]}" + MSG=$(echo "$MSG" | sed 's/^[[:space:]]*//') + TYPE_COMMITS="${TYPE_COMMITS}
  • ${MSG}
  • " + HAS_CONVENTIONAL=true + fi + done <<< "$COMMITS" - # Check if commit matches this type - if [[ "$COMMIT" =~ ^${TYPE}(\(.+\))?:(.*)$ ]]; then - local MSG="${BASH_REMATCH[2]}" - MSG=$(echo "$MSG" | sed 's/^[[:space:]]*//') - TYPE_COMMITS="${TYPE_COMMITS}
  • ${MSG}
  • " - HAS_CONVENTIONAL=true + if [ -n "$TYPE_COMMITS" ]; then + RELEASE_CONTENT="${RELEASE_CONTENT}
    ${LABEL}
      ${TYPE_COMMITS}
    " fi - done <<< "$COMMITS" + done - if [ -n "$TYPE_COMMITS" ]; then - RELEASE_CONTENT="${RELEASE_CONTENT}
    ${LABEL}
      ${TYPE_COMMITS}
    " + # If no conventional commits found, add a message + if [ "$HAS_CONVENTIONAL" = false ]; then + RELEASE_CONTENT='

    No conventional changes for this release.

    ' fi - done - - # If no conventional commits found, add a message - if [ "$HAS_CONVENTIONAL" = false ]; then - RELEASE_CONTENT='

    No conventional changes for this release.

    ' - fi - - # Build the collapsible release item - RELEASE_HTML="${RELEASE_HTML}
    Release ${TAG} – ${TAG_DATE}
    ${RELEASE_CONTENT}
    " + + # Build the collapsible release item + RELEASE_HTML="${RELEASE_HTML}
    Release ${TAG} – ${TAG_DATE}
    ${RELEASE_CONTENT}
    " + + PREV_TAG="$TAG" + done <<< "$TAGS" - PREV_TAG="$TAG" - done <<< "$TAGS" - - echo "$RELEASE_HTML" - } - - RELEASE_NOTES=$(generate_release_notes) - - # If release notes are empty, provide default - if [ -z "$RELEASE_NOTES" ]; then - RELEASE_NOTES='

    No release notes available.

    ' - fi - - # Replace placeholders in index.html - sed -i "s|{{VERSION}}|v${{ needs.bump-version.outputs.version }}|g" deploy/index.html - sed -i "s|{{ZIP_SHA}}|${SHORT_ZIP_SHA}|g" deploy/index.html - sed -i "s|{{ZIP_URL}}|${ZIP_URL}|g" deploy/index.html - sed -i "s|{{APPINSTALLER_URL}}|${APPINSTALLER_URL}|g" deploy/index.html - - # Create a temporary file with release notes and use it for replacement - echo "$RELEASE_NOTES" > /tmp/release_notes.html - # Use awk for multi-line replacement - awk -v notes="$(cat /tmp/release_notes.html)" '{gsub(/\{\{RELEASE_NOTES\}\}/, notes)}1' deploy/index.html > deploy/index.html.tmp && mv deploy/index.html.tmp deploy/index.html - - echo "Landing page prepared successfully" - echo "Files in deploy/downloads:" - ls -la deploy/downloads/ - shell: bash - - - name: Deploy to Cloudflare Pages - uses: cloudflare/wrangler-action@v3 - env: - CLOUDFLARE_PROJECT: ${{ secrets.CLOUDFLARE_PROJECT_NAME }} - DEPLOY_BRANCH: ${{ github.ref_name }} - with: - apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} - accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - command: pages deploy deploy --project-name="${{ secrets.CLOUDFLARE_PROJECT_NAME }}" --branch="${{ github.ref_name }}" \ No newline at end of file + echo "$RELEASE_HTML" + } + + RELEASE_NOTES=$(generate_release_notes) + + # If release notes are empty, provide default + if [ -z "$RELEASE_NOTES" ]; then + RELEASE_NOTES='

    No release notes available.

    ' + fi + + # Replace placeholders in index.html + sed -i "s|{{VERSION}}|v${{ needs.bump-version.outputs.version }}|g" deploy/index.html + sed -i "s|{{ZIP_SHA}}|${SHORT_ZIP_SHA}|g" deploy/index.html + sed -i "s|{{ZIP_URL}}|${ZIP_URL}|g" deploy/index.html + sed -i "s|{{APPINSTALLER_URL}}|${APPINSTALLER_URL}|g" deploy/index.html + + # Create a temporary file with release notes and use it for replacement + echo "$RELEASE_NOTES" > /tmp/release_notes.html + # Use awk for multi-line replacement + awk -v notes="$(cat /tmp/release_notes.html)" '{gsub(/\{\{RELEASE_NOTES\}\}/, notes)}1' deploy/index.html > deploy/index.html.tmp && mv deploy/index.html.tmp deploy/index.html + + echo "Landing page prepared successfully" + echo "Files in deploy/downloads:" + ls -la deploy/downloads/ + shell: bash + + - name: Deploy to Cloudflare Pages + uses: cloudflare/wrangler-action@v3 + env: + CLOUDFLARE_PROJECT: ${{ secrets.CLOUDFLARE_PROJECT_NAME }} + DEPLOY_BRANCH: ${{ github.ref_name }} + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + command: pages deploy deploy --project-name="${{ secrets.CLOUDFLARE_PROJECT_NAME }}" --branch="${{ github.ref_name }}" + + deploy-docs: + name: Deploy to GitHub Pages + runs-on: ubuntu-latest + needs: build + if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/ci') + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + + steps: + - name: Download documentation artifact + uses: actions/download-artifact@v7 + with: + name: documentation + path: _site + + - name: Setup Pages + uses: actions/configure-pages@v5 + + - name: Upload to GitHub Pages + uses: actions/upload-pages-artifact@v3 + with: + path: _site + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 From 454870d33d1fa786b40c19a1d2eb49b8c53f20b1 Mon Sep 17 00:00:00 2001 From: Kumara Krishnan <5687650+Kumara-Krishnan@users.noreply.github.com> Date: Tue, 27 Jan 2026 04:32:45 +0530 Subject: [PATCH 7/9] style: update release notes styling and default message for non-conventional commits --- .github/landing-page/index.html | 99 +++++++++--------------- .github/workflows/msix-build-release.yml | 6 +- 2 files changed, 41 insertions(+), 64 deletions(-) diff --git a/.github/landing-page/index.html b/.github/landing-page/index.html index 9b9c3be..d550de1 100644 --- a/.github/landing-page/index.html +++ b/.github/landing-page/index.html @@ -110,38 +110,7 @@ background: radial-gradient(ellipse 80% 50% at 50% -20%, rgba(120, 119, 198, 0.3), transparent); } - .device-mockup-container { - perspective: 1000px; - max-width: 1400px; - margin: 0 auto; - } - - .device-mockup { - position: relative; - animation: float 6s ease-in-out infinite; - } - - @keyframes float { - - 0%, - 100% { - transform: translateY(0); - } - - 50% { - transform: translateY(-10px); - } - } - - .glow-effect { - box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5); - } - @media (prefers-color-scheme: light) { - .glow-effect { - box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); - } - } .feature-card { background: rgba(255, 255, 255, 0.03); @@ -208,11 +177,12 @@ height: 44px; min-width: 161px; border-radius: 4px; - padding: 0 1.2rem; + padding: 0 1.5rem; font-weight: 700; display: inline-flex; align-items: center; justify-content: center; + gap: 0.5rem; box-sizing: border-box; } @@ -282,44 +252,58 @@ } .release-item { - background: rgba(255, 255, 255, 0.03); - border: 1px solid rgba(255, 255, 255, 0.08); + background: rgba(255, 255, 255, 0.02); + border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 8px; - padding: 1rem; + padding: 0; + overflow: hidden; + transition: all 0.2s ease; + } + + .release-item:hover { + background: rgba(255, 255, 255, 0.04); + border-color: rgba(255, 255, 255, 0.15); } .release-item summary { cursor: pointer; list-style: none; - display: flex; - align-items: center; - gap: 0.5rem; + padding: 1rem 1.25rem; + user-select: none; } .release-item summary::-webkit-details-marker { display: none; } - .release-item summary::before { - content: '▶'; - display: inline-block; - transition: transform 0.2s ease; - color: #6366f1; - font-size: 0.75rem; + .release-item[open] { + background: rgba(255, 255, 255, 0.05); } - .release-item[open] summary::before { - transform: rotate(90deg); + .release-item .release-content { + padding: 0 1.25rem 1.25rem 1.25rem; + color: #9ca3af; } - .screenshot-container { - position: relative; - width: 100%; - max-width: 1200px; - margin: 0 auto; - aspect-ratio: 16/10; - border-radius: 12px; - box-shadow: 0 25px 50px -20px rgba(0, 0, 0, 0.45); + @media (prefers-color-scheme: light) { + .release-item { + background: #ffffff; + border: 1px solid #e5e7eb; + } + + .release-item:hover { + background: #f9fafb; + border-color: #d1d5db; + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1); + } + + .release-item[open] { + background: #f9fafb; + } + + .release-item .release-content { + color: #6b7280; + } } .section-surface { @@ -346,13 +330,6 @@ color: #334155; } } - - @media (prefers-color-scheme: light) { - .laptop-screen { - background: #f5f5f5; - box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1); - } - } diff --git a/.github/workflows/msix-build-release.yml b/.github/workflows/msix-build-release.yml index 0a68b17..3cd2c51 100644 --- a/.github/workflows/msix-build-release.yml +++ b/.github/workflows/msix-build-release.yml @@ -349,13 +349,13 @@ jobs: fi done - # If no conventional commits found, add a message + # If no conventional commits found, add a generic message if [ "$HAS_CONVENTIONAL" = false ]; then - RELEASE_CONTENT='

    No conventional changes for this release.

    ' + RELEASE_CONTENT='

    Bug fixes and performance improvements.

    ' fi # Build the collapsible release item - RELEASE_HTML="${RELEASE_HTML}
    Release ${TAG} – ${TAG_DATE}
    ${RELEASE_CONTENT}
    " + RELEASE_HTML="${RELEASE_HTML}
    Release ${TAG} • ${TAG_DATE}
    ${RELEASE_CONTENT}
    " PREV_TAG="$TAG" done <<< "$TAGS" From 08074698ef6751ba65ce9cccb1401d903b158ab9 Mon Sep 17 00:00:00 2001 From: Kumara Krishnan <5687650+Kumara-Krishnan@users.noreply.github.com> Date: Sun, 8 Feb 2026 16:47:37 +0530 Subject: [PATCH 8/9] chore: update Cyclotron submodule to latest commit --- Cyclotron | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cyclotron b/Cyclotron index 7d131bf..682db7e 160000 --- a/Cyclotron +++ b/Cyclotron @@ -1 +1 @@ -Subproject commit 7d131bffda52956f2bccdd7d3e84c044eca96f78 +Subproject commit 682db7eb1b95e36905afa5a7d9772f12d9ae0777 From 8aacc2e09bf9164af69b0b97b49370b71fd8dc53 Mon Sep 17 00:00:00 2001 From: Kumara Krishnan <5687650+Kumara-Krishnan@users.noreply.github.com> Date: Sun, 8 Feb 2026 16:49:14 +0530 Subject: [PATCH 9/9] Refactor application structure and improve code organization - Updated .gitignore to include _manifest directory. - Refactored App.xaml.cs to streamline application initialization and improve readability. - Modified MainWindow.xaml to bind ListView to _datas instead of datas. - Refactored MainWindow.xaml.cs to enhance clarity and maintainability, including service registration. - Deleted unused Class1.cs from Bookmarkly.Entities.Abstractions. - Cleaned up interfaces in Bookmarkly.Entities.Abstractions for consistency and clarity. - Updated project references in Bookmarkly.slnx for better organization. - Added generate-sbom.ps1 script to automate SBOM generation for direct NuGet dependencies. - Enhanced Directory.Build.Props with documentation properties and additional build settings. --- .editorconfig | 466 ++++++++++-------- .gitignore | 2 + Bookmarkly.App/App.xaml.cs | 61 +-- Bookmarkly.App/MainWindow.xaml | 2 +- Bookmarkly.App/MainWindow.xaml.cs | 85 ++-- Bookmarkly.Entities.Abstractions/Class1.cs | 8 - .../IArticleContent.cs | 19 +- .../IArticleMetaData.cs | 17 +- Bookmarkly.Entities.Abstractions/IFolder.cs | 15 +- Bookmarkly.Entities.Abstractions/IUser.cs | 11 +- .../IUserArticleMetaData.cs | 19 +- Bookmarkly.code-workspace | 8 +- Bookmarkly.slnx | 19 +- Directory.Build.Props | 27 +- .../Instapaper.Entities/ArticleContent.cs | 11 +- .../Instapaper.Entities/ArticleMetaData.cs | 17 +- Instapaper/Instapaper.Entities/Folder.cs | 15 +- Instapaper/Instapaper.Entities/User.cs | 13 +- .../UserArticleMetaData.cs | 19 +- Tests/ArchitectureTests/Test1.cs | 15 +- build/generate-sbom.ps1 | 115 +++++ 21 files changed, 530 insertions(+), 434 deletions(-) delete mode 100644 Bookmarkly.Entities.Abstractions/Class1.cs create mode 100644 build/generate-sbom.ps1 diff --git a/.editorconfig b/.editorconfig index a4717e7..88c6e7e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,219 +1,247 @@ -# Remove the line below if you want to inherit .editorconfig settings from higher directories -root = true - -# Generated code -[*{_AssemblyInfo.cs,.g.cs}] -generated_code = true - -# All files -[*] - -#### Core EditorConfig Options #### - -# Encoding -charset = utf-8 - -# Indentation and spacing -tab_width = 4 -indent_size = 4 -indent_style = space - -# New line preferences -end_of_line = crlf -trim_trailing_whitespace = true -insert_final_newline = true - -#### .NET Coding Conventions #### - -# Organize usings -dotnet_sort_system_directives_first = true -dotnet_separate_import_directive_groups = false - -# this. preferences -dotnet_style_qualification_for_field = false:silent -dotnet_style_qualification_for_property = false:silent -dotnet_style_qualification_for_method = false:silent -dotnet_style_qualification_for_event = false:silent - -# Language keywords vs BCL types preferences -dotnet_style_predefined_type_for_locals_parameters_members = true:silent -dotnet_style_predefined_type_for_member_access = true:silent - -# Parentheses preferences -dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent -dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent -dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent -dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent - -# Modifier preferences -dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent -dotnet_style_readonly_field = true:suggestion - -# Expression-level preferences -dotnet_style_object_initializer = true:suggestion -dotnet_style_collection_initializer = true:suggestion -dotnet_style_explicit_tuple_names = true:suggestion -dotnet_style_null_propagation = true:suggestion -dotnet_style_coalesce_expression = true:suggestion -dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion -dotnet_style_prefer_inferred_tuple_names = true:suggestion -dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion -dotnet_style_prefer_auto_properties = true:silent -dotnet_style_prefer_conditional_expression_over_assignment = true:silent -dotnet_style_prefer_conditional_expression_over_return = true:silent -dotnet_style_prefer_simplified_boolean_expressions = true:suggestion -dotnet_style_prefer_compound_assignment = true:suggestion -dotnet_style_prefer_simplified_interpolation = true:suggestion -dotnet_style_namespace_match_folder = true:suggestion -dotnet_style_operator_placement_when_wrapping = beginning_of_line -dotnet_style_allow_multiple_blank_lines_experimental = true:silent -dotnet_style_allow_statement_immediately_after_block_experimental = true:silent -dotnet_code_quality_unused_parameters = all:suggestion - -#### C# Coding Conventions #### - -[*.cs] - -# var preferences -csharp_style_var_for_built_in_types = true:silent -csharp_style_var_when_type_is_apparent = true:silent -csharp_style_var_elsewhere = true:silent - -# Expression-bodied members -csharp_style_expression_bodied_methods = false:silent -csharp_style_expression_bodied_constructors = false:silent -csharp_style_expression_bodied_operators = false:silent -csharp_style_expression_bodied_properties = true:silent -csharp_style_expression_bodied_indexers = true:silent -csharp_style_expression_bodied_accessors = true:silent -csharp_style_expression_bodied_lambdas = true:silent -csharp_style_expression_bodied_local_functions = false:silent - -# Pattern matching preferences -csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion -csharp_style_pattern_matching_over_as_with_null_check = true:suggestion -csharp_style_prefer_switch_expression = true:suggestion -csharp_style_prefer_pattern_matching = true:silent -csharp_style_prefer_not_pattern = true:suggestion - -# Null-checking preferences -csharp_style_throw_expression = true:suggestion -csharp_style_prefer_null_check_over_type_check = true:suggestion -csharp_style_conditional_delegate_call = true:suggestion - -# Modifier preferences -csharp_prefer_static_local_function = true:suggestion -csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent - -# Code-block preferences -csharp_prefer_braces = true:silent -csharp_prefer_simple_using_statement = true:suggestion - -# Expression-level preferences -csharp_prefer_simple_default_expression = true:suggestion -csharp_style_pattern_local_over_anonymous_function = true:suggestion -csharp_style_inlined_variable_declaration = true:suggestion -csharp_style_deconstructed_variable_declaration = true:suggestion -csharp_style_prefer_index_operator = true:suggestion -csharp_style_prefer_range_operator = true:suggestion -csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion - -# 'using' directive preferences -csharp_using_directive_placement = outside_namespace:silent - -#### C# Formatting Rules #### - -# New line preferences -csharp_new_line_before_open_brace = all -csharp_new_line_before_else = true -csharp_new_line_before_catch = true -csharp_new_line_before_finally = true -csharp_new_line_before_members_in_object_initializers = true -csharp_new_line_before_members_in_anonymous_types = true -csharp_new_line_between_query_expression_clauses = true - -# Indentation preferences -csharp_indent_case_contents = true -csharp_indent_switch_labels = true -csharp_indent_labels = flush_left -csharp_indent_block_contents = true -csharp_indent_braces = false -csharp_indent_case_contents_when_block = false - -# Space preferences -csharp_space_after_cast = false -csharp_space_after_keywords_in_control_flow_statements = true -csharp_space_between_parentheses = false -csharp_space_before_colon_in_inheritance_clause = true -csharp_space_after_colon_in_inheritance_clause = true -csharp_space_around_binary_operators = before_and_after -csharp_space_between_method_declaration_parameter_list_parentheses = false -csharp_space_between_method_declaration_empty_parameter_list_parentheses = false -csharp_space_between_method_declaration_name_and_open_parenthesis = false -csharp_space_between_method_call_parameter_list_parentheses = false -csharp_space_between_method_call_empty_parameter_list_parentheses = false -csharp_space_between_method_call_name_and_opening_parenthesis = false -csharp_space_after_comma = true -csharp_space_after_dot = false -csharp_space_after_semicolon_in_for_statement = true -csharp_space_before_semicolon_in_for_statement = false -csharp_space_around_declaration_statements = false -csharp_space_before_open_square_brackets = false -csharp_space_between_empty_square_brackets = false -csharp_space_between_square_brackets = false - -# Wrapping preferences -csharp_preserve_single_line_statements = false -csharp_preserve_single_line_blocks = true - -#### Naming Conventions #### - -# Naming rules - -dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion -dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface -dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i - -dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.types_should_be_pascal_case.symbols = types -dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case - -dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members -dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case - -# Symbol specifications - -dotnet_naming_symbols.interface.applicable_kinds = interface -dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.interface.required_modifiers = - -dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum -dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.types.required_modifiers = - -dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method -dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.non_field_members.required_modifiers = - -# Naming styles - -dotnet_naming_style.begins_with_i.required_prefix = I -dotnet_naming_style.begins_with_i.required_suffix = -dotnet_naming_style.begins_with_i.word_separator = -dotnet_naming_style.begins_with_i.capitalization = pascal_case - -dotnet_naming_style.pascal_case.required_prefix = -dotnet_naming_style.pascal_case.required_suffix = -dotnet_naming_style.pascal_case.word_separator = -dotnet_naming_style.pascal_case.capitalization = pascal_case - -dotnet_naming_style.pascal_case.required_prefix = -dotnet_naming_style.pascal_case.required_suffix = -dotnet_naming_style.pascal_case.word_separator = -dotnet_naming_style.pascal_case.capitalization = pascal_case - -# XAML files -[*.xaml] -indent_size = 4 -tab_width = 4 +# Rules in this file were initially inferred by Visual Studio IntelliCode from the Template Studio codebase. +# You can modify the rules from these initially generated values to suit your own policies. +# You can learn more about editorconfig here: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference. + +# top-most EditorConfig file for this level +root = true + +[*] +trim_trailing_whitespace = true + +[*.cs] + +# file_header_template = Copyright (c) Microsoft Corporation.\r\nLicensed under the MIT License. + +# Core editorconfig formatting - indentation + +# Use soft tabs (spaces) for indentation +indent_style = space + +# Formatting - new line options + +# Place else statements on a new line +csharp_new_line_before_else = true +# Require braces to be on a new line for lambdas, methods, control_blocks, types, properties, and accessors (also known as "Allman" style) +csharp_new_line_before_open_brace = all + +# Formatting - organize using options + +# Sort System.* using directives alphabetically, and place them before other usings +dotnet_sort_system_directives_first = true +# Remove unnecessary usings +dotnet_diagnostic.IDE0005.severity = warning + +# Formatting - spacing options + +# Require NO space between a cast and the value +csharp_space_after_cast = false +# Require a space before the colon for bases or interfaces in a type declaration +csharp_space_after_colon_in_inheritance_clause = true +# Require a space after a keyword in a control flow statement such as a for loop +csharp_space_after_keywords_in_control_flow_statements = true +# Require a space before the colon for bases or interfaces in a type declaration +csharp_space_before_colon_in_inheritance_clause = true +# Remove space within empty argument list parentheses +csharp_space_between_method_call_empty_parameter_list_parentheses = false +# Remove space between method call name and opening parenthesis +csharp_space_between_method_call_name_and_opening_parenthesis = false +# Do not place space characters after the opening parenthesis and before the closing parenthesis of a method call +csharp_space_between_method_call_parameter_list_parentheses = false +# Remove space within empty parameter list parentheses for a method declaration +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +# Place a space character after the opening parenthesis and before the closing parenthesis of a method declaration parameter list. +csharp_space_between_method_declaration_parameter_list_parentheses = false + +# Formatting - wrapping options + +# Leave code block on separate lines +csharp_preserve_single_line_blocks = true + +# Style - Code block preferences + +# Prefer curly braces even for one line of code +csharp_prefer_braces = true:suggestion + +# Style - expression bodied member options + +# Prefer expression bodies for accessors +csharp_style_expression_bodied_accessors = true:warning +# Prefer block bodies for constructors +csharp_style_expression_bodied_constructors = false:suggestion +# Prefer expression bodies for methods +csharp_style_expression_bodied_methods = when_on_single_line:silent +# Prefer expression-bodied members for properties +csharp_style_expression_bodied_properties = true:warning + +# Style - expression level options + +# Prefer out variables to be declared before the method call +csharp_style_inlined_variable_declaration = false:suggestion +# Prefer the language keyword for member access expressions, instead of the type name, for types that have a keyword to represent them +dotnet_style_predefined_type_for_member_access = true:suggestion + +# Style - Expression-level preferences + +# Prefer default over default(T) +csharp_prefer_simple_default_expression = true:suggestion +# Prefer objects to be initialized using object initializers when possible +dotnet_style_object_initializer = true:suggestion + +# Style - implicit and explicit types + +# Prefer var over explicit type in all cases, unless overridden by another code style rule +csharp_style_var_elsewhere = true:suggestion +# Prefer var is used to declare variables with built-in system types such as int +csharp_style_var_for_built_in_types = true:suggestion +# Prefer var when the type is already mentioned on the right-hand side of a declaration expression +csharp_style_var_when_type_is_apparent = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:warning + +# Style - language keyword and framework type options + +# Prefer the language keyword for local variables, method parameters, and class members, instead of the type name, for types that have a keyword to represent them +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion + +# Style - modifier options + +# Prefer accessibility modifiers to be declared except for public interface members. This will currently not differ from always and will act as future proofing for if C# adds default interface methods. +dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion + +# Style - Modifier preferences + +# When this rule is set to a list of modifiers, prefer the specified ordering. +csharp_preferred_modifier_order = public,private,protected,internal,static,async,readonly,override,sealed,abstract,virtual:warning +dotnet_style_readonly_field = true:warning + +# Style - Pattern matching + +# Prefer pattern matching instead of is expression with type casts +csharp_style_pattern_matching_over_as_with_null_check = true:warning + +# Style - qualification options + +# Prefer events not to be prefaced with this. or Me. in Visual Basic +dotnet_style_qualification_for_event = false:suggestion +# Prefer fields not to be prefaced with this. or Me. in Visual Basic +dotnet_style_qualification_for_field = false:suggestion +# Prefer methods not to be prefaced with this. or Me. in Visual Basic +dotnet_style_qualification_for_method = false:suggestion +# Prefer properties not to be prefaced with this. or Me. in Visual Basic +dotnet_style_qualification_for_property = false:suggestion +csharp_indent_labels = one_less_than_current +csharp_using_directive_placement = outside_namespace:silent +csharp_prefer_simple_using_statement = true:warning +csharp_style_namespace_declarations = file_scoped:warning +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_prefer_primary_constructors = true:silent + +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +indent_size = 4 +end_of_line = crlf +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent + +# Style - Unnecessary code rules +csharp_style_unused_value_assignment_preference = discard_variable:warning + +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_compound_assignment = true:warning +dotnet_style_prefer_simplified_interpolation = true:suggestion + +# Define what we will treat as private fields. +dotnet_naming_symbols.private_fields.applicable_kinds = field +dotnet_naming_symbols.private_fields.applicable_accessibilities = private + +dotnet_naming_symbols.const_private_fields.applicable_kinds = field +dotnet_naming_symbols.const_private_fields.applicable_accessibilities = private +dotnet_naming_symbols.const_private_fields.required_modifiers = const + +# Define rule that something must begin with an underscore and be in camel case. +dotnet_naming_style.require_underscore_prefix_and_camel_case.required_prefix = _ +dotnet_naming_style.require_underscore_prefix_and_camel_case.capitalization = camel_case + +# Define rule that something must not begin with an underscore and be in pascal case +dotnet_naming_style.require_no_prefix_and_pascal_case.required_prefix = +dotnet_naming_style.require_no_prefix_and_pascal_case.capitalization = pascal_case + +# Appy our rule to private fields. +dotnet_naming_rule.private_fields_must_begin_with_underscore_and_be_in_camel_case.symbols = private_fields +dotnet_naming_rule.private_fields_must_begin_with_underscore_and_be_in_camel_case.style = require_underscore_prefix_and_camel_case +dotnet_naming_rule.private_fields_must_begin_with_underscore_and_be_in_camel_case.severity = warning + +dotnet_naming_rule.const_fields_must_begin_with_no_prefix_and_be_in_pascal_case.symbols = const_private_fields +dotnet_naming_rule.const_fields_must_begin_with_no_prefix_and_be_in_pascal_case.style = require_no_prefix_and_pascal_case +dotnet_naming_rule.const_fields_must_begin_with_no_prefix_and_be_in_pascal_case.severity = warning + +# Spelling + +spelling_exclusion_path = .\exclusion.dic + +# Diagnostic configuration + +# CS8305: Type is for evaluation purposes only and is subject to change or removal in future updates. +dotnet_diagnostic.CS8305.severity = suggestion + +# Suppress IDE0079 (Unnecessary suppression) diagnostics +dotnet_diagnostic.IDE0079.severity = none + +# Suppress IDE0130 (Namespace does not match folder structure) diagnostics for ServiceCollectionExtensions.cs, as it is a common pattern to have extension methods in a different namespace than the folder structure. +[ServiceCollectionExtensions.cs] +dotnet_diagnostic.IDE0130.severity = none diff --git a/.gitignore b/.gitignore index 498eee6..a23450a 100644 --- a/.gitignore +++ b/.gitignore @@ -423,3 +423,5 @@ api/ .manifest .playwright-mcp/ + +_manifest/ diff --git a/Bookmarkly.App/App.xaml.cs b/Bookmarkly.App/App.xaml.cs index 7329918..be8266f 100644 --- a/Bookmarkly.App/App.xaml.cs +++ b/Bookmarkly.App/App.xaml.cs @@ -1,50 +1,33 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Controls.Primitives; -using Microsoft.UI.Xaml.Data; -using Microsoft.UI.Xaml.Input; -using Microsoft.UI.Xaml.Media; -using Microsoft.UI.Xaml.Navigation; -using Microsoft.UI.Xaml.Shapes; -using Windows.ApplicationModel; -using Windows.ApplicationModel.Activation; -using Windows.Foundation; -using Windows.Foundation.Collections; +using Microsoft.UI.Xaml; // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. -namespace Bookmarkly.App +namespace Bookmarkly.App; + +/// +/// Provides application-specific behavior to supplement the default Application class. +/// +public partial class App : Application { + private Window? _window; + /// - /// Provides application-specific behavior to supplement the default Application class. + /// Initializes the singleton application object. This is the first line of authored code + /// executed, and as such is the logical equivalent of main() or WinMain(). /// - public partial class App : Application + public App() { - private Window? _window; - - /// - /// Initializes the singleton application object. This is the first line of authored code - /// executed, and as such is the logical equivalent of main() or WinMain(). - /// - public App() - { - InitializeComponent(); - } + InitializeComponent(); + } - /// - /// Invoked when the application is launched. - /// - /// Details about the launch request and process. - protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) - { - _window = new MainWindow(); - _window.Activate(); - } + /// + /// Invoked when the application is launched. + /// + /// Details about the launch request and process. + protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) + { + _window = new MainWindow(); + _window.Activate(); } } diff --git a/Bookmarkly.App/MainWindow.xaml b/Bookmarkly.App/MainWindow.xaml index 7fddeea..569bc75 100644 --- a/Bookmarkly.App/MainWindow.xaml +++ b/Bookmarkly.App/MainWindow.xaml @@ -14,6 +14,6 @@ - + diff --git a/Bookmarkly.App/MainWindow.xaml.cs b/Bookmarkly.App/MainWindow.xaml.cs index d8b03c1..d479ac9 100644 --- a/Bookmarkly.App/MainWindow.xaml.cs +++ b/Bookmarkly.App/MainWindow.xaml.cs @@ -1,70 +1,55 @@ -using System; -using System.Collections.Generic; using System.Collections.ObjectModel; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; using Cyclotron.Telemetry.DependencyInjection; using Cyclotron.Telemetry.Logging; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Controls.Primitives; -using Microsoft.UI.Xaml.Data; -using Microsoft.UI.Xaml.Input; -using Microsoft.UI.Xaml.Media; -using Microsoft.UI.Xaml.Navigation; -using Windows.Foundation; -using Windows.Foundation.Collections; -using Windows.Storage; // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. -namespace Bookmarkly.App +namespace Bookmarkly.App; + +/// +/// An empty window that can be used on its own or navigated to within a Frame. +/// +public sealed partial class MainWindow : Window { - /// - /// An empty window that can be used on its own or navigated to within a Frame. - /// - public sealed partial class MainWindow : Window + private readonly ObservableCollection _datas = new(); + public MainWindow() { - ObservableCollection datas = new(); - public MainWindow() - { - datas.Add(1); - datas.Add(1); - datas.Add(1); - datas.Add(1); - datas.Add(1); - datas.Add(1); - datas.Add(1); - datas.Add(1); - datas.Add(1); - InitializeComponent(); - } + _datas.Add(1); + _datas.Add(1); + _datas.Add(1); + _datas.Add(1); + _datas.Add(1); + _datas.Add(1); + _datas.Add(1); + _datas.Add(1); + _datas.Add(1); + InitializeComponent(); + } - private void OnWindowLoaded(object sender, RoutedEventArgs e) - { - var services = new ServiceCollection(); + private void OnWindowLoaded(object sender, RoutedEventArgs e) + { + var services = new ServiceCollection(); - services.AddCyclotronTelemetry(options => - { - options.ServiceName = "Bookmarkly"; - options.ServiceVersion = "1.0.0"; - options.DefaultModule = "main"; + services.AddCyclotronTelemetry(options => + { + options.ServiceName = "Bookmarkly"; + options.ServiceVersion = "1.0.0"; + options.DefaultModule = "main"; - options.Logging.MinimumLevel = LogLevel.Debug; - options.Logging.File.Path = "{LocalAppData}/Bookmarkly/logs/app-.log"; - options.Logging.File.RetainedFileCountLimit = 3; // Already set as default - options.Logging.File.RollingInterval = Serilog.RollingInterval.Day; - }); + options.Logging.MinimumLevel = LogLevel.Debug; + options.Logging.File.Path = "{LocalAppData}/Bookmarkly/logs/app-.log"; + options.Logging.File.RetainedFileCountLimit = 3; // Already set as default + options.Logging.File.RollingInterval = Serilog.RollingInterval.Day; + }); - var provider = services.BuildServiceProvider(); - var logger = provider.GetRequiredService().ForModule("Instapaper"); + var provider = services.BuildServiceProvider(); + var logger = provider.GetRequiredService().ForModule("Instapaper"); - logger.LogInformation("Starting operation"); // Caller info auto-captured! - } + logger.LogInformation("Starting operation"); // Caller info auto-captured! } } diff --git a/Bookmarkly.Entities.Abstractions/Class1.cs b/Bookmarkly.Entities.Abstractions/Class1.cs deleted file mode 100644 index 7959d38..0000000 --- a/Bookmarkly.Entities.Abstractions/Class1.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System; - -namespace Bookmarkly.Entities.Abstractions; - -public class Class1 -{ - -} diff --git a/Bookmarkly.Entities.Abstractions/IArticleContent.cs b/Bookmarkly.Entities.Abstractions/IArticleContent.cs index 419e4b7..40adf9e 100644 --- a/Bookmarkly.Entities.Abstractions/IArticleContent.cs +++ b/Bookmarkly.Entities.Abstractions/IArticleContent.cs @@ -1,14 +1,13 @@ -namespace Bookmarkly.Entities.Abstractions +namespace Bookmarkly.Entities.Abstractions; + +public interface IArticleContent { - public interface IArticleContent - { - string ArticleId { get; set; } + string ArticleId { get; set; } - string Content { get; set; } - } + string Content { get; set; } +} - public interface IUserArticleContent : IArticleContent - { - string UserId { get; set; } - } +public interface IUserArticleContent : IArticleContent +{ + string UserId { get; set; } } diff --git a/Bookmarkly.Entities.Abstractions/IArticleMetaData.cs b/Bookmarkly.Entities.Abstractions/IArticleMetaData.cs index 67ba9d5..ec1bd15 100644 --- a/Bookmarkly.Entities.Abstractions/IArticleMetaData.cs +++ b/Bookmarkly.Entities.Abstractions/IArticleMetaData.cs @@ -1,15 +1,14 @@ -namespace Bookmarkly.Entities.Abstractions +namespace Bookmarkly.Entities.Abstractions; + +public interface IArticleMetaData { - public interface IArticleMetaData - { - string Id { get; set; } + string Id { get; set; } - string Title { get; set; } + string Title { get; set; } - string Url { get; set; } + string Url { get; set; } - string Summary { get; set; } + string Summary { get; set; } - string ThumbnailUrl { get; set; } - } + string ThumbnailUrl { get; set; } } diff --git a/Bookmarkly.Entities.Abstractions/IFolder.cs b/Bookmarkly.Entities.Abstractions/IFolder.cs index c70ff80..34e580f 100644 --- a/Bookmarkly.Entities.Abstractions/IFolder.cs +++ b/Bookmarkly.Entities.Abstractions/IFolder.cs @@ -1,13 +1,12 @@ -namespace Bookmarkly.Entities.Abstractions +namespace Bookmarkly.Entities.Abstractions; + +public interface IFolder { - public interface IFolder - { - string Id { get; set; } + string Id { get; set; } - string UserId { get; set; } + string UserId { get; set; } - string Name { get; set; } + string Name { get; set; } - string ParentFolderId { get; set; } - } + string ParentFolderId { get; set; } } diff --git a/Bookmarkly.Entities.Abstractions/IUser.cs b/Bookmarkly.Entities.Abstractions/IUser.cs index 7c070cd..a32f45a 100644 --- a/Bookmarkly.Entities.Abstractions/IUser.cs +++ b/Bookmarkly.Entities.Abstractions/IUser.cs @@ -1,9 +1,8 @@ -namespace Bookmarkly.Entities.Abstractions +namespace Bookmarkly.Entities.Abstractions; + +public interface IUser { - public interface IUser - { - string Id { get; set; } + string Id { get; set; } - string DisplayName { get; set; } - } + string DisplayName { get; set; } } diff --git a/Bookmarkly.Entities.Abstractions/IUserArticleMetaData.cs b/Bookmarkly.Entities.Abstractions/IUserArticleMetaData.cs index 617ae58..c4aa1f8 100644 --- a/Bookmarkly.Entities.Abstractions/IUserArticleMetaData.cs +++ b/Bookmarkly.Entities.Abstractions/IUserArticleMetaData.cs @@ -1,17 +1,16 @@ -namespace Bookmarkly.Entities.Abstractions +namespace Bookmarkly.Entities.Abstractions; + +public interface IUserArticleMetaData : IArticleMetaData { - public interface IUserArticleMetaData : IArticleMetaData - { - string UserId { get; set; } + string UserId { get; set; } - string FolderId { get; set; } + string FolderId { get; set; } - DateTimeOffset CreatedAt { get; set; } + DateTimeOffset CreatedAt { get; set; } - DateTimeOffset UpdatedAt { get; set; } + DateTimeOffset UpdatedAt { get; set; } - bool IsRead { get; set; } + bool IsRead { get; set; } - bool IsFavorite { get; set; } - } + bool IsFavorite { get; set; } } diff --git a/Bookmarkly.code-workspace b/Bookmarkly.code-workspace index 876a149..a0676a3 100644 --- a/Bookmarkly.code-workspace +++ b/Bookmarkly.code-workspace @@ -4,5 +4,11 @@ "path": "." } ], - "settings": {} + "settings": { + "github-actions.workflows.pinned.workflows": [ + ".github/workflows/msix-build-release.yml", + ".github/workflows/test.yml", + ".github/workflows/docfx.yml" + ] + } } \ No newline at end of file diff --git a/Bookmarkly.slnx b/Bookmarkly.slnx index 9694faf..57fbd53 100644 --- a/Bookmarkly.slnx +++ b/Bookmarkly.slnx @@ -6,10 +6,8 @@ - - + + @@ -17,18 +15,16 @@ - - + + + - + @@ -36,7 +32,6 @@ - + \ No newline at end of file diff --git a/Directory.Build.Props b/Directory.Build.Props index 78d953e..2b62b9d 100644 --- a/Directory.Build.Props +++ b/Directory.Build.Props @@ -1,19 +1,20 @@ - - enable + true enable - true + enable + latest + true + true + true + true + true - - - 10.0.19041.0 - win-x86;win-x64;win-arm64 - - - 10.0.19041.0 - win-x86;win-x64;win-arm64 + + + true + $(NoWarn);CS1591 + bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml - - + \ No newline at end of file diff --git a/Instapaper/Instapaper.Entities/ArticleContent.cs b/Instapaper/Instapaper.Entities/ArticleContent.cs index 1b5f5d7..ad9840c 100644 --- a/Instapaper/Instapaper.Entities/ArticleContent.cs +++ b/Instapaper/Instapaper.Entities/ArticleContent.cs @@ -1,9 +1,8 @@ -namespace Instapaper.Entities +namespace Instapaper.Entities; + +public class ArticleContent : IArticleContent { - public class ArticleContent : IArticleContent - { - public string ArticleId { get; set; } = default!; + public string ArticleId { get; set; } = default!; - public string Content { get; set; } = default!; - } + public string Content { get; set; } = default!; } diff --git a/Instapaper/Instapaper.Entities/ArticleMetaData.cs b/Instapaper/Instapaper.Entities/ArticleMetaData.cs index 7e1d8aa..0ddf55c 100644 --- a/Instapaper/Instapaper.Entities/ArticleMetaData.cs +++ b/Instapaper/Instapaper.Entities/ArticleMetaData.cs @@ -1,15 +1,14 @@ -namespace Instapaper.Entities +namespace Instapaper.Entities; + +public class ArticleMetaData : IArticleMetaData { - public class ArticleMetaData : IArticleMetaData - { - public string Id { get; set; } = default!; + public string Id { get; set; } = default!; - public string Title { get; set; } = default!; + public string Title { get; set; } = default!; - public string Url { get; set; } = default!; + public string Url { get; set; } = default!; - public string Summary { get; set; } = default!; + public string Summary { get; set; } = default!; - public string ThumbnailUrl { get; set; } = default!; - } + public string ThumbnailUrl { get; set; } = default!; } diff --git a/Instapaper/Instapaper.Entities/Folder.cs b/Instapaper/Instapaper.Entities/Folder.cs index 2555dc9..47fa716 100644 --- a/Instapaper/Instapaper.Entities/Folder.cs +++ b/Instapaper/Instapaper.Entities/Folder.cs @@ -1,10 +1,9 @@ -namespace Instapaper.Entities +namespace Instapaper.Entities; + +public class Folder : IFolder { - public class Folder : IFolder - { - public string Id { get; set; } = default!; - public string UserId { get; set; } = default!; - public string Name { get; set; } = default!; - public string ParentFolderId { get; set; } = default!; - } + public string Id { get; set; } = default!; + public string UserId { get; set; } = default!; + public string Name { get; set; } = default!; + public string ParentFolderId { get; set; } = default!; } diff --git a/Instapaper/Instapaper.Entities/User.cs b/Instapaper/Instapaper.Entities/User.cs index 300c91f..9220391 100644 --- a/Instapaper/Instapaper.Entities/User.cs +++ b/Instapaper/Instapaper.Entities/User.cs @@ -1,11 +1,10 @@ -namespace Instapaper.Entities +namespace Instapaper.Entities; + +public class User : IUser { - public class User : IUser - { - public string Id { get; set; } = default!; + public string Id { get; set; } = default!; - public string Username { get; set; } = default!; + public string Username { get; set; } = default!; - public string DisplayName { get; set; } = default!; - } + public string DisplayName { get; set; } = default!; } diff --git a/Instapaper/Instapaper.Entities/UserArticleMetaData.cs b/Instapaper/Instapaper.Entities/UserArticleMetaData.cs index 9ebabc8..9c0cbcd 100644 --- a/Instapaper/Instapaper.Entities/UserArticleMetaData.cs +++ b/Instapaper/Instapaper.Entities/UserArticleMetaData.cs @@ -1,17 +1,16 @@ -namespace Instapaper.Entities +namespace Instapaper.Entities; + +public class UserArticleMetaData : ArticleMetaData, IUserArticleMetaData { - public class UserArticleMetaData : ArticleMetaData, IUserArticleMetaData - { - public string UserId { get; set; } = default!; + public string UserId { get; set; } = default!; - public string FolderId { get; set; } = default!; + public string FolderId { get; set; } = default!; - public DateTimeOffset CreatedAt { get; set; } + public DateTimeOffset CreatedAt { get; set; } - public DateTimeOffset UpdatedAt { get; set; } + public DateTimeOffset UpdatedAt { get; set; } - public bool IsRead { get; set; } = true; + public bool IsRead { get; set; } = true; - public bool IsFavorite { get; set; } = false; - } + public bool IsFavorite { get; set; } = false; } diff --git a/Tests/ArchitectureTests/Test1.cs b/Tests/ArchitectureTests/Test1.cs index 5e2f0af..e422772 100644 --- a/Tests/ArchitectureTests/Test1.cs +++ b/Tests/ArchitectureTests/Test1.cs @@ -1,12 +1,11 @@ -namespace ArchitectureTests +namespace ArchitectureTests; + +[TestClass] +public sealed class Test1 { - [TestClass] - public sealed class Test1 + [TestMethod] + public void TestMethod1() { - [TestMethod] - public void TestMethod1() - { - Assert.IsTrue(true, "This is a dummy test to verify testing and report generation works."); - } + Assert.IsTrue(true, "This is a dummy test to verify testing and report generation works."); } } diff --git a/build/generate-sbom.ps1 b/build/generate-sbom.ps1 new file mode 100644 index 0000000..93aaa1e --- /dev/null +++ b/build/generate-sbom.ps1 @@ -0,0 +1,115 @@ +# Generate SBOM for Bookmarkly - Direct NuGet Dependencies Only +# This script generates an SBOM containing only direct NuGet package dependencies + +param( + [string]$OutputPath = ".", + [switch]$IncludeTransitive +) + +# Read version from version.json +$versionFile = Join-Path $PSScriptRoot "version.json" +$version = Get-Content $versionFile | ConvertFrom-Json +$versionString = "$($version.major).$($version.build).$($version.revision)" + +Write-Host "Generating SBOM for Bookmarkly v$versionString" -ForegroundColor Cyan + +# Create temp directory for empty BuildDropPath +$tempDir = Join-Path $PSScriptRoot "..\temp_sbom" +New-Item -ItemType Directory -Path $tempDir -Force | Out-Null + +# Clean up any existing manifest +$manifestPath = Join-Path $OutputPath "_manifest" +if (Test-Path $manifestPath) { + Remove-Item -Path $manifestPath -Recurse -Force +} + +# Find sbom.exe +$sbomTool = "$env:LOCALAPPDATA\Microsoft\WinGet\Packages\Microsoft.SBOMTool_Microsoft.Winget.Source_8wekyb3d8bbwe\sbom.exe" +if (-not (Test-Path $sbomTool)) { + Write-Error "SBOM tool not found. Please install it using: winget install Microsoft.SBOMTool" + exit 1 +} + +# Generate SBOM +Write-Host "Running SBOM generation..." -ForegroundColor Yellow +$componentPath = Split-Path $PSScriptRoot -Parent + +& $sbomTool generate ` + -BuildDropPath $tempDir ` + -BuildComponentPath $componentPath ` + -PackageName "Bookmarkly" ` + -PackageVersion $versionString ` + -PackageSupplier "ScribblesByKK" ` + -NamespaceUriBase "https://github.com/ScribblesByKK/Bookmarkly" ` + -AdditionalComponentDetectorArgs "--DetectorCategories NuGet" ` + -ManifestDirPath $OutputPath ` + -DeleteManifestDirIfPresent true ` + -Verbosity Information + +# Clean up temp directory +Remove-Item -Path $tempDir -Recurse -Force + +if (-not $IncludeTransitive) { + Write-Host "Filtering to direct dependencies only..." -ForegroundColor Yellow + + # Read the generated SBOM + $sbomFile = Join-Path $OutputPath "_manifest\spdx_2.2\manifest.spdx.json" + $sbom = Get-Content $sbomFile | ConvertFrom-Json + + # Get all direct dependencies from project files + $projectFiles = Get-ChildItem -Path $componentPath -Recurse -Filter "*.csproj" + $directPackages = @{} + + foreach ($projectFile in $projectFiles) { + [xml]$project = Get-Content $projectFile.FullName + $packageRefs = $project.Project.ItemGroup.PackageReference + foreach ($ref in $packageRefs) { + $pkgName = $ref.Include + if ($pkgName) { + $directPackages[$pkgName] = $true + } + } + } + + # Also include packages from Directory.Packages.Props + $dirPackagesFile = Join-Path $componentPath "Directory.Packages.Props" + if (Test-Path $dirPackagesFile) { + [xml]$dirPackages = Get-Content $dirPackagesFile + $packageVersions = $dirPackages.Project.ItemGroup.PackageVersion + foreach ($pkg in $packageVersions) { + $pkgName = $pkg.Include + if ($pkgName) { + $directPackages[$pkgName] = $true + } + } + } + + Write-Host "Found $($directPackages.Count) direct package references" -ForegroundColor Green + + # Filter packages to only include direct dependencies + $filteredPackages = $sbom.packages | Where-Object { + $directPackages.ContainsKey($_.name) + } + + Write-Host "Filtered from $($sbom.packages.Count) to $($filteredPackages.Count) packages" -ForegroundColor Green + + # Update the SBOM + $sbom.packages = $filteredPackages + + # Save the filtered SBOM + $sbom | ConvertTo-Json -Depth 100 | Set-Content $sbomFile -Encoding UTF8 + + # Regenerate the SHA256 hash + $hash = Get-FileHash -Path $sbomFile -Algorithm SHA256 + $hash.Hash | Set-Content "$sbomFile.sha256" -Encoding ASCII +} + +Write-Host "`nSBOM generated successfully!" -ForegroundColor Green +Write-Host "Location: $manifestPath" -ForegroundColor Cyan +Write-Host "Version: $versionString" -ForegroundColor Cyan + +# Display summary +$sbomFile = Join-Path $OutputPath "_manifest\spdx_2.2\manifest.spdx.json" +$finalSbom = Get-Content $sbomFile | ConvertFrom-Json +Write-Host "`nPackages in SBOM: $($finalSbom.packages.Count)" -ForegroundColor Cyan +Write-Host "Files in SBOM: $($finalSbom.files.Count)" -ForegroundColor Cyan