Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
235 changes: 35 additions & 200 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,53 +6,6 @@ canonical list of *parked* v1 features is the "Out of scope for v1" section in

---

## Shared parameter definitions (global and CLI-scoped)

**Problem.** Today every `Snip` carries its own `Parameter` definitions inline.
If twenty Snips all need an `env` Choice dropdown with options
`dev` / `staging` / `prod`, that definition is duplicated twenty times and the
user has to keep them in sync by hand. Same story for any other recurring
token (`region`, `org_id`, `tenant`, etc.).

**Idea.** Let parameter definitions live above the Snip and be *referenced* by
name from individual Snips, with the option to still define a parameter
locally on a Snip when something one-off is needed.

**Sketch.**
- Add a collection of shared `Parameter` definitions, probably at two scopes:
- **CLI-scoped** — most common case, e.g. an `env` defined once on the
`pl-app` CLI and used by every `pl-app` Snip.
- **Global** — for the rare cross-CLI definition (`yes_no`, common flags).
- Both scopes should be supported; CLI-scoped takes precedence over global
when names collide.
- A Snip can either:
- **Reference** a shared parameter by name — it picks up the type, options,
and default automatically.
- **Define** a parameter locally — overrides any shared definition with the
same name *for that Snip only*.
- The substitution engine doesn't change: tokens still resolve from a
`name → value` dictionary. Only the *definition* moves; resolution is the
same.

**Open questions** to settle when this gets scheduled:
- Reference by **name** or by **ID**? Names match the `{token}` model and read
well; IDs survive renames. Probably name-based with a uniqueness constraint
per scope, plus a rename flow that propagates.
- Where does the UI to manage shared parameters live? A Settings page? A
flyout from each CLI in the pane header? Probably the latter for CLI-scoped
and a Settings entry for global.
- Schema migration: additive. New collections on `SnipStoreDocument` (global)
and `Cli` (CLI-scoped). Existing Snips with local `Parameter` entries keep
working unchanged.
- Should a Snip's `Parameters` list contain a discriminated union (local
definition vs. reference) or two parallel lists? A small object with
`Name` + optional inline definition feels cleanest — absent inline ⇒ resolve
from shared scope.

This is the single biggest content-quality-of-life feature after v1 ships.

---

## Execute Snips, not just construct them (with per-CLI shell + execution history)

**Problem.** Today Snipdeck is a sophisticated clipboard — it builds the command
Expand Down Expand Up @@ -297,165 +250,47 @@ Reasons:

---

## Code-sign the installer (SmartScreen / Authenticode)

**Problem.** The v0.1.0-alpha.1 installer is unsigned. Downloading it in
Edge / Chrome trips SmartScreen's "this file isn't commonly downloaded —
make sure you trust it" warning, and running it shows the blue "Windows
protected your PC" dialog. Every user has to click through "More info →
Run anyway", which is a terrible first impression and a real deterrent.

**Cause.** SmartScreen reputation is built from a combination of valid
Authenticode signatures plus aggregate download counts. An unsigned
binary with zero downloads is automatic prompt-territory. Signing solves
the warning immediately for EV certs and over time (as downloads
accumulate) for OV certs.

**Options.**

- **Azure Trusted Signing** (recommended). Microsoft's new managed
signing service, ~$10/month, gives you a cloud-hosted signing identity
with no HSM to manage. Velopack supports it natively via
`--azureTrustedSigningAccount` / `--azureTrustedSigningCertProfile`
flags on `vpk pack`. Wants an Azure subscription and an OIDC trust
relationship from the GitHub Actions runner — five-step setup, no
long-lived secrets in the repo.
- **Standard OV code-signing certificate.** From DigiCert / Sectigo /
GlobalSign, ~$200–400/year. Comes as a cert + key the runner imports.
SmartScreen reputation builds gradually rather than instantly. Easier
if you already have an account with a CA.
- **EV code-signing certificate.** ~$400–600/year, requires an HSM /
USB token (hard to use from CI without a paid signing service in front
of it) but gets immediate SmartScreen reputation. Probably overkill
for an alpha; revisit pre-1.0 stable.

**Sketch — Azure Trusted Signing path.**

