[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/QueryaHub/Querya-Desktop/main/docs/theme-parser-github-issues.md [Back]  [Original]

# GitHub Issues:  JSON-   theme system

: [`theme-parser-implementation-tasks.md`](theme-parser-implementation-tasks.md).  
      GitHub Issues:       issue.

## Labels

 labels:

- `theme`
- `frontend`
- `performance`
- `parser`
- `settings`
- `docs`
- `tests`
- `good first issue`     docs/fixtures/test 

## Milestones / Epics

- **Epic A  Custom theme parser core**
- **Epic B  Theme registry and caching**
- **Epic C  Preferences theme picker for 50+ themes**
- **Epic D  Built-in themes, filesystem loading, docs**
- **Epic E  Window chrome theme sync**

## Recommended order

1. TP-01  TP-07: parser core.
2. TP-08  TP-14: registry, persistence, cache.
3. TP-15  TP-20: Preferences UI and import flow.
4. TP-21  TP-24: built-in assets, filesystem folder, docs.
5. TP-25  TP-27: window chrome sync and final hardening.
6. TP-28  TP-30: QA, regression tests, release checklist.

---

## TP-01  Document Querya custom theme JSON schema

**Labels:** `theme`, `docs`, `good first issue`  
**Epic:** A  
**Depends on:** none

### Goal

Create public documentation for the new Querya custom theme JSON format (`querya.theme.v1`) before implementing the parser.

### Context

Current `docs/theme-import.md` describes VS Code theme import. The new format must be documented separately so parser behavior is clear and testable.

### Implementation

Create `docs/theme-custom-json.md` with:

- purpose of the Querya custom format;
- required root fields:
  - `schema`
  - `id`
  - `name`
  - `type`
  - `shadcn_colors`
  - `editor_colors`
- optional root fields:
  - `tokenColors`
  - `description`
  - `author`
  - `version`
- accepted `type` values: `dark`, `light`;
- accepted color formats:
  - `#RRGGBB`
  - `RRGGBB`
  - `#AARRGGBB`
  - `AARRGGBB`
  - optionally `#RGB` / `#RGBA` if supported by existing parser;
- fallback rules:
  - missing optional colors fallback to `QueryaTheme.darkDefault` / `QueryaTheme.lightDefault`;
  - invalid optional color is ignored;
  - missing required root field fails parsing;
  - broken selected theme falls back to Querya Dark on startup;
- difference between VS Code JSON/JSONC and Querya custom JSON;
- one minimal working example and one full example.

Update `docs/theme-import.md` with a short link to `docs/theme-custom-json.md`.

### Acceptance Criteria

- `docs/theme-custom-json.md` exists.
- It includes a valid copy-pastable JSON example.
- It explicitly says VS Code import remains supported.
- It documents fallback/error behavior.

### Tests

Docs only. No automated tests required.

---

## TP-02  Add custom theme JSON fixtures

**Labels:** `theme`, `tests`, `good first issue`  
**Epic:** A  
**Depends on:** TP-01

### Goal

Add stable fixtures for parser and factory tests.

### Files

- `test/fixtures/themes/querya_custom_dark.json`
- `test/fixtures/themes/querya_custom_light.json`
- `test/fixtures/themes/querya_custom_minimal.json`
- `test/fixtures/themes/querya_custom_invalid_missing_id.json`
- `test/fixtures/themes/querya_custom_invalid_color.json`
- `test/fixtures/themes/querya_custom_jsonc.jsonc`

### Implementation

Add fixtures:

- `querya_custom_dark.json`
  - full dark theme with `shadcn_colors`, `editor_colors`, and sample `tokenColors`;
- `querya_custom_light.json`
  - full light theme;
- `querya_custom_minimal.json`
  - only required root fields and a small set of colors;
- `querya_custom_invalid_missing_id.json`
  - no `id`;
- `querya_custom_invalid_color.json`
  - one optional invalid color and enough valid fields to test skip/failure policy;
- `querya_custom_jsonc.jsonc`
  - comments and trailing commas.

### Acceptance Criteria

- Fixtures are small enough to read in tests.
- Dark/light/minimal fixtures use distinct values so tests can assert mapping.
- Invalid fixtures target one failure mode each.

### Tests

No parser tests in this issue. Fixtures are consumed by later issues.

---

## TP-03  Add `QueryaThemeManifest` model

**Labels:** `theme`, `parser`  
**Epic:** A  
**Depends on:** TP-02

### Goal

Create an immutable model for Querya custom theme manifests without converting colors to Flutter `Color` yet.

### Files

- `lib/core/theme/parser/querya_theme_manifest.dart`
- `test/core/theme/parser/querya_theme_manifest_test.dart`

### Implementation

Add:

```dart
enum QueryaThemeType { dark, light }

class QueryaThemeManifest {
  const QueryaThemeManifest({
    required this.schema,
    required this.id,
    required this.name,
    required this.type,
    required this.shadcnColors,
    required this.editorColors,
    this.tokenColors = const [],
    this.description,
    this.author,
    this.version,
  });
}

class QueryaThemeManifestParseException implements Exception {
  const QueryaThemeManifestParseException(this.message);
  final String message;
}
```

Fields:

- `schema: String`
- `id: String`
- `name: String`
- `type: QueryaThemeType`
- `shadcnColors: Map`
- `editorColors: Map`
- `tokenColors: List`
- optional metadata fields.

Add `QueryaThemeManifest.fromJsonString(String raw)`.

Use existing:

- `stripJsonc`
- `TokenColorRule` / existing token color parser path where possible.

### Performance Notes

- Do not create `Color`, `QueryaTheme`, or `ThemeData` here.
- Keep maps unmodifiable.
- Parsing should be pure and synchronous for a single file; async belongs in services.

### Acceptance Criteria

- Valid dark/light fixtures parse.
- JSONC fixture parses.
- Missing required fields throw `QueryaThemeManifestParseException`.
- Unknown fields are ignored.
- Returned maps are immutable or safely copied.

### Tests

Cover:

- valid full dark;
- valid full light;
- minimal manifest;
- JSONC comments/trailing commas;
- missing `id`;
- invalid `type`;
- empty `shadcn_colors` / `editor_colors` behavior according to docs.

---

## TP-04  Add Querya theme color parser wrapper

**Labels:** `theme`, `parser`, `tests`  
**Epic:** A  
**Depends on:** TP-03

### Goal

Support Querya custom HEX formats without duplicating incompatible color parsing logic.

### Files

- `lib/core/theme/parser/color_parser.dart`
- `test/core/theme/parser/color_parser_test.dart`

### Implementation

Add:

```dart
Color parseQueryaThemeColor(String raw)
```

Behavior:

- trim whitespace;
- accept `#RRGGBB`;
- accept `RRGGBB`;
- accept `#AARRGGBB`;
- accept `AARRGGBB`;
- delegate to `parseVsCodeColor` where possible;
- throw `FormatException` for invalid input.

If current `parseVsCodeColor` already supports all formats, implement this wrapper as normalization + delegate.

### Acceptance Criteria

- Wrapper exists and is used by custom theme factory.
- No second unrelated parser implementation.
- Error messages mention the invalid value.

### Tests

Cases:

- `#1E1E1E`
- `1E1E1E`
- `#FF1E1E1E`
- `FF1E1E1E`
- lowercase hex
- invalid length
- invalid characters
- empty string

---

## TP-05  Map `shadcn_colors` to `ColorScheme`

**Labels:** `theme`, `parser`  
**Epic:** A  
**Depends on:** TP-04

### Goal

Convert custom `shadcn_colors` into `shadcn_flutter.ColorScheme` with fallback values.

### Files

- `lib/core/theme/parser/querya_theme_color_scheme.dart`
- `test/core/theme/parser/querya_theme_color_scheme_test.dart`

### Implementation

Add pure function:

```dart
ColorScheme colorSchemeFromQueryaThemeColors({
  required Map colors,
  required QueryaTheme fallback,
});
```

Map these keys:

- `background`
- `foreground`
- `card`
- `cardForeground`
- `popover`
- `popoverForeground`
- `primary`
- `primaryForeground`
- `secondary`
- `secondaryForeground`
- `muted`
- `mutedForeground`
- `accent`
- `accentForeground`
- `destructive`
- `destructiveForeground`
- `border`
- `input`
- `ring`
- `chart1`
- `chart2`
- `chart3`
- `chart4`
- `chart5`

Fallback:

- missing key -> fallback `colorScheme` value;
- invalid optional color -> fallback value;
- debug log invalid optional key if useful.

### Acceptance Criteria

- Function is pure.
- Missing optional keys preserve fallback.
- Full fixture maps distinct expected values.
- Brightness comes from fallback theme, not from colors map.

### Tests

- full map uses custom values;
- missing keys use fallback;
- invalid optional color uses fallback;
- chart colors fallback correctly.

---

## TP-06  Map `editor_colors` to `QueryaEditorTheme`

**Labels:** `theme`, `parser`  
**Epic:** A  
**Depends on:** TP-04

### Goal

Convert custom editor color tokens into `QueryaEditorTheme`.

### Files

- `lib/core/theme/parser/querya_editor_theme_from_manifest.dart`
- `test/core/theme/parser/querya_editor_theme_from_manifest_test.dart`

### Implementation

Add:

```dart
QueryaEditorTheme editorThemeFromQueryaColors({
  required Map colors,
  required QueryaEditorTheme fallback,
});
```

Support keys matching current `QueryaEditorTheme` fields. At minimum:

- `background`
- `foreground`
- `selection`
- `lineNumber`
- `bracketMatch`
- `widgetBorder`

If `QueryaEditorTheme` has additional fields, include them explicitly.

Fallback:

- missing/invalid key -> fallback field.

### Acceptance Criteria

- Full fixture changes editor background/foreground/selection.
- Minimal fixture falls back for missing fields.
- Invalid optional color does not crash.

### Tests

- full custom values;
- fallback behavior;
- invalid optional value.

---

## TP-07  Map `editor_colors` to `QueryaWorkbenchTheme`

**Labels:** `theme`, `parser`  
**Epic:** A  
**Depends on:** TP-04

### Goal

Convert workbench-related custom tokens into `QueryaWorkbenchTheme`.

### Files

- `lib/core/theme/parser/querya_workbench_theme_from_manifest.dart`
- `test/core/theme/parser/querya_workbench_theme_from_manifest_test.dart`

### Implementation

Add:

```dart
QueryaWorkbenchTheme workbenchThemeFromQueryaColors({
  required Map colors,
  required QueryaWorkbenchTheme fallback,
});
```

Support keys:

- `sidebarBackground`
- `canvas`
- `surface`
- `editorBackground`
- `mutedForeground`
- `accent`
- `onAccent`
- `borderSubtle`
- `destructive`
- `gitModified`
- `gitUntracked`

If the docs use `background`, map it deliberately:

- `background` -> `canvas` and/or `editorBackground` only if explicit in docs.

### Acceptance Criteria

- Mapping is documented in code comments or docs table.
- Missing keys use fallback.
- Invalid optional colors use fallback.

### Tests

- full custom workbench mapping;
- minimal fallback;
- invalid optional value.

---

## TP-08  Build `QueryaTheme` from custom manifest

**Labels:** `theme`, `parser`  
**Epic:** A  
**Depends on:** TP-05, TP-06, TP-07

### Goal

Provide one factory that converts `QueryaThemeManifest` into the existing app-level `QueryaTheme`.

### Files

- `lib/core/theme/parser/querya_theme_from_manifest.dart`
- `test/core/theme/parser/querya_theme_from_manifest_test.dart`

### Implementation

Add:

```dart
QueryaTheme queryaThemeFromManifest(QueryaThemeManifest manifest)
```

Algorithm:

1. Pick fallback:
   - dark -> `QueryaTheme.darkDefault`
   - light -> `QueryaTheme.lightDefault`
2. Build `ColorScheme` from `shadcn_colors`.
3. Build `QueryaEditorTheme` from `editor_colors`.
4. Build `QueryaWorkbenchTheme` from `editor_colors`.
5. Return `fallback.copyWith(...)`.
6. Preserve `tokenColors`.

### Acceptance Criteria

- Full dark fixture creates dark `QueryaTheme`.
- Full light fixture creates light `QueryaTheme`.
- `tokenColors` are preserved.
- No `ThemeData` is created.

### Tests

- dark brightness;
- light brightness;
- shadcn color mapping;
- editor/workbench mapping;
- token colors preserved;
- minimal fixture fallback.

---

## TP-09  Add typed theme load result

**Labels:** `theme`, `parser`, `error-handling`  
**Epic:** A  
**Depends on:** TP-08

### Goal

Introduce result types for loading/parsing themes so UI and startup can handle failures without exceptions leaking.

### Files

- `lib/core/theme/theme_load_result.dart`

### Implementation

Add sealed result:

```dart
sealed class ThemeLoadResult {
  const ThemeLoadResult();
}

class ThemeLoadSuccess extends ThemeLoadResult {
  const ThemeLoadSuccess({
    required this.definition,
    required this.theme,
  });
}

class ThemeLoadFailure extends ThemeLoadResult {
  const ThemeLoadFailure({
    required this.definition,
    required this.message,
    this.error,
  });
}
```

Use this result in later registry APIs.

### Acceptance Criteria

- Result can represent success/failure without throwing.
- Failure keeps enough data to show user-facing error and debug logs.

### Tests

No direct tests required unless lint coverage demands it. Later registry tests will cover usage.

---

## TP-10  Add `ThemeDefinition`

**Labels:** `theme`, `registry`  
**Epic:** B  
**Depends on:** TP-03

### Goal

Represent lightweight theme metadata for lists/pickers without full parsing.

### Files

- `lib/core/theme/theme_definition.dart`
- `test/core/theme/theme_definition_test.dart`

### Implementation

Add:

```dart
enum ThemeSource { builtin, imported, filesystem, legacyImported }
enum ThemeFormat { queryaCustom, vscode }

class ThemeDefinition {
  const ThemeDefinition({
    required this.id,
    required this.name,
    required this.source,
    required this.format,
    required this.isDark,
    this.path,
    this.lastModified,
    this.contentHash,
  });
}
```

Add helpers:

- `bool get isFileBacked`
- `String get stableCacheKey`

### Acceptance Criteria

- `ThemeDefinition` is immutable.
- `stableCacheKey` changes when `contentHash` changes.
- Does not depend on Flutter widgets.

### Tests

- file-backed vs builtin;
- cache key includes id/hash/source;
- equality if implemented.

---

## TP-11  Add theme paths helper

**Labels:** `theme`, `filesystem`  
**Epic:** B  
**Depends on:** TP-10

### Goal

Centralize app theme directories and avoid path logic scattered across services.

### Files

- `lib/core/theme/theme_paths.dart`
- `test/core/theme/theme_paths_test.dart` if path provider can be faked easily.

### Implementation

Add:

```dart
abstract final class ThemePaths {
  static Future userThemesDirectory();
  static Future importedThemesDirectory();
}
```

Rules:

- Primary user dir: app support directory + `themes`.
- Imported dir: app support directory + `themes/imported`.
- Optionally expose `legacyDotQueryaThemesDirectory()` for later `~/.querya/themes`.

### Acceptance Criteria

- Directories are not created by path getter unless method name says `ensure`.
- Separate `ensureUserThemesDirectory()` can create it.

### Tests

- If existing test support fakes path provider, assert paths.
- Otherwise cover through registry tests.

---

## TP-12  Implement filesystem theme scan

**Labels:** `theme`, `filesystem`, `performance`  
**Epic:** B  
**Depends on:** TP-10, TP-11

### Goal

Scan app support theme folder and return lightweight `ThemeDefinition` objects.

### Files

- `lib/core/theme/theme_registry_service.dart`
- `test/core/theme/theme_registry_service_test.dart`

### Implementation

Add:

```dart
class ThemeRegistryService {
  Future loadThemeDefinitions();
}
```

For `.json` and `.jsonc` files:

1. Read file async.
2. Detect format:
   - if root `schema == querya.theme.v1` -> custom;
   - otherwise try VS Code manifest.
3. Extract only metadata:
   - id
   - name
   - type/isDark
   - format
   - path
   - lastModified
   - contentHash
4. Skip broken files from list or return a disabled/error definition. Prefer disabled/error definition if UI should show it later.

### Performance Notes

- Do not construct `QueryaTheme`.
- Do not construct `ThemeData`.
- Hash file content once during scan.
- Async file IO only.

### Acceptance Criteria

- Valid custom files appear.
- Valid VS Code files appear.
- Broken file does not crash scan.
- Only `.json` / `.jsonc` are considered.

### Tests

- temp dir with 2 valid themes and 1 broken;
- stable ordering by name;
- content hash changes when file changes.

---

## TP-13  Load selected theme by definition

**Labels:** `theme`, `registry`  
**Epic:** B  
**Depends on:** TP-12, TP-09

### Goal

Given a `ThemeDefinition`, parse the full theme and return `ThemeLoadResult`.

### Files

- `lib/core/theme/theme_registry_service.dart`
- `test/core/theme/theme_registry_service_test.dart`

### Implementation

Add:

```dart
Future loadTheme(ThemeDefinition definition)
```

Behavior:

- `ThemeFormat.queryaCustom` -> `QueryaThemeManifest.fromJsonString` -> `queryaThemeFromManifest`.
- `ThemeFormat.vscode` -> existing `VsCodeThemeManifest` -> existing `queryaThemeFromVsCode`.
- failure -> `ThemeLoadFailure`.
- missing file -> `ThemeLoadFailure`.

### Acceptance Criteria

- Custom definition loads to `QueryaTheme`.
- VS Code definition still loads.
- Missing/deleted file returns failure.
- No app crash on parse failure.

### Tests

- custom success;
- VS Code success using existing fixture;
- deleted file failure;
- invalid file failure.

---

## TP-14  Add LRU cache for parsed themes

**Labels:** `theme`, `performance`, `registry`  
**Epic:** B  
**Depends on:** TP-13

### Goal

Avoid repeated file reads and parsing when switching between themes.

### Files

- `lib/core/theme/theme_registry_service.dart`
- `test/core/theme/theme_registry_cache_test.dart`

### Implementation

Inside `ThemeRegistryService`:

- cache `QueryaTheme` by `definition.stableCacheKey`;
- max entries: 12 or 20;
- on cache hit, return cached theme;
- on content hash change, key changes naturally;
- expose `clearCache()` for tests/reset.

If current project has no LRU helper, implement tiny private LRU using `LinkedHashMap`.

### Acceptance Criteria

- Loading same definition twice parses once.
- Loading changed file parses again.
- Cache evicts oldest entry after limit.

### Tests

- fake parser counter or temp file mutation;
- cache hit;
- cache invalidation by hash;
- eviction.

---

## TP-15  Persist selected theme id/path in AppSettings

**Labels:** `theme`, `storage`  
**Epic:** B  
**Depends on:** TP-10

### Goal

Persist selected registry theme across restarts without storing heavy objects.

### Files

- `lib/core/storage/app_settings.dart`
- `test/core/storage/app_settings_test.dart`

### Implementation

Add keys:

- `theme_selected_id`
- `theme_selected_source`
- `theme_selected_path`

Add methods:

```dart
Future getSelectedThemeId();
Future setSelectedThemeId(String? id);
Future getSelectedThemeSource();
Future setSelectedThemeSource(String? source);
Future getSelectedThemePath();
Future setSelectedThemePath(String? path);
```

Keep existing preset/imported settings unchanged.

### Acceptance Criteria

- Settings roundtrip.
- Clearing selected theme works.
- No SQL workspace revision bump unless existing theme settings already do that intentionally.

### Tests

- id/source/path roundtrip;
- clear values;
- existing theme preset tests still pass.

---

## TP-16  Migrate legacy imported theme into registry

**Labels:** `theme`, `migration`, `compatibility`  
**Epic:** B  
**Depends on:** TP-12, TP-15

### Goal

Users with existing imported VS Code themes should keep them after registry lands.

### Files

- `lib/core/theme/theme_registry_service.dart`
- `lib/core/theme/theme_import_service.dart`
- `lib/core/theme/theme_controller.dart`
- tests as needed.

### Implementation

During registry load:

- check existing persisted import path/name/colors;
- if found, add a `ThemeDefinition`:
  - `id: legacy-imported` or `imported`;
  - `source: legacyImported`;
  - `format: vscode`;
  - `path: stored import file`;
  - `name: importedThemeName ?? "Imported theme"`.

Do not delete old settings.

### Acceptance Criteria

- Existing `QueryaThemePreset.imported` still applies.
- Legacy imported theme appears in new picker.
- Missing legacy file falls back gracefully.

### Tests

- fake old imported path -> registry definition exists;
- selected legacy imported theme loads;
- missing old file does not crash.

---

## TP-17  Integrate registry into ThemeController load

**Labels:** `theme`, `controller`  
**Epic:** B  
**Depends on:** TP-13, TP-15, TP-16

### Goal

Make `ThemeController` aware of registry themes while preserving existing presets.

### Files

- `lib/core/theme/theme_controller.dart`
- `lib/core/theme/querya_theme_preset.dart`
- tests for theme controller.

### Implementation

Add state:

- `_availableThemes: List`
- `_selectedThemeId: String?`
- `_selectedThemePath: String?`
- `_selectedThemeLoadError: String?`

Add getters:

- `availableThemes`
- `selectedThemeId`
- `selectedThemeLoadError`

Add methods:

```dart
Future loadAvailableThemes();
Future setThemeById(String id);
Future previewThemeById(String id);
```

Behavior:

- `load()` first loads existing mode/preset.
- Then load registry definitions async.
- If stored selected id exists, try load it.
- On failure, apply Querya Dark fallback but keep error visible.

### Performance Notes

- Do not call `notifyListeners()` for every discovered file.
- Batch registry load and notify once.
- `previewThemeById` must not mutate active app theme.

### Acceptance Criteria

- Existing `setPreset()` behavior still works.
- New `setThemeById()` applies registry theme.
- Broken selected theme falls back to Querya Dark.
- `previewThemeById()` returns theme/result without notifying app listeners.

### Tests

- old presets still pass;
- select by id persists setting;
- preview does not change `activeTheme`;
- broken selected id fallback.

---

## TP-18  Add `ThemePickerButton` widget shell

**Labels:** `theme`, `settings`, `frontend`  
**Epic:** C  
**Depends on:** TP-10

### Goal

Introduce a dedicated picker UI for many themes instead of overloading a small dropdown.

### Files

- `lib/features/settings/theme_picker_button.dart`
- `test/features/settings/theme_picker_button_test.dart`

### Implementation

Create widget:

```dart
class ThemePickerButton extends StatelessWidget {
  const ThemePickerButton({
    required this.themes,
    required this.selectedThemeId,
    required this.onSelected,
    this.isLoading = false,
  });
}
```

Use:

- `MenuAnchor`;
- fixed/max popup height 300-360px;
- `Scrollbar`;
- `ListView.builder`;
- row shows:
  - name;
  - source badge;
  - dark/light icon or label.

### Acceptance Criteria

- Opens menu with 50+ fake themes without overflow.
- Uses builder list, not `Column(children: themes.map(...))`.
- Does not parse or apply theme during build.

### Tests

- pump with 60 definitions;
- open menu;
- visible rows render;
- no exception/overflow in test logs if test harness supports it;
- tap row triggers `onSelected(id)`.

---

## TP-19  Add search/filter to ThemePickerButton

**Labels:** `theme`, `settings`, `frontend`  
**Epic:** C  
**Depends on:** TP-18

### Goal

Make 50+ themes easy to navigate.

### Files

- `lib/features/settings/theme_picker_button.dart`
- `test/features/settings/theme_picker_button_test.dart`

### Implementation

Inside popup:

- small search input at top;
- local `TextEditingController`;
- filter by lowercase `name`, `id`, `source`;
- debounce not strictly required for 50 items, but avoid parsing/building themes;
- dispose controller.

### Acceptance Criteria

- Typing filters list.
- Empty result shows small message.
- Search does not call `ThemeController.setThemeById`.

### Tests

- filter by theme name;
- filter no results;
- clear input restores list.

---

## TP-20  Add safe preview card without applying theme on hover

**Labels:** `theme`, `settings`, `performance`  
**Epic:** C  
**Depends on:** TP-18, TP-17

### Goal

Optional visual preview for hovered/selected theme without rebuilding the whole app.

### Files

- `lib/features/settings/theme_preview_card.dart`
- `lib/features/settings/theme_picker_button.dart`
- tests as needed.

### Implementation

Add `ThemePreviewCard`:

- accepts `QueryaTheme` or lightweight preview colors;
- displays:
  - background;
  - surface;
  - primary/accent;
  - sample text;
  - editor background strip.

In picker:

- hover selects preview target id locally;
- debounce 100-150ms before calling `previewThemeById`;
- preview result stored in local state only;
- never call `setThemeById` on hover.

### Acceptance Criteria

- Hovering row does not change app theme.
- Preview card updates after debounce.
- Broken preview shows non-blocking error in card.

### Tests

- hover/callback does not call `onSelected`;
- preview future resolves and card updates;
- broken preview shows fallback/error.

---

## TP-21  Wire ThemePickerButton into Preferences

**Labels:** `theme`, `settings`, `frontend`  
**Epic:** C  
**Depends on:** TP-17, TP-18

### Goal

Replace or extend current Color preset dropdown with registry-backed theme selection.

### Files

- `lib/features/settings/preferences_appearance_section.dart`
- `lib/features/settings/theme_picker_button.dart`

### Implementation

In Appearance:

- keep `Theme mode`;
- replace `Color preset` row with `Theme` row using `ThemePickerButton`;
- include existing Querya Dark/Light as built-in definitions;
- show current imported/registry selected theme;
- call `ThemeController.setThemeById(id)` on select;
- keep old `Import theme` and `Reset appearance` buttons.

### Compatibility

- If registry unavailable/empty, fallback to current preset dropdown behavior or show Querya Dark/Light only.

### Acceptance Criteria

- Querya Dark/Light selectable.
- Legacy imported theme selectable if present.
- Selecting registry theme applies immediately.
- Existing reset returns to Querya Dark.

### Tests

- widget shows built-in themes;
- selecting theme calls controller hook or fake callback;
- reset remains visible;
- import button remains visible.

---

## TP-22  Add refresh themes action in Preferences

**Labels:** `theme`, `settings`, `filesystem`  
**Epic:** C  
**Depends on:** TP-17, TP-21

### Goal

Let users refresh filesystem themes without restarting the app.

### Files

- `lib/features/settings/preferences_appearance_section.dart`
- `lib/core/theme/theme_controller.dart`

### Implementation

Add button near import/reset:

- `Refresh themes`
- calls `ThemeController.loadAvailableThemes()`;
- shows small loading state;
- preserves active theme if still available;
- if active theme changed on disk, optionally reload when selected again, not immediately.

### Acceptance Criteria

- Refresh updates list.
- Broken files do not break Preferences.
- Loading state does not block whole dialog.

### Tests

- fake controller list changes after refresh;
- button disabled while refreshing.

---

## TP-23  Import custom themes into user themes directory

**Labels:** `theme`, `filesystem`, `settings`  
**Epic:** D  
**Depends on:** TP-12, TP-21

### Goal

Make `Import theme` add themes to registry instead of only overwriting one `imported.json`.

### Files

- `lib/core/theme/theme_registry_service.dart`
- `lib/core/theme/theme_import_service.dart`
- `lib/features/settings/preferences_appearance_section.dart`

### Implementation

Add:

```dart
Future importThemeFile(String sourcePath)
```

Behavior:

- detect Querya custom vs VS Code;
- validate;
- copy into app support themes directory;
- filename should be stable and safe:
  - `${id}.json` for custom;
  - slugified name for VS Code;
- avoid overwrite:
  - if same id/hash exists, reuse;
  - if same id different hash, append suffix or replace only after explicit policy;
- return new `ThemeDefinition`.

### Acceptance Criteria

- Importing custom theme adds it to picker.
- Importing VS Code theme still works.
- Multiple imported themes can coexist.
- Old single imported flow still works until fully migrated.

### Tests

- import custom;
- import VS Code;
- duplicate import same hash;
- duplicate id different content.

---

## TP-24  Add built-in theme assets

**Labels:** `theme`, `assets`, `docs`  
**Epic:** D  
**Depends on:** TP-12

### Goal

Ship built-in sample themes in release builds, not only as repository files.

### Files

- `assets/themes/`
- `pubspec.yaml`
- `lib/core/theme/theme_registry_service.dart`
- tests as feasible.

### Implementation

Move/copy curated themes to:

- `assets/themes/cyberpunk-neon.json`
- any other approved built-in themes.

Update `pubspec.yaml`:

```yaml
flutter:
  assets:
    - assets/themes/
```

Registry:

- load built-in asset manifest;
- create `ThemeDefinition(source: ThemeSource.builtin)`;
- load full theme from asset when selected.

### Acceptance Criteria

- Built-in themes show in picker in release/profile builds.
- App does not depend on repo `themes/samples/` path at runtime.
- Existing `themes/samples/` can remain for docs/manual testing.

### Tests

- Asset loading if test environment supports bundle.
- Otherwise unit-test parsing with same file content.

---

## TP-25  Add user theme folder docs and open-folder affordance

**Labels:** `theme`, `docs`, `settings`  
**Epic:** D  
**Depends on:** TP-11, TP-21

### Goal

Make filesystem themes discoverable.

### Files

- `docs/theme-custom-json.md`
- `docs/theme-import.md`
- `lib/features/settings/preferences_appearance_section.dart`

### Implementation

Docs:

- show actual app support path behavior;
- mention accepted extensions;
- mention refresh/restart.

UI:

- show hint text:
  - "Themes are loaded from app support themes folder."
- optional button:
  - `Open themes folder`
  - can be follow-up if cross-platform opening helper does not exist.

### Acceptance Criteria

- User can understand where to put downloaded themes.
- UI does not promise watcher/live reload if not implemented.

### Tests

Docs only unless adding button.

---

## TP-26  Sync custom window chrome with active theme

**Labels:** `theme`, `frontend`, `bitsdojo`  
**Epic:** E  
**Depends on:** TP-17

### Goal

Ensure title bar / window controls follow custom theme background/canvas.

### Files

- `lib/main.dart`
- `lib/features/main_screen/main_screen.dart`
- any title bar/window button widgets.

### Implementation

Find where `bitsdojo_window` title area and window controls are styled.

Use:

- `QueryaThemeScope.of(context).workbench.canvas`
- `QueryaThemeScope.of(context).workbench.surface`
- `QueryaThemeScope.of(context).workbench.mutedForeground`

Avoid:

- direct singleton reads inside deep widgets when inherited theme is available;
- app-wide notify on hover.

### Acceptance Criteria

- Switching theme updates title bar background.
- Window buttons remain readable.
- Hover states use theme tokens.

### Tests

- Widget test if title bar is testable.
- Otherwise manual smoke checklist in PR body:
  - dark;
  - light;
  - custom dark;
  - custom light.

---

## TP-27  Startup fallback for missing/broken selected theme

**Labels:** `theme`, `error-handling`, `stability`  
**Epic:** E  
**Depends on:** TP-17

### Goal

Prevent broken custom themes from breaking app startup.

### Files

- `lib/core/theme/theme_controller.dart`
- tests for controller.

### Implementation

On `ThemeController.load()`:

1. Read selected theme id/path.
2. Try registry load.
3. If failure:
   - set active theme to Querya Dark;
   - keep `selectedThemeLoadError`;
   - do not crash;
   - do not delete user setting automatically.
4. Preferences can show:
   - "Selected theme failed to load. Using Querya Dark."

### Acceptance Criteria

- Missing selected file starts app with Querya Dark.
- Invalid selected file starts app with Querya Dark.
- Error visible in Preferences.
- User can choose another theme and clear error.

### Tests

- missing file;
- invalid file;
- subsequent valid selection clears error.

---

## TP-28  Performance test: 50+ themes in picker

**Labels:** `theme`, `performance`, `tests`  
**Epic:** E  
**Depends on:** TP-18, TP-21

### Goal

Prevent regression where many themes make Preferences slow or overflow.

### Files

- `test/features/settings/theme_picker_button_test.dart`
- maybe `test/features/settings/preferences_appearance_section_test.dart`

### Implementation

Create 60 fake `ThemeDefinition` objects.

Test:

- picker opens;
- only visible subset is built if measurable;
- no overflow exception;
- scroll to bottom works;
- select last item works.

If exact build count is hard to assert, assert behavior and no exceptions.

### Acceptance Criteria

- Test fails if picker uses unbounded `Column` and overflows.
- Test passes with `ListView.builder`.

---

## TP-29  End-to-end theme import test

**Labels:** `theme`, `tests`, `integration`  
**Epic:** E  
**Depends on:** TP-21, TP-23

### Goal

Cover the full import/select path with a fake filesystem theme.

### Files

- `test/features/settings/theme_import_flow_test.dart`

### Implementation

Use fake/temp app support path if project test support allows it.

Flow:

1. Put custom JSON in temp source.
2. Import through service/controller.
3. Registry list includes it.
4. Select it.
5. `ThemeController.activeTheme` changes expected token.
6. Restart-like reload preserves selection.

### Acceptance Criteria

- Custom theme can be imported, selected, and restored.
- Test does not depend on real user home directory.

---

## TP-30  Release docs and QA checklist for custom themes

**Labels:** `theme`, `docs`, `qa`  
**Epic:** E  
**Depends on:** TP-01 through TP-29

### Goal

Prepare the feature for release and manual verification.

### Files

- `docs/theme-custom-json.md`
- `docs/theme-import.md`
- `docs/release-checklist.md`
- `CHANGELOG.md` when release branch is prepared.

### Implementation

Add QA checklist:

- import valid custom dark;
- import valid custom light;
- import VS Code JSONC;
- select among 50+ fake themes or test pack;
- restart app and verify selected theme persists;
- delete selected theme file and restart;
- verify fallback + Preferences error;
- verify title bar/window controls colors;
- verify SQL/JSON highlighting still uses tokenColors.

### Acceptance Criteria

- Release checklist includes custom theme scenarios.
- Docs include troubleshooting for invalid colors/missing fields.
- CHANGELOG entry can be written from completed issues.

---

## Optional follow-up issues

These are intentionally out of the first implementation pass (**shipped in 0.4.2**).
**Shipped in 0.4.3** ([milestone](https://github.com/QueryaHub/Querya-Desktop/milestone/2), epic **#159**)  see [planned-0.4.3.md](planned-0.4.3.md).

### TP-F1  File watcher for user themes folder ([#160](https://github.com/QueryaHub/Querya-Desktop/issues/160))

Use a filesystem watcher to auto-refresh themes after files are added/removed. Keep as follow-up because watchers differ by OS and can introduce lifecycle bugs.

### TP-F2  Theme marketplace metadata ([#161](https://github.com/QueryaHub/Querya-Desktop/issues/161))

Support metadata fields like preview image, tags, homepage, license. Useful only after custom theme format is stable.

### TP-F3  Visual theme editor ([#162](https://github.com/QueryaHub/Querya-Desktop/issues/162))

Allow editing theme colors in Preferences and export to `querya.theme.v1`. This is larger than parser/import support.

### TP-F4  Remote theme install ([#163](https://github.com/QueryaHub/Querya-Desktop/issues/163))

Install theme from URL. Requires network, trust/security decisions, and probably signature/checksum policy.

## Master checklist

- [x] TP-01 docs schema
- [x] TP-02 fixtures
- [x] TP-03 manifest model
- [x] TP-04 color parser wrapper
- [x] TP-05 shadcn color scheme mapping
- [x] TP-06 editor theme mapping
- [x] TP-07 workbench theme mapping
- [x] TP-08 QueryaTheme factory
- [x] TP-09 load result types
- [x] TP-10 ThemeDefinition
- [x] TP-11 theme paths
- [x] TP-12 filesystem scan
- [x] TP-13 load selected definition
- [x] TP-14 parsed theme cache
- [x] TP-15 AppSettings selected theme
- [x] TP-16 legacy imported migration
- [x] TP-17 ThemeController registry integration
- [x] TP-18 ThemePickerButton shell
- [x] TP-19 picker search/filter
- [x] TP-20 safe preview card
- [x] TP-21 Preferences integration
- [x] TP-22 refresh themes action
- [x] TP-23 multi-theme import
- [x] TP-24 built-in theme assets
- [x] TP-25 user theme folder docs
- [x] TP-26 window chrome sync
- [x] TP-27 startup fallback
- [x] TP-28 50+ themes performance test
- [x] TP-29 end-to-end import test
- [x] TP-30 release QA docs

Web Proxy Viewer  |  New URL  |  Original Page