feat(socials): bar appearance — text colour, background plate, opacity, text shadow

In the Socials drawer, under the show/hide switch: text colour, an optional background plate
with its own colour and opacity, and a text drop shadow. The bar had been hard-wired
white-on-transparent since it shipped.

- `SocialsConfig` gains five additive properties (BarTextColor, BarBackgroundEnabled,
  BarBackgroundColor, BarBackgroundOpacity, BarTextShadow).
- Persistence: five new Socials columns, added by the per-column idempotent migration pattern
  (a half-migrated DB heals instead of failing), written in Save and read back in Load with
  defaults for pre-existing rows.
- `SocialBarRenderer.Render(entries, SocialBarStyle?)` — the look is a separate value type so the
  renderer stays a pure "entries + look" function and the preview can never read a half-applied
  config mid-edit. A malformed hex falls back to white/black instead of throwing inside a render
  tick.
- The plate hugs the entries (rounded 20px, padding) so it reads as a caption bar, not a band
  across the frame. Off by default, because transparent is what creators have today.
- The colour pickers reuse `BorderColorPalette` — one catalogue of the app's colours, per the
  TASK 60/63 rule.

Guard: SocialBarAppearanceTests paints the real strip and inspects pixels — default is white with
no plate, a red text colour reaches the glyphs, a 55% blue plate yields half-transparent blue
pixels, and the shadow adds dark pixels that were not there before.
This commit is contained in:
2026-10-30 08:47:50 -07:00
parent 9528128ca7
commit c82636e1a9
9 changed files with 329 additions and 16 deletions
+16 -4
View File
@@ -591,11 +591,23 @@ public partial class LayoutStore : IDisposable
columns.Add(reader.GetString(1));
}
if (columns.Contains("BarEnabled")) return;
// Each column is added on its own, guarded by presence — the same idempotent pattern the
// webcam table uses, so a half-migrated DB heals instead of failing.
AddColumnIfMissing("BarEnabled", "INTEGER NOT NULL DEFAULT 1");
AddColumnIfMissing("TextColor", "TEXT NOT NULL DEFAULT '#FFFFFF'");
AddColumnIfMissing("BackgroundEnabled", "INTEGER NOT NULL DEFAULT 0");
AddColumnIfMissing("BackgroundColor", "TEXT NOT NULL DEFAULT '#000000'");
AddColumnIfMissing("BackgroundOpacity", "REAL NOT NULL DEFAULT 0.55");
AddColumnIfMissing("TextShadow", "INTEGER NOT NULL DEFAULT 0");
using var alter = _connection.CreateCommand();
alter.CommandText = "ALTER TABLE Socials ADD COLUMN BarEnabled INTEGER NOT NULL DEFAULT 1;";
alter.ExecuteNonQuery();
void AddColumnIfMissing(string name, string spec)
{
if (columns.Contains(name)) return;
using var alter = _connection.CreateCommand();
alter.CommandText = $"ALTER TABLE Socials ADD COLUMN {name} {spec};";
alter.ExecuteNonQuery();
columns.Add(name);
}
}
// v8 → v9: SocialEntry gains Software (the fediverse instance's nodeinfo