1. Create the Azure resources (Trusted Signing account, certificate
profile, OIDC trust to the GitHub repo) per
<https://learn.microsoft.com/azure/trusted-signing/>.
2. Add the federated-identity secrets / variables to the repo: tenant
id, client id, account / profile names.
3. In `release.yml`, extend the `vpk pack` step with
`--azureTrustedSigningAccount`, `--azureTrustedSigningCertProfile`,
`--azureTrustedSigningEndpoint` (region URL) and an `azure/login@v2`
step ahead of it for federated auth.
4. Verify with `signtool verify /pa /v Snipdeck-*-Setup.exe` locally
after a download, and re-do the manual install smoke test.

**Open questions.**

- Whether the cost ($10/month indefinitely) is acceptable for a hobby /
side project. If not, the OV cert path is a single annual payment.
- Whether the Velopack-generated update packages (the `.nupkg` deltas
used by self-update) also need signing for self-update to keep working
smoothly under SmartScreen. Worth reading the Velopack signing docs
carefully before flipping the switch.

Not urgent — alpha users can click through the prompt — but blocking
for any kind of broader distribution.
## Icon picker (glyph browser) for tag icons

---

## Tighten the iteration loop (build/CI feedback)
**Problem.** Tag icons are set by typing a raw glyph — either pasting a Segoe
Fluent Icons character or entering its hex code point (`GlyphInput.Resolve`
already accepts `E8EC`, `U+E8EC`, `0xE8EC`, `&#xE8EC;`). That works, but it's
unfriendly: the user has to know or look up a code point and can't see what's
available. There's no way to browse the icon set.

