Files
yellowjacket/.planning/quick/7-pin-default-playlist-to-top-of-playlist-/7-PLAN.md
T
yonlu 96a9dc43d5 docs(quick-7): complete pin default playlist to top plan
- Add 7-SUMMARY.md with implementation details and self-check
- Update STATE.md with quick task 007 entry and session context
2026-03-01 10:08:11 -05:00

13 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
phase plan type wave depends_on files_modified autonomous requirements must_haves
quick-7 1 execute 1
backend/favorites/config.go
backend/config/config.go
frontend/src/store/favorites-store.ts
frontend/src/store/controllers/favorites-controller.ts
frontend/src/components/playlist-view/playlist-view.ts
frontend/src/components/config-page/config-page.ts
true
PIN-DEFAULT-01
truths artifacts key_links
When pin is enabled, the default/favorites playlist always appears first in the playlist list regardless of sort field or direction
When pin is disabled, the default playlist sorts normally with all other playlists
The pin setting is toggleable from the config/settings page under the Favorites section
The pin preference persists across app restarts via config.toml
path provides contains
backend/favorites/config.go PinDefault bool field on Config struct PinDefault
path provides exports
backend/config/config.go GetPinDefaultPlaylist and SetPinDefaultPlaylist methods
GetPinDefaultPlaylist
SetPinDefaultPlaylist
path provides
frontend/src/store/favorites-store.ts pinDefault state, getter, setter, and event reactivity
path provides
frontend/src/components/playlist-view/playlist-view.ts sortedEntries getter pins default playlist to top when enabled
from to via pattern
frontend/src/components/playlist-view/playlist-view.ts frontend/src/store/controllers/favorites-controller.ts favCtrl.pinDefault and favCtrl.playlistId in sortedEntries this.favCtrl.pinDefault
from to via pattern
frontend/src/store/favorites-store.ts backend/config/config.go GetPinDefaultPlaylist/SetPinDefaultPlaylist Wails bindings (Get|Set)PinDefaultPlaylist
from to via pattern
backend/config/config.go frontend/src/store/favorites-store.ts FavoritesConfigChanged event includes PinDefault field PinDefault
Pin the default/favorites playlist to the top of the playlist view regardless of sort order, controlled by a toggleable config setting.

Purpose: Users who rely on a favorites playlist want instant access without scrolling/sorting to find it. Output: Full-stack feature — config field, backend getter/setter, frontend store/controller, sort logic, and settings toggle.

<execution_context> @/home/caleb/.config/Claude/get-shit-done/workflows/execute-plan.md @/home/caleb/.config/Claude/get-shit-done/templates/summary.md </execution_context>

@backend/favorites/config.go @backend/config/config.go @frontend/src/store/favorites-store.ts @frontend/src/store/controllers/favorites-controller.ts @frontend/src/components/playlist-view/playlist-view.ts @frontend/src/components/config-page/config-page.ts

From backend/favorites/config.go:

type Config struct {
    PlaylistID int64     `toml:"PlaylistID"`
    IconStyle  IconStyle `toml:"IconStyle"`
}

From backend/config/config.go:

// Pattern for getter/setter — follow GetFavoritesPlaylistID / SetFavoritesPlaylistID exactly
func (c *Config) GetFavoritesPlaylistID() int64 { ... }
func (c *Config) SetFavoritesPlaylistID(id int64) error { ... }
func (c *Config) emitFavoritesChanged() {
    runtime.EventsEmit(c.ctx, events.FavoritesConfigChanged, map[string]any{
        "PlaylistID": c.Favorites.PlaylistID,
        "IconStyle":  string(c.Favorites.IconStyle),
    })
}

From frontend/src/store/favorites-store.ts:

// Event handler in constructor:
EventsOn(Events.FavoritesConfigChanged, (data: {
    PlaylistID: number;
    IconStyle: string;
}) => { ... });

// loadConfig pattern:
private async loadConfig(): Promise<void> {
    const [id, style] = await Promise.all([
        GetFavoritesPlaylistID(),
        GetFavoritesIconStyle(),
    ]);
    ...
}

