Conversation
…unityToolkit.Mvvm
…extField, Button)
- Add ConventionNames internal helper that strips known widget suffixes (Field, Label, Button, Text, View, Box, List) from a widget Id to derive the ViewModel property name, and appends "Command" for ICommand members - Add ViewBinder.AutoBind partial that iterates View.SubViews and convention-binds each named subview to the matching VM member: Button → ICommand property via Command(), TextField → string property two-way (OneWay + ValueChanged write-back), Label → string property one-way - Add ConventionNamesTests (5 cases: 4 MemberName theory + 1 CommandName)
- Add TuiApplicationHost: boots Terminal.Gui via Application.Init/Run(shell)/Shutdown; creates a borderless full-screen Window shell before NavigateToAsync so the first screen is added to a non-null container (fixes the null Application.TopRunnableView race under Terminal.Gui 2.4.16 which removed Application.Top/Toplevel) - Revise TerminalGuiViewHost: replace Application.TopRunnableView usage with an explicit View? Container property set by TuiApplicationHost; Show/Remove operate on the guaranteed-non-null shell, sizing each child with Dim.Fill() - Add RegisterTuiExtensions (C# 14 extension block on IContainer): RegisterTui() wires TuiViewRegistry, TerminalGuiViewHost (singleton), ITuiViewHost mapped to the same concrete singleton via RegisterMapping<,>, ITuiNavigator, TuiApplicationHost; RegisterView<TView,TViewModel>() registers the pair transient and maps in the registry - Add DI tests RegisterTuiExtensionsTests (2 tests, TDD: written before production code) - Add README.md for SquidStd.Tui
- add samples/SquidStd.Samples.Tui with CounterViewModel, CounterView, Program.cs - register SquidStd.Samples.Tui in SquidStd.slnx under /samples/ folder - add TerminalGuiViewHostTests: headless Show/Remove coverage for blank-screen regression - harden RegisterTui_RegistersNavigatorAndRegistry with Assert.Same singleton guard (TerminalGuiViewHost and ITuiViewHost must be the same instance)
…dead code - TuiNavigator: deactivate the covered screen before pushing a new one so OnActivatedAsync/OnDeactivatedAsync are paired (no double-activate on forward-then-back navigation) - add ViewBinderAutoBindTests covering the reflection binding paths (label one-way, textfield two-way apply, button command discovery, unrecognised/wrong-type skip) - delete unused ViewBinder.Track method - add missing XML summaries on ViewBinder ctor/Dispose, TuiViewRegistry Map/ViewTypeFor and TerminalGuiViewHost Show/Remove
feat(tui): add SquidStd.Tui — MVVM layer over Terminal.Gui v2
- Add BindMode enum (OneWay/TwoWay) under SquidStd.Tui.Types.Tui - Add StackOrientation enum (Vertical/Horizontal) under SquidStd.Tui.Types.Tui - Add abstract TuiNode<TViewModel> base class under SquidStd.Tui.Dsl - Add LabelNode<TViewModel>: one-way string property binding - Add TextFieldNode<TViewModel>: string property binding with BindMode - Add ButtonNode<TViewModel>: caption + ICommand expression binding - Add StackNode<TViewModel>: orientation + typed children list - Add TuiNodeTests covering all four node types (4 tests, all green)
… views - add TuiNodeMaterializer in SquidStd.Tui.Dsl; walks TuiNode<TViewModel> trees and produces View graphs - wire LabelNode via binder.OneWayText, TextFieldNode via TwoWay/OneWay binder overloads, ButtonNode via binder.Command - StackNode (Vertical/Horizontal) produces a container View using Pos.Bottom/Pos.Right and Dim.Fill for layout - TViewModel constrained to INotifyPropertyChanged so existing binder overloads resolve without new binding logic - add TuiNodeMaterializerTests covering VStack+Label one-way propagation and TextField two-way VM→widget; both pass headless - outer test class marked partial because nested ObservableObject partial class requires it (CommunityToolkit source generator)
feat(tui): add fluent declarative UI DSL for SquidStd.Tui
Views derive from Window (IDisposable). DryIoc throws RegisteredDisposableTransientWontBeDisposedByContainer when a disposable type is registered as a transient, so resolving the application host and navigating to the first screen crashed at startup. Register the view with Setup.With(allowDisposableTransient: true): the view host already disposes each view on Remove, so the container must not also track/dispose it. Add a regression test registering a disposable view through RegisterView.
fix(tui): allow disposable transient views in RegisterView
Watch_RapidWritesToSameFile_DebouncesToSingleEvent assumed five rapid writes fall inside one 200ms debounce window; on a loaded CI runner a scheduler/GC pause between writes split the burst across two windows, emitting two events and failing Assert.Single. Use a 1s debounce window for this test and a tight synchronous write loop so the burst reliably collapses to a single event without weakening the assertion's intent.
test(core): stabilise flaky FileWatcher debounce test
Apply the configured ReSharper/formatter layout across the solution (attribute placement, member layout, whitespace). One semantic side-effect was corrected: the cleanup turned a test helper property get-only, breaking PropertyPathTests.Setter_Property_WritesValue (the property is written via an expression-tree setter the analyzer can't see) — restored its setter. No production property lost a setter (verified) and the full suite is green.
style: apply repo-wide code formatting
- StackExchange.Redis 3.0.7 -> 3.0.11 - AWSSDK.SimpleNotificationService / AWSSDK.SQS 4.0.3.8 -> 4.0.100 - AWSSDK.KeyManagementService 4.0.12.9 -> 4.0.100 - AWSSDK.SecretsManager 4.0.5.8 -> 4.0.100 Build clean and full suite green (917) with no new deprecation warnings in the affected adapters.
chore(deps): update Redis and AWS SDK packages
…+ AES-GCM) - Add PasswordCipher static class: Encrypt/Decrypt byte[], EncryptString/DecryptString (base64) - Add PasswordDecryptionException (sealed, extends CryptographicException) for wrong password or tampered data - AES-256-GCM with AAD-bound authentication; wrong password and ciphertext/header tampering all throw PasswordDecryptionException - Encrypt produces unique blobs per call via random salt + nonce - PbkdfCost defaults to Moderate when omitted
The envelope stores parallelism in a single byte, so a value above 255 would truncate silently and produce an undecryptable blob (the key is derived with the real lane count on encrypt but the envelope records the truncated one). Guard it with ArgumentOutOfRangeException.ThrowIfGreaterThan up front. Add tests for the guard plus empty-plaintext and non-ASCII-password round-trips.
feat(crypto): add password-based encryption (PasswordCipher)
- new project SquidStd.Vfs.S3 (net10.0, PublishNuget=true, Minio 7.0.0) - S3FileSystemOptions: Aws (AwsConfigEntry) + Bucket, mirrors S3StorageOptions shape - S3FileSystem: IVirtualFileSystem + IDisposable over MinIO client - lazy EnsureBucketAsync (BucketExistsArgs / MakeBucketArgs) with SemaphoreSlim - ExistsAsync via StatObjectArgs, ObjectNotFoundException → false - ReadAllBytesAsync via GetObjectArgs + callback stream, ObjectNotFoundException → null - OpenReadAsync: same buffering, ObjectNotFoundException → FileNotFoundException - WriteAllBytesAsync via PutObjectArgs with MemoryStream + WithObjectSize - OpenWriteAsync returns DeferredWriteStream flushing through WriteAllBytesAsync - DeleteAsync: stat-then-remove, returns whether object existed - ListAsync: ListObjectsEnumAsync with WithRecursive(true), yields VfsEntry(Key, Size, LastModifiedDateTime) - CreateClient mirrors S3StorageService (URI parsing, WithSSL, optional WithRegion) - Dispose last, Interlocked-guarded - RegisterS3FileSystemExtensions: extension(IContainer) block using RegisterDelegate<IVirtualFileSystem> directly (no SquidStd.Vfs dep) - integration tests: MinioCollection fixture reused, 9 tests all green (Docker available locally) - slnx + test csproj updated to include new project
- New project SquidStd.Vfs.Database targeting net10.0 with FreeSql 3.5.310
- VfsFileEntity: BaseEntity subclass with Path/Content/Size; unique index on
Path via FreeSql class-level [Index("uq_vfs_file_path", "Path", true)]
- DatabaseFileSystem: IVirtualFileSystem backed by IDataAccess<VfsFileEntity>;
query-then-write upsert in WriteAllBytesAsync; StartsWith predicate for
ListAsync prefix filtering; DeferredWriteStream for OpenWriteAsync
- RegisterDatabaseFileSystemExtensions: extension(IContainer) DryIoc helper
- Integration tests (SQLite, no Docker): write-read round-trip, upsert
same-path deduplication, ExistsAsync, DeleteAsync, ListAsync with/without
prefix — all 7 pass
- Added SquidStd.Vfs.Database project reference to SquidStd.Tests
feat(vfs): add VFS decorators and S3/database backends
…S backends - README: add SquidStd.Tui row under new ### User interface section (after Workers) - README: update SquidStd.Vfs row to mention composable decorators (ReadOnlyFileSystem, ScopedFileSystem, OverlayFileSystem, CachingFileSystem) - README: add SquidStd.Vfs.S3 and SquidStd.Vfs.Database rows to Storage section - README: update SquidStd.Crypto row to mention PasswordCipher and Argon2id + AES-256-GCM - README: extend Overview bullet for Data & storage to mention new VFS backends/decorators; add new Terminal UI bullet for SquidStd.Tui - docs/articles: add tui.md, vfs-s3.md, vfs-database.md include articles - docs/articles/toc.yml: add SquidStd.Vfs.S3, SquidStd.Vfs.Database after SquidStd.Vfs; add SquidStd.Tui before SquidStd.Crypto - docs/tutorials/tui.md: new tutorial covering CounterViewModel, imperative TuiView<T>, declarative TuiComposedView<T> DSL, DI boot, and ViewModel-first navigation - docs/tutorials/vfs.md: add S3 and database backend sections; add CachingFileSystem, ReadOnlyFileSystem, ScopedFileSystem, and OverlayFileSystem decorator examples - docs/tutorials/crypto.md: add password-based encryption section (PasswordCipher.Encrypt/ Decrypt, EncryptString/DecryptString, PbkdfCost.Sensitive, PasswordDecryptionException) - docs/tutorials/toc.yml: add Terminal UI (MVVM) entry
docs: document TUI, password-based encryption, and S3/database VFS backends
Contributor
There was a problem hiding this comment.
CodeQL found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release summary
Rolls up this development cycle from
developintomain(triggers semantic-release + NuGet publish).Highlights
SquidStd.Tui— new module: MVVM for terminal apps over Terminal.Gui v2 (observable ViewModels, fluentViewBinder+AutoBind, ViewModel-first navigation, DryIoc wiring) plus a declarative DSL (TuiComposedView<T>/Ui.VStack/Ui.Label/Ui.Button).SquidStd.Cryptopassword-based encryption —PasswordCipher(Argon2id → AES-256-GCM, self-describing versioned envelope) withPbkdfCostpresets.SquidStd.Vfs.S3(S3-compatible via MinIO SDK),SquidStd.Vfs.Database(FreeSql), and composable decoratorsReadOnlyFileSystem/ScopedFileSystem/OverlayFileSystem/CachingFileSystem(offline-resilient reads).Also
Merge note
Merge with a merge commit (not squash) so semantic-release sees the conventional commits and publishes to NuGet.
Test plan