**Problem.** During the phase build-out and post-release fixes, the
build-and-debug cycle relied heavily on PRs as the feedback loop:
local Linux can't build the `Snipdeck.App` project (the WinUI XAML
compiler is Windows-only), so analyser errors / build breaks only
surface in CI. Each round-trip is a PR, which generates churn and
sometimes ends with main broken (PR #13 was merged with red CI).

**Idea.** Three independent improvements; each is small, all three
together would make the iteration loop tight.
**Idea.** A visual icon picker — a searchable grid of glyphs the user clicks to
choose — instead of (or alongside) typing a code. Driven from the Tags
management view, and a reusable fit for any future "choose a glyph" need.

**Sketch.**
- A reusable `IconPicker` control / flyout: a virtualised `GridView` of
`FontIcon`s over a list of Segoe Fluent Icons glyphs, with a search box that
filters by name and/or code point.
- Needs a glyph **catalogue** — code point + friendly name — for the grid and
search. Either hand-curate a useful subset or embed the published Segoe Fluent
Icons mapping; the names are what make search worthwhile.
- Wire into the Tags view: add a "Choose…" button next to each row that opens
the picker, while keeping the free-text field as an escape hatch for pasting /
power users.
- Selection still flows through `GlyphInput.Resolve` and the stored-character
model, so persistence is unchanged.

- **`EnableWindowsTargeting=true` for local builds.** Adding this to
`Snipdeck.App.csproj` (or passing as `-p:EnableWindowsTargeting=true`
on Linux) lets the restore + compile step run on non-Windows hosts.
The WinUI XAML pass still requires Windows, but most analyser /
C# compiler rules fire under plain `dotnet build` and would catch
editorconfig violations (IDE0058, IDE0330, IDE0370, IDE0005 — all
hit in this session) before the push.
- **Branch protection: require status checks to pass.** Add a rule to
the existing branch ruleset on `main` that requires the
`App build (windows)` and `Core build + tests (ubuntu)` checks to be
green before the Merge button activates. Mechanical guardrail
against the merged-red scenario.
- **Draft PRs with force-push fixups during iteration.** Convention,
not config: open PRs as **Draft** while iterating, and amend +
force-push fixup commits into the original commit instead of stacking
"fix lint" follow-ups. The final merged history shows one clean
commit per change, which is what the project's commit log wants.
Auto-mode currently blocks `git push --force-with-lease` — would
need an explicit settings.json permission or a one-off approval
to enable. Force-push to `main` itself stays blocked.

**Sequencing.** Do `EnableWindowsTargeting` first (cheapest, biggest
quality-of-life win for me); then branch protection (one-off setup,
done forever); then adopt the draft-PR convention.

---
**Open questions** to settle when scheduled:
- Curated subset vs. full catalogue (the full set is ~1.5k glyphs — needs
virtualisation and good search to stay usable).
- Where do the glyph **names** come from, and is fuzzy search worth it?
- Do CLI icons (today: uploaded image, identicon fallback) also gain a
"pick a glyph instead" option, or stay image-only? Keep the first cut to tags.

## Final UI polish pass

A deliberate sweep of visual / interaction rough edges, done **at the end**
once the feature surface has settled — batching the nitpicks avoids
re-polishing the same screens after every feature lands. Known items so far:

- **Snip card Copy button is too wide.** It currently stretches further than
it should; size it to its content (or a sensible fixed width) so the card
action row reads cleanly.
- **"Delete CLI" button should be styled as a danger action.** It's a
destructive, hard-to-reverse operation — give it the red/danger accent
(e.g. a danger `Button` style / `Foreground` from the theme palette)
rather than the neutral default, so it visually distinguishes itself from
benign actions.
- **Inconsistent button corner radii.** Cancel buttons render with square
corners while Save / Copy buttons are rounded. Standardise on rounded
corners for *all* buttons (the dialog `CloseButton` is the likely culprit —
align it with the themed `CornerRadius` the primary buttons pick up).
- **Consider making the whole Snip card the Copy target.** Rather than a
dedicated Copy button on the card, let a click anywhere on the card trigger
the copy / parameter-fill flow. Weigh the trade-offs before committing:
discoverability and a cleaner card vs. losing an explicit affordance and
the risk of accidental copies / conflicts with the overflow menu and
favourite star hit-targets. Decide, then either remove the button or keep
it.

Add to this list as other cosmetic / interaction snags turn up during
feature work, then knock them out in one pass before a stable cut.

---

## Carried over from the phase stack

These were trimmed out of Phase 4–6 to keep the PRs reviewable. None are
load-bearing for the v1 demo, but they're the obvious next-pulls.

- **Hotkey rebinding UI.** The setting is editable in `AppConfig` already;
what's missing is a key-capture control on the Settings page and the call
to `IHotkeyService.TryRegister` after the change. Tooling: a small custom
`Control` that listens for a single key chord then displays it formatted.
- **Storage path: move / adopt / warn-on-conflict.** Per `CLAUDE.md`, when
the user changes the storage path we need three flows: move the existing
store to the new path; adopt a store already at the new path; warn when
both exist. UI: a "Change…" button next to the read-only path display.
- **Re-enable `PublishTrimmed` once JSON serialisation is trim-safe.**
Disabled in `Snipdeck.App.csproj` to unblock the first release. To
turn it back on:
1. Move `JsonSnipStore` / `JsonSettingsStore` onto
`JsonSerializerContext` source generation
(`[JsonSerializable(typeof(SnipStoreDocument))]` etc.) so the
untyped `Serialize/Deserialize` calls disappear. Removes IL2026.
2. Audit Jdenticon-net, Microsoft.Windows.SDK.NET and WinRT.Runtime
trim warnings (IL2104); either suppress per-assembly with
`<TrimmerRootAssembly>` entries / `[DynamicallyAccessedMembers]`
attributes, or accept them via targeted
`<TrimmerSingleWarn>false</TrimmerSingleWarn>` carve-outs.
3. Flip `PublishTrimmed` back to `True` for Release.

Payoff is a meaningfully smaller self-contained Velopack package
(probably ~80 MB instead of ~150–200 MB). Not urgent for alpha but
worth doing before a stable cut.
## Resizable, size-remembering snip editor

The New/Edit snip editor was widened to fit its content during the UI polish
pass, but it's still a `ContentDialog` (fixed size). Make it genuinely
user-resizable and persist the chosen size (to `AppConfig`) so it reopens at
the same dimensions. `ContentDialog` can't do this natively — the likely route
is to host the editor in a resizable secondary `Window` (native drag-resize)
rather than a dialog overlay, which means rethinking how the editor is shown
and how its result returns through `IShellInteractions`.
Loading