Skip to content

feat(deck): tab-bar-style tiles with repo icons, attention sorting, and tile-style setting - #576

Merged
danshapiro merged 30 commits into
mainfrom
feat/deck-tile-modes
Jul 29, 2026
Merged

feat(deck): tab-bar-style tiles with repo icons, attention sorting, and tile-style setting#576
danshapiro merged 30 commits into
mainfrom
feat/deck-tile-modes

Conversation

@danshapiro

Copy link
Copy Markdown
Owner

Part 1 — Deck tile redesign

Stream Deck tiles now mirror the tab bar instead of showing terminal screenshots: tab title on top, centered repo icon(s) resolved by the same code the tab bar uses (cap-at-3, colored letter-avatar fallback, blank-SVG detection), status backgrounds matching tab-bar semantics (green border + light green fill for bar-on-top tabs; light green fill for green-filled tabs; no fill otherwise), white ring still marks the active tab. Keys sort by attention priority (bar-on-top → green-filled → green-icon → blue-icon → rest, stable by tab order within groups) with a press-snapshot guard so mid-press reorders can't misfire a press. The old preview/capture machinery was removed from the default path.

Part 2 — Tile-style setting + waiting-count union

New Settings → Stream Deck "tile style" option: "Status icons" (new design, default) vs "Terminal previews" (classic look restored from git history: live previews + status rings + tab-bar order). Switching applies live with no reload; capture polling only runs in classic mode; persists client-side. Stream Deck+ touch strip "waiting" count now counts the union of needs-attention and waiting-for-approval tabs (each tab once), in both styles; approval-waiting tabs get an amber attention treatment.

Quality notes

  • Client-only (no server changes); TDD throughout
  • Redesign: independent whole-branch delta review passed round 1 with zero blockers
  • Modes: plan review formally PASSED 2 rounds, delta review passed round 1
  • Full coordinated suites green (client/server/electron), lint + typecheck clean
  • Known non-blocking follow-ups noted in review logs (minor test hygiene; classic-mode terminals that never stop printing never idle-dim — faithful restoration of old behavior)

danshapiro and others added 30 commits July 29, 2026 01:48
…ndings

- deck owns un-gated fetchRepoIconMeta probing (TabBar probe is setting-gated
  and conditionally mounted; leader window may lack TabBar)
- panesForTab synthesized-pane fallback for layout-less tabs (addTab never
  seeds a layout), mirroring TabBar.tsx:203-221
- IconImageCache drawn-empty probe (<1% alpha) for dimensionless SVGs that
  load but draw blank; explicit-dims drawImage rule in the renderer
- verified jsdom NoOpResourceLoader constraints encoded in test guidance
- Rust-server terminal-meta coverage documented as accepted tab-bar parity
…h-eyes review

- Task 4 step 4: state the required terminalMeta/repoIcons reducer registration
  in the controller/e2e/VirtualDeckPanel fixture stores (preloadedState alone is
  silently ignored without matching reducers) and correct the misdiagnosed
  failure mode; run VirtualDeckPanel suite too. Task 3/8 now reference it.
- Task 8: make the no-periodic-repaint test able to go RED (register a
  changing-snapshot terminal-text reader); drop the false claim that
  deck-controller.test.ts has PREVIEW_REFRESH_TICKS tests (only the e2e suite
  covers previews); add a late-terminalMeta probe test.
- Task 8 step 3.7: call probeRepoIcons BEFORE the model-JSON bail-out in
  onStoreChange - terminalMeta enrichment doesn't change the model JSON, so a
  post-bail-out probe never fires in the TabBar-less leader scenario.
- Task 10/11: use the real markTabAttention({ tabId }) object payload (a bare
  string silently defeats the RED gate) and add an armed-gate sanity assertion.
…ting, controller-suite API, grep scope

Fresh-eyes review iteration 2 blocking fixes:
- Task 3: layout-less tests override tab.mode to 'claude' (fixture tabs are
  mode 'shell'; synthesized pane inherits tab.mode, so greenIcon and repo-cwd
  resolution were unreachable as written); corrected the wrong fixture-fact
  comment and the drift-guard note
- Task 6: deferredLoader counts loader invocations; dedup/no-retry assertions
  use calls() instead of the vacuous Map-keyed pending.size, pinning the
  load-bearing no-retry-after-failure property