From frontend/src/components/playlist-view/playlist-view.ts:

private get sortedEntries(): PlaylistEntry[] {
    const entries = this.filteredEntries;
    const dir = this.sortDirection === 'asc' ? 1 : -1;
    return [...entries].sort((a, b) => { ... });
}

From frontend/src/components/config-page/config-page.ts:

// Favorites section uses config-field with type: 'select'
// Pattern for toggle: use type: 'toggle' with boolean value
private renderFavoritesSection() { ... }
Task 1: Add PinDefault to backend config and expose getter/setter backend/favorites/config.go backend/config/config.go 1. In `backend/favorites/config.go`, add `PinDefault bool` field to the `Config` struct with TOML tag `"PinDefault"`. Default should be `true` (pin enabled by default). Update `ApplyDefaults()` — since Go zero-value for bool is false, add a separate mechanism: add a `pinDefaultSet bool` unexported field (no toml tag) to track if PinDefault was explicitly set, OR simpler: just document that the default is applied in `config.go`'s `applyDefaults`. Actually, simplest approach: since `bool` zero-value is `false`, and we want default `true`, handle this in `config.go`'s `applyDefaults()` method by setting `c.Favorites.PinDefault = true` when initializing a new Favorites config. No validation needed for a bool field.
  1. In backend/config/config.go:
    • Add GetPinDefaultPlaylist() bool method following the exact pattern of GetFavoritesPlaylistID():
      func (c *Config) GetPinDefaultPlaylist() bool {
          if c.Favorites == nil {
              return true // default: pinned
          }
          return c.Favorites.PinDefault
      }
      
    • Add SetPinDefaultPlaylist(pin bool) error method following the pattern of SetFavoritesPlaylistID():
      • Ensure c.Favorites is initialized (same nil guard pattern)
      • Set c.Favorites.PinDefault = pin
      • Call c.Save(), return error if save fails
      • Call c.emitFavoritesChanged()
      • Log the change
    • Update emitFavoritesChanged() to include "PinDefault": c.Favorites.PinDefault in the event payload map
    • In the applyDefaults() method, ensure when creating a new favorites.Config{}, PinDefault is set to true

NOTE: The Wails bindings (frontend/wailsjs/go/config/Config.js and .d.ts) are auto-generated by wails generate module. Run wails generate module after making Go changes, or if not available, manually add the binding stubs to match the pattern of existing bindings. Run go build ./... from the backend directory to verify compilation. Grep for PinDefault in backend/ to confirm it appears in both files. - favorites.Config has PinDefault bool field with TOML tag - config.Config has GetPinDefaultPlaylist() and SetPinDefaultPlaylist() methods - emitFavoritesChanged includes PinDefault in event payload - Default value is true (pin enabled) - Code compiles without errors

