Add EmptyState control — v1.40.0
Details
Introduces a reusable `EmptyState` control to `PrivStack.UI.Adaptive`, eliminating
per-plugin ad-hoc "no items yet" implementations.
**What was added**
`Controls/EmptyState.cs` — a `Border` subclass following the established imperative
construction pattern (no AXAML template, no reflection bindings). All visual tokens
resolve dynamically from the active PrivStack theme via `GetResourceObservable`.
Properties exposed:
- `IconData` (Geometry?) — Material icon path supplied by the consumer.
- `Title` (string) — Primary heading displayed below the icon bubble.
- `Message` (string) — Supporting copy, center-aligned, max-width 320, wrapping.
- `ActionLabel` / `ActionCommand` — Optional primary CTA (renders as `Button.accent`,
hidden when ActionLabel is null or empty).
- `SecondaryActionLabel` / `SecondaryActionCommand` — Optional ghost CTA (renders as
`Button.ghost`, hidden when SecondaryActionLabel is null or empty).
- `Variant` (EmptyStateVariant enum: Default | Search | Error | Permission) — Controls
which theme brush pair is applied to the icon bubble background and icon fill.
Visual structure:
```
StackPanel (center, spacing=24)
Border (72×72, CornerRadius=20) ← icon bubble; color driven by Variant
Path (36×36, Stretch=Uniform) ← icon path
StackPanel (spacing=8)
TextBlock ← title (ThemeFontSizeHeading2, SemiBold, ThemeTextPrimaryBrush)
TextBlock ← message (ThemeFontSizeBody, ThemeTextMutedBrush, MaxWidth=320)
StackPanel (spacing=12) [hidden when no ActionLabel]
Button.accent ← primary CTA
Button.ghost ← secondary CTA [hidden when no SecondaryActionLabel]
```
Variant → brush mapping:
- Default → ThemePrimaryMutedBrush / ThemePrimaryBrush
- Search → ThemeInfoMutedBrush / ThemeInfoBrush
- Error → ThemeDangerMutedBrush / ThemeDangerBrush
- Permission → ThemeWarningMutedBrush / ThemeWarningBrush
Theme changes are handled by subscribing to `ActualThemeVariantChanged` in
`OnAttachedToVisualTree` and unsubscribing in `OnDetachedFromVisualTree`, matching
the lifecycle pattern used by `PluginToolbar`, `PluginSidebar`, and `IconLabel`.
**Version bump**
`Directory.Build.props`: `PrivStackSdkVersion` 1.39.0 → 1.40.0
This is a minor bump reflecting new additive API surface in `PrivStack.UI.Adaptive`.
No breaking changes to existing controls.
Visual modernization pass 2 — shell components (v1.37.0)
Details
Continued modernization of the PrivStack desktop shell components
toward a Material 3 aesthetic, following the Phase 1 color and nav
overhaul (v1.36.0).
PluginCommandBar.axaml:
- Fixed StackPanel Spacing="-2" → Spacing="2" eliminating the
negative-spacing title/subtitle overlap
- Title downgraded from FontSizeLg/SemiBold to FontSizeMd/Medium
to reduce visual weight in the command bar
- Subtitle font size reduced to ThemeFontSizeXsSm for better hierarchy
PluginFooter.axaml:
- Removed the Windows XP-era 1×12px Border vertical separator between
primary and secondary stat sections
- Replaced with a modern "·" mid-dot separator (·) consistent
with the FilesView footer pattern
PrivStackTheme.axaml:
- Added global Button.view-toggle, Button.view-toggle:pointerover, and
Button.view-toggle.active styles so plugin views using the view-toggle
class work without needing a ViewToggleGroup wrapper
- Active state: ThemePrimaryMutedBrush background + ThemePrimaryBrush
foreground + SemiBold weight
Modernize design system to material-inspired indigo palette (v1.36.0)
Details
Overhaul the default Dark and Light themes plus core component styles to bring the
visual language in line with modern 2024-era desktop UI conventions (Linear, Craft,
Arc, Material 3). The previous design used a neon cyan primary (#00D4FF) and sharp
nav-item indicators that read as 2018-era SaaS. This commit replaces them
systematically across every touch point that the token system covers.
Color system (Dark theme):
- Primary accent: cyan #00D4FF → indigo #6366F1. The new hue is softer, premium,
and works equally well on dark and light surfaces without the "tech-startup neon"
connotation.
- Background surfaces shifted: #0A0A10 → #0F0F14, surface #141420 → #16161E,
elevated #1E1E2C → #21212C. The steps are now perceptibly distinct, giving the
depth hierarchy real visual weight instead of near-identical near-blacks.
- Navigation sidebar given its own explicit dark token set (#0E0E16 bg) rather than
aliasing the standard surface color, creating a clear light/dark split between
sidebar and content even in dark mode.
- Secondary accent: #8B5CF6 → #A78BFA (lighter, better contrast on the new surfaces).
- Semantic colors refreshed: success → #34D399 (vibrant emerald), warning →
#FBB32F (amber), info → #60A5FA (sky), danger → #F87171 (rose). All softer
and more readable than the previous Tailwind 500-level values on dark bg.
- Hover/selected/pressed states carry a subtle indigo tint (#1D1D2C / #201E3A)
instead of the cyan-tinted #1A2A35 that was previously selected state.
- Text primary: pure white → #EBEBF0 (slightly warm, reduces harshness on dark).
Color system (Light theme):
- Primary: #0099CC → #4F46E5. Consistent indigo family, darker shade for
sufficient contrast on white/near-white backgrounds.
- Surfaces and borders carry a subtle violet tint (#F0F0F6 bg, #E8E8F2 border-subtle)
that ties the light theme to the same design language as dark.
- Hover/selected states: #E0E2E8 / #D0E8F0 → #E2E2F0 / #D4D0F8 (indigo-tinted
instead of the previous cyan-tinted selection blue).
- Nav (both themes): unified to #111118 / #636380 / #1A1A2A for hover — consistent
dark sidebar regardless of light/dark content theme.
Component styles (PrivStackTheme.axaml):
- Button.accent, Button.danger, Button.success: corner radius ThemeRadiusSm (6px) →
ThemeRadiusLg (12px). On a 32px tall button this produces a clearly rounded shape
without being a full pill — exactly what MD3 specifies for filled buttons.
- Foreground on accent/danger/success buttons changed to ThemeTextOnAccentBrush
(#FFFFFF) rather than ThemeBackgroundBrush. This is semantically correct: the
background token can be near-black on dark themes, but text-on-accent should always
be white for legibility.
- Border.badge CornerRadius: hardcoded 10 → ThemeRadiusFull. Tags and badges are
now true pills, consistent with every modern design system.
- ProgressBar MinHeight: 4 → 6px; CornerRadius 2 → 3. The extra 2px makes the
bar visually present without being heavy, and matches typical Material 3 density.
Navigation sidebar (NavigationSidebar.axaml):
- Active item indicator: removed the 3px left border + asymmetric CornerRadius hack
(0,6,6,0) + offset padding (9,10,12,10). Replaced with a filled background pill
using ThemePrimaryMutedBrush (20% alpha indigo). The icon continues to render in
ThemePrimaryBrush (solid indigo) and text stays ThemeNavTextHoverBrush (white/near-
white), giving a clear active indication without the visual noise of a left stripe.
- Same change applied to Button.nav-item-collapsed.active for the collapsed sidebar
state, so the icon-only view is consistent.
- The pill treatment matches the Linear / Arc / Craft navigation pattern that
users associate with modern, well-designed desktop software.
Version: 1.35.0 → 1.36.0 (minor — new visual feature, no API/data changes)
Redesign setup wizard — auth first, cloud workspace selection (v1.35.0)
Details
Reorder the first-run setup wizard so authentication (License step)
happens before workspace creation. This enables users who already have
a cloud workspace from another device (e.g., Android) to connect to it
during initial setup instead of always creating a new empty workspace.
New flow: Welcome → License → Workspace → DataDirectory → [CloudWorkspaces] → Password → EmergencyKit → Complete
Key changes:
SetupStep enum reordered: License now comes after Welcome (position 2)
instead of after DataDirectory (position 4). CloudWorkspaces is a new
conditional step shown when the user selects PrivStack Cloud and has
existing cloud workspaces.
TrialResponse DTO extended with access_token, refresh_token, user_id,
and cloud_config fields to receive auth tokens from the server's trial
verification endpoint.
ListCloudWorkspacesAsync added to PrivStackApiClient — a direct HTTP
GET /api/cloud/workspaces call that works before FFI initialization,
needing only an access token.
Both auth flows (OAuth sign-in and trial verification) now capture
cloud sync tokens (CloudSyncAccessToken, CloudSyncRefreshToken,
CloudSyncConfigJson, CloudSyncUserId) and set IsCloudSyncAvailable
when cloud_config is present.
DataDirectory step gains a PrivStack Cloud option (visible when
authenticated). When selected, the wizard fetches cloud workspaces
via the API client and either shows the CloudWorkspaces picker step
(if existing workspaces are found) or falls through to create a new
cloud workspace automatically.
InitializeServiceAndContinue updated: after runtime initialization,
if PrivStack Cloud is selected, it calls Configure + AuthenticateWithTokens
on the ICloudSyncService, then either connects to the selected existing
cloud workspace or registers a new one, and persists the CloudWorkspaceId
+ SyncTier on the workspace record.
ICloudSyncService injected into the SetupWizardViewModel constructor.
AXAML rewritten to match the new step order with the License step
content block before Workspace, a new PrivStack Cloud card in the
DataDirectory options, and a new CloudWorkspaces step with workspace
list + "Create New Workspace" option.
Add alter_column_type mutation across Rust, FFI, and SDK (v1.12.0, SDK v1.39.0)
Details
Add ALTER COLUMN SET DATA TYPE support for datasets. The Rust store validates
the target type against a whitelist of known DuckDB types, executes the DDL,
and refreshes column metadata. The FFI layer exposes the operation as
privstack_dataset_alter_column_type returning PrivStackError. The SDK adds
AlterColumnTypeAsync to IDatasetService, with P/Invoke binding and service
implementation following the existing DropColumn/RenameColumn pattern. This
enables the Data plugin to let users change column types after import via
a confirmation overlay.
Get notified about new releases