- Task 8: test snippets rewritten against deck-controller.test.ts's real API
  (tabCount, 2-arg setup + 3rd-arg controller options, no defaultSettings);
  repoIconsOnTabs seeded pre-start via updateSettingsLocal through a new
  StoreOpts option; setup-extension instructions rewritten with verified facts
- Task 4: per-suite fixture API claim corrected (makeState vs makeStore vs
  makeDeckStore)
- Task 9: dead-reference grep excludes SettingsView.core.test.tsx's unrelated
  previewLines local so the no-matches gate is achievable
- Task 10: snippets use tabCount (active tab defaults to t1); closeTab
  described accurately as the tabsSlice async thunk
…e2e setup 4th param + deferredLoader port

Fresh-eyes iteration 3 blocking fixes:
- Task 8 step 3.8 (new): register terminalMeta/repoIcons reducers in
  deck-manager.test.ts makeStore - probeRepoIcons unconditionally reads
  both slices and that suite starts real controllers, so without them
  every manager test dies in start(); Step 4 Expected updated to match.
- Task 11: Interfaces now mandate the two missing harness extensions -
  extend the e2e suite's 3-param setup() with a 4th extra-controller-
  options parameter (spread last into the controller constructor) and
  port deferredLoader from icon-image-cache.test.ts; Step 1 heading and
  the repo-icon scenario annotated so harness failures aren't
  misdiagnosed as integration bugs.
…rget a press

Task 4 made selectDeckModel sorted by status priority, so a mid-press
re-sort (e.g. a tab gaining attention) could move tabs between keys and
the release would act on whatever the slot showed at keyUp, not what the
user saw when they pressed. keyDown now snapshots the key's displayed
target (pager / tab / none); keyUp acts on that snapshot. A tab closed
mid-press is a no-op; the action-layer keyUp branch is unchanged (action
keys are fixed indices, unaffected by sorting).

🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier)

Co-Authored-By: Amplifier <[email protected]>
…ns, press snapshot

Task 11: seven new e2e scenarios through the real store + real
DeckController + FakeDeckDevice covering status-priority sorting (stable
within groups), the three background treatments + active ring, blue/green
dots, repo-icon unready->ready repaint via an injected IconImageCache with
a deferred loader, pager paging over the sorted order, the press-down
target snapshot under a mid-press re-sort, and short/long-press on the
sorted layout. Harness: setup() gains a 4th extra-controller-options
param spread last into the constructor; makeDeckStore gains activeTab,
paneStatus, terminalMeta, and repoIcons seeding; deferredLoader ported
from icon-image-cache.test.ts. All 9 pre-existing scenarios pass
unmodified (already sorted-order-aware).

🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier)

Co-Authored-By: Amplifier <[email protected]>
… plan

- Task 8: mergeLocalSettings does not exist as an action; use updateSettingsLocal
  (verified synchronous re-resolution; sanity assertion added against the
  extractLegacyLocalSettingsSeed silent no-op trap)
- Task 4: reducer state is immer-frozen; adopt the structuredClone withTileStyle
  idiom instead of direct mutation
- Task 1: name the three streamDeck whitelists explicitly (silent-drop trap);
  record verified cross-window crossTabSync propagation
- Task 2: record verified a11y-pattern evidence; note TabsView private
  SegmentedControl duplicate is out of scope
…eaking strict toEqual assertions

Fresh Eyes review found the 'all existing e2e scenarios PASS unchanged'
gate unachievable: Task 6 adds style:'icons' to tab KeySpecs, which
breaks three strict toEqual tab-spec expectations in
test/e2e/stream-deck-flow.test.tsx that no task updated. Step 5 now
instructs both fixture edits (defaultSettings tileStyle + the three
toEqual expectations) and corrects the false 'fails to compile'
prediction (tests are outside tsconfig include; signal is runtime
assertion behavior).
…assertion

Add `tileStyle: 'terminal-previews'` to the 'survives the legacy seed normalizer'
test fixture and assert it survives extraction/normalization. This positive-path
guard prevents silent-strip failures if `tileStyle` is removed from the
`pickKeys` whitelist in extractLegacyLocalSettingsSeed or the seed normalizer
Zod block.

Generated with Amplifier

Co-Authored-By: Amplifier <[email protected]>
…polling gate, strip waiting union

🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier)

Co-Authored-By: Amplifier <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant