|
54 | 54 | "service": BASE_ENTRY_FIELDS, |
55 | 55 | "shortcut": BASE_ENTRY_FIELDS, |
56 | 56 | "launcher_provider": BASE_ENTRY_FIELDS |
57 | | - | {"prefix", "glyph", "include_in_global_search", "debounce_ms", "setting"}, |
| 57 | + | {"prefix", "glyph", "include_in_global_search", "debounce_ms", "setting", "category"}, |
58 | 58 | } |
| 59 | +CATEGORY_FIELDS = {"label", "glyph"} |
59 | 60 | SETTING_FIELDS = { |
60 | 61 | "key", |
61 | 62 | "type", |
@@ -286,6 +287,39 @@ def validate_launcher_fields(self, manifest_path: Path, context: str, entry: dic |
286 | 287 | "debounce_ms must be a non-negative integer", |
287 | 288 | ) |
288 | 289 |
|
| 290 | + if "category" in entry: |
| 291 | + self.validate_launcher_categories(manifest_path, f"{context}.category", entry["category"]) |
| 292 | + |
| 293 | + def validate_launcher_categories(self, manifest_path: Path, context: str, categories: Any) -> None: |
| 294 | + if not isinstance(categories, list): |
| 295 | + self.add_context_error(manifest_path, context, "must be an array of tables") |
| 296 | + return |
| 297 | + |
| 298 | + if not categories: |
| 299 | + self.add_context_error(manifest_path, context, "must not be empty") |
| 300 | + |
| 301 | + seen_labels: set[str] = set() |
| 302 | + for index, category in enumerate(categories): |
| 303 | + category_context = f"{context}[{index}]" |
| 304 | + if not isinstance(category, dict): |
| 305 | + self.add_context_error(manifest_path, category_context, "must be a table") |
| 306 | + continue |
| 307 | + |
| 308 | + unknown = sorted(set(category) - CATEGORY_FIELDS) |
| 309 | + for field in unknown: |
| 310 | + self.add_context_error(manifest_path, category_context, f"unknown field '{field}'") |
| 311 | + |
| 312 | + label = category.get("label") |
| 313 | + if not is_non_empty_string(label): |
| 314 | + self.add_context_error(manifest_path, category_context, "label must be a non-empty string") |
| 315 | + elif label in seen_labels: |
| 316 | + self.add_context_error(manifest_path, category_context, f"duplicate category label '{label}'") |
| 317 | + else: |
| 318 | + seen_labels.add(label) |
| 319 | + |
| 320 | + if not is_non_empty_string(category.get("glyph")): |
| 321 | + self.add_context_error(manifest_path, category_context, "glyph must be a non-empty string") |
| 322 | + |
289 | 323 | def validate_panel_fields(self, manifest_path: Path, context: str, entry: dict[str, Any]) -> None: |
290 | 324 | for field in ("width", "height"): |
291 | 325 | if field not in entry: |
|
0 commit comments