Task 2: Wire frontend store, controller, playlist-view sort logic, and config page toggle frontend/src/store/favorites-store.ts frontend/src/store/controllers/favorites-controller.ts frontend/src/components/playlist-view/playlist-view.ts frontend/src/components/config-page/config-page.ts frontend/wailsjs/go/config/Config.js frontend/wailsjs/go/config/Config.d.ts 1. **Wails bindings** — Add `GetPinDefaultPlaylist` and `SetPinDefaultPlaylist` to `frontend/wailsjs/go/config/Config.js` and `.d.ts` following the exact pattern of the existing exports (e.g. `GetFavoritesPlaylistID`/`SetFavoritesPlaylistID`): - In `.d.ts`: `export function GetPinDefaultPlaylist():Promise;` and `export function SetPinDefaultPlaylist(arg1:boolean):Promise;` - In `.js`: Follow the exact `window['go']['config']['Config']['MethodName']` pattern used by other exports
  1. favorites-store.ts:

    • Import GetPinDefaultPlaylist and SetPinDefaultPlaylist from @go/config/Config
    • Add private pinDefault = true; field (default true)
    • Add getPinDefault(): boolean getter
    • Add async setPinDefault(pin: boolean): Promise<void> action (same pattern as setIconStyle)
    • In loadConfig(): add GetPinDefaultPlaylist() to the Promise.all call, store result in this.pinDefault
    • In the FavoritesConfigChanged event handler: read data.PinDefault (as boolean) and store in this.pinDefault, then notify
  2. favorites-controller.ts:

    • Add get pinDefault(): boolean getter that delegates to favoritesStore.getPinDefault()
    • Add async setPinDefault(pin: boolean): Promise<void> that delegates to favoritesStore.setPinDefault(pin)
  3. playlist-view.ts — Update sortedEntries getter to pin default playlist when enabled:

    private get sortedEntries(): PlaylistEntry[] {
        const entries = this.filteredEntries;
        const dir = this.sortDirection === 'asc' ? 1 : -1;
    
        const sorted = [...entries].sort((a, b) => {
            // Pin default playlist to top when enabled
            if (this.favCtrl.pinDefault) {
                const aIsDefault = a.summary.ID === this.favCtrl.playlistId;
                const bIsDefault = b.summary.ID === this.favCtrl.playlistId;
                if (aIsDefault && !bIsDefault) return -1;
                if (!aIsDefault && bIsDefault) return 1;
            }
    
            let cmp = 0;
            switch (this.sortField) {
                // ... existing sort cases unchanged
            }
            return cmp * dir;
        });
    
        return sorted;
    }
    
  4. config-page.ts — Add a toggle in renderFavoritesSection() AFTER the existing Icon Style field:

    <config-field
        .schema=${{
            key: 'pinDefaultPlaylist',
            label: 'Pin to Top',
            description: 'Always show the default playlist first, regardless of sort order.',
            type: 'toggle' as const,
        }}
        .value=${this.favCtrl.pinDefault}
        @config-change=${this.handlePinDefaultChange}
    ></config-field>
    
    • Add handler private handlePinDefaultChange:
      private handlePinDefaultChange = (
          e: CustomEvent<ConfigFieldChangeEvent>,
      ): void => {
          const pin = Boolean(e.detail.value);
          this.favCtrl
              .setPinDefault(pin)
              .catch((err: unknown) => {
                  console.error('Failed to set pin default:', err);
              });
      };
      

IMPORTANT: Check if config-field supports type: 'toggle'. If not, check what boolean toggle type it supports (could be 'switch' or 'checkbox'). Look at the config-field component to determine the correct type string. If toggle isn't supported, use whatever boolean field type the component supports. Run npm run build (or the project's frontend build command) from the frontend directory to verify TypeScript compilation. Visually verify by launching the app that: (1) The favorites playlist appears at the top of the playlist list regardless of sort, (2) The setting toggle appears in Settings > Favorites, (3) Disabling the toggle causes the favorites playlist to sort normally. - Favorites store exposes pinDefault state with getter/setter - FavoritesController exposes pinDefault getter and setPinDefault action - sortedEntries in playlist-view pins default playlist to index 0 when pinDefault is true - Config page shows "Pin to Top" toggle in Favorites section - Toggling the setting immediately updates the playlist view (reactive via store subscription) - Setting persists across app restarts (saved to config.toml via backend) - Frontend builds without TypeScript errors

1. `go build ./...` passes (backend compiles) 2. Frontend build passes (TypeScript compiles) 3. App launches; default playlist appears pinned to top regardless of sort field/direction 4. Settings > Favorites shows "Pin to Top" toggle (default: on) 5. Disabling the toggle causes the default playlist to sort normally 6. Re-enabling the toggle immediately pins the default playlist back to the top 7. Restarting the app preserves the pin preference

<success_criteria>

  • Default playlist pinned to top of playlist view when setting enabled (default: enabled)
  • Toggle in Settings > Favorites controls the behavior
  • Setting persists in config.toml across restarts
  • All other sort functionality (field + direction) works normally for non-default playlists
  • No regressions to existing playlist features (sorting, filtering, drag-drop, context menu) </success_criteria>
After completion, create `.planning/quick/7-pin-default-playlist-to-top-of-playlist-/7-SUMMARY.md`