@64mb/freelens-power 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +3 -3
  2. package/out/main/common/resource-search.js +61 -0
  3. package/out/main/common/resource-search.js.map +1 -0
  4. package/out/main/common/store/monitoring-preferences-store.js +5 -0
  5. package/out/main/common/store/monitoring-preferences-store.js.map +1 -1
  6. package/out/main/index.js +2 -0
  7. package/out/main/index.js.map +1 -1
  8. package/out/main/resource-search-database.js +209 -0
  9. package/out/main/resource-search-database.js.map +1 -0
  10. package/out/main/resource-search-worker.js +56 -0
  11. package/out/main/resource-search-worker.js.map +1 -0
  12. package/out/main/resource-search.js +155 -0
  13. package/out/main/resource-search.js.map +1 -0
  14. package/out/renderer/appearance.js +1 -1
  15. package/out/renderer/appearance.js.map +1 -1
  16. package/out/renderer/gateway-relationships-client.js +107 -0
  17. package/out/renderer/gateway-relationships-client.js.map +1 -0
  18. package/out/renderer/preferences/monitoring-preferences.js +10 -0
  19. package/out/renderer/preferences/monitoring-preferences.js.map +1 -1
  20. package/out/renderer/preferences/preferences.js +3 -3
  21. package/out/renderer/preferences/preferences.js.map +1 -1
  22. package/out/renderer/resource-relationships.css.js +1 -1
  23. package/out/renderer/resource-relationships.css.js.map +1 -1
  24. package/out/renderer/resource-relationships.js +46 -8
  25. package/out/renderer/resource-relationships.js.map +1 -1
  26. package/out/renderer/resource-search-client.js +116 -0
  27. package/out/renderer/resource-search-client.js.map +1 -0
  28. package/out/renderer/resource-search-discovery.js +79 -0
  29. package/out/renderer/resource-search-discovery.js.map +1 -0
  30. package/out/renderer/resource-search-model.js +2 -1
  31. package/out/renderer/resource-search-model.js.map +1 -1
  32. package/out/renderer/resource-search-routing.js +78 -0
  33. package/out/renderer/resource-search-routing.js.map +1 -0
  34. package/out/renderer/resource-search-sync.js +199 -0
  35. package/out/renderer/resource-search-sync.js.map +1 -0
  36. package/out/renderer/resource-search.js +279 -54
  37. package/out/renderer/resource-search.js.map +1 -1
  38. package/out/renderer/src/common/gateway-relationships.js +122 -0
  39. package/out/renderer/src/common/gateway-relationships.js.map +1 -0
  40. package/out/renderer/src/common/resource-relationships.js +8 -0
  41. package/out/renderer/src/common/resource-relationships.js.map +1 -1
  42. package/out/renderer/src/common/resource-search.js +6 -0
  43. package/out/renderer/src/common/resource-search.js.map +1 -0
  44. package/out/renderer/src/common/store/monitoring-preferences-store.js +5 -0
  45. package/out/renderer/src/common/store/monitoring-preferences-store.js.map +1 -1
  46. package/package.json +6 -2
@@ -1,13 +1,18 @@
1
1
  const require_extensions = require("./_virtual/_global-external_@freelensapp/extensions.js");
2
2
  const require__global_external_mobx = require("./_virtual/_global-external_mobx.js");
3
+ const require__global_external_mobx_react = require("./_virtual/_global-external_mobx-react.js");
4
+ const require_monitoring_preferences_store = require("./src/common/store/monitoring-preferences-store.js");
3
5
  const require__global_external_react = require("./_virtual/_global-external_react.js");
4
6
  const require__global_external_react_dom = require("./_virtual/_global-external_react-dom.js");
5
7
  const require_jsx_runtime = require("./_virtual/_global-external_react/jsx-runtime.js");
6
8
  const require_ai_icon = require("./ai-icon.js");
7
9
  const require_ai_diagnostic_handoff = require("./ai-diagnostic-handoff.js");
8
10
  const require_diagnostic_explain_button = require("./diagnostic-explain-button.js");
11
+ const require_resource_search_client = require("./resource-search-client.js");
12
+ const require_resource_search_routing = require("./resource-search-routing.js");
9
13
  const require_resource_search_fields = require("./resource-search-fields.js");
10
14
  const require_resource_search_model = require("./resource-search-model.js");
15
+ const require_resource_search_sync = require("./resource-search-sync.js");
11
16
  //#region src/renderer/resource-search.tsx
12
17
  var OPEN = "power-resource-search";
13
18
  var activeId = () => require_extensions.Renderer.Catalog.activeCluster.get()?.metadata.uid;
@@ -74,16 +79,16 @@ var inventory = () => [
74
79
  ]
75
80
  ];
76
81
  var icons = new Map(inventory().map(([kind, , icon]) => [kind, icon]));
77
- function project(kind, objects) {
78
- return objects.slice(0, 500).flatMap((object) => object.metadata.name ? [{
79
- kind,
80
- name: object.metadata.name,
81
- namespace: object.metadata.namespace,
82
- labels: Object.fromEntries(Object.entries(object.metadata.labels ?? {}).filter((entry) => typeof entry[1] === "string")),
83
- path: object.selfLink,
84
- fields: require_resource_search_fields.projectSearchFields(kind, object)
85
- }] : []);
86
- }
82
+ require__global_external_mobx_react.observer(() => {
83
+ if (!require_monitoring_preferences_store.MonitoringPreferencesStore.getInstanceOrCreate().resourceSearchEnabled) return null;
84
+ return /* @__PURE__ */ require_jsx_runtime.jsx("button", {
85
+ type: "button",
86
+ title: "Search resources (double Shift)",
87
+ "aria-label": "Search resources",
88
+ onClick: () => window.dispatchEvent(new Event(OPEN)),
89
+ children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, { material: "search" })
90
+ });
91
+ });
87
92
  function SearchSnippet({ field, query }) {
88
93
  if (!field) return null;
89
94
  const token = require_resource_search_model.queryTokens(query).find((token) => field.text.toLocaleLowerCase().includes(token));
@@ -104,9 +109,13 @@ function SearchSnippet({ field, query }) {
104
109
  }
105
110
  function SearchDialog({ close, clusterId }) {
106
111
  const [query, setQuery] = require__global_external_react.useState("");
112
+ const [knownNamespace, setKnownNamespace] = require__global_external_react.useState("");
113
+ const [kindFilter, setKindFilter] = require__global_external_react.useState("");
114
+ const [namespaceFilter, setNamespaceFilter] = require__global_external_react.useState("");
107
115
  const [resources, setResources] = require__global_external_react.useState([]);
108
116
  const [loading, setLoading] = require__global_external_react.useState(true);
109
117
  const [failed, setFailed] = require__global_external_react.useState(0);
118
+ const [cachedCount, setCachedCount] = require__global_external_react.useState();
110
119
  const [selectedPath, setSelectedPath] = require__global_external_react.useState();
111
120
  const [error, setError] = require__global_external_react.useState("");
112
121
  const dialog = require__global_external_react.useRef(null);
@@ -114,55 +123,73 @@ function SearchDialog({ close, clusterId }) {
114
123
  const searchIndex = require__global_external_react.useMemo(() => require_resource_search_model.createResourceSearchIndex(resources), [resources]);
115
124
  const matches = searchIndex.search(query);
116
125
  const selectedResource = matches.find((resource) => resource.path === selectedPath) ?? matches[0];
117
- const results = matches.slice(0, 100);
126
+ const results = matches;
118
127
  if (selectedResource && !results.includes(selectedResource)) results[results.length - 1] = selectedResource;
119
128
  const selected = results.findIndex((resource) => resource.path === selectedResource?.path);
120
129
  require__global_external_react.useEffect(() => {
121
130
  if (selectedPath !== void 0 && selectedPath !== selectedResource?.path) setSelectedPath(void 0);
122
131
  }, [selectedPath, selectedResource?.path]);
132
+ const [coverage, setCoverage] = require__global_external_react.useState(currentCoverage);
133
+ const [limit, setLimit] = require__global_external_react.useState(100);
134
+ const [hasMore, setHasMore] = require__global_external_react.useState(false);
123
135
  require__global_external_react.useEffect(() => {
124
136
  const previous = document.activeElement;
125
137
  input.current?.focus();
126
- const controller = new AbortController();
127
- const timeout = window.setTimeout(() => controller.abort(), 15e3);
138
+ const update = () => setCoverage({ ...currentCoverage });
139
+ window.addEventListener("power-search-coverage", update);
140
+ return () => {
141
+ window.removeEventListener("power-search-coverage", update);
142
+ if (previous?.isConnected) previous.focus();
143
+ };
144
+ }, []);
145
+ require__global_external_react.useEffect(() => {
128
146
  let live = true;
129
- const sources = inventory();
130
- const parts = sources.map(([kind, store]) => project(kind, store.items));
131
- setResources(parts.flat());
132
- let next = 0;
133
- const worker = async () => {
134
- while (next < sources.length && live && activeId() === clusterId) {
135
- const index = next++;
136
- const [kind, store] = sources[index];
137
- try {
138
- const objects = await store.api.list({
139
- namespace: "",
140
- reqInit: { signal: controller.signal }
141
- }, { limit: 500 });
142
- if (!live || activeId() !== clusterId) return;
143
- parts[index] = project(kind, objects ?? []);
144
- setResources(parts.flat());
145
- } catch {
146
- if (live && activeId() === clusterId) setFailed((count) => count + 1);
147
+ const client = require_resource_search_client.createResourceSearchClient(clusterId, require_resource_search_client.searchContextId());
148
+ const timer = window.setTimeout(() => {
149
+ client.query(query, limit, {
150
+ ...kindFilter ? { kind: coverage.kinds?.find((kind) => kind.toLocaleLowerCase() === kindFilter.toLocaleLowerCase()) ?? kindFilter } : {},
151
+ ...namespaceFilter ? { namespace: namespaceFilter === "(cluster)" ? "" : namespaceFilter } : {}
152
+ }).then((result) => {
153
+ if (!live || activeId() !== clusterId) return;
154
+ setResources(result.documents);
155
+ setHasMore(result.hasMore);
156
+ setLoading(false);
157
+ setFailed(0);
158
+ client.status?.().then((status) => {
159
+ if (live) setCachedCount(status.count);
160
+ }).catch(() => {});
161
+ }).catch(() => {
162
+ if (live) {
163
+ setFailed(1);
164
+ setLoading(false);
147
165
  }
148
- }
149
- };
150
- Promise.all([worker(), worker()]).then(() => {
151
- if (live) setLoading(false);
152
- });
166
+ });
167
+ }, 100);
153
168
  return () => {
154
169
  live = false;
155
- window.clearTimeout(timeout);
156
- controller.abort();
157
- if (previous?.isConnected) previous.focus();
170
+ window.clearTimeout(timer);
158
171
  };
159
- }, [clusterId]);
172
+ }, [
173
+ clusterId,
174
+ query,
175
+ limit,
176
+ kindFilter,
177
+ namespaceFilter,
178
+ coverage.scanned,
179
+ coverage.indexing
180
+ ]);
160
181
  require__global_external_react.useEffect(() => {
161
182
  document.getElementById(`power-resource-result-${selected}`)?.scrollIntoView?.({ block: "nearest" });
162
183
  }, [selected, selectedResource?.path]);
184
+ const clearCache = () => {
185
+ require_resource_search_client.createResourceSearchClient(clusterId, require_resource_search_client.searchContextId()).clear().then(() => {
186
+ setResources([]);
187
+ restartSearchSync();
188
+ }).catch(() => setError("Could not clear the local search cache. Try again."));
189
+ };
163
190
  const open = (resource) => {
164
191
  if (activeId() !== clusterId) return;
165
- Promise.resolve().then(() => require_extensions.Renderer.Navigation.showDetails(resource.path)).then(() => {
192
+ Promise.resolve().then(() => require_resource_search_routing.openSearchResource(resource)).then(() => {
166
193
  if (activeId() === clusterId) close();
167
194
  }).catch(() => setError("Could not open this resource. Try again or refresh the inventory."));
168
195
  };
@@ -186,7 +213,7 @@ function SearchDialog({ close, clusterId }) {
186
213
  close();
187
214
  }
188
215
  if (event.key === "Tab") {
189
- const controls = Array.from(dialog.current?.querySelectorAll("input, button:not(:disabled)") ?? []);
216
+ const controls = Array.from(dialog.current?.querySelectorAll("input, button:not(:disabled), select") ?? []);
190
217
  const index = controls.indexOf(document.activeElement);
191
218
  if (event.shiftKey && index <= 0) {
192
219
  event.preventDefault();
@@ -211,11 +238,23 @@ function SearchDialog({ close, clusterId }) {
211
238
  material: "search",
212
239
  size: 18
213
240
  }), "Search resources"]
214
- }), /* @__PURE__ */ require_jsx_runtime.jsx("button", {
215
- type: "button",
216
- "aria-label": "Close resource search",
217
- onClick: close,
218
- children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, { material: "close" })
241
+ }), /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
242
+ style: {
243
+ display: "flex",
244
+ gap: 4
245
+ },
246
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx("button", {
247
+ type: "button",
248
+ "aria-label": "Refresh resource inventory",
249
+ title: "Refresh resource inventory",
250
+ onClick: () => restartSearchSync(),
251
+ children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, { material: "refresh" })
252
+ }), /* @__PURE__ */ require_jsx_runtime.jsx("button", {
253
+ type: "button",
254
+ "aria-label": "Close resource search",
255
+ onClick: close,
256
+ children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, { material: "close" })
257
+ })]
219
258
  })]
220
259
  }),
221
260
  /* @__PURE__ */ require_jsx_runtime.jsx("input", {
@@ -240,14 +279,68 @@ function SearchDialog({ close, clusterId }) {
240
279
  }
241
280
  }
242
281
  }),
282
+ /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
283
+ className: "power-resource-search-filters",
284
+ style: {
285
+ display: "flex",
286
+ gap: 8,
287
+ flexWrap: "wrap"
288
+ },
289
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx("input", {
290
+ list: "power-search-kinds",
291
+ "aria-label": "Filter by exact resource kind",
292
+ placeholder: "Kind (all types)",
293
+ value: kindFilter,
294
+ maxLength: 253,
295
+ onChange: (event) => {
296
+ setKindFilter(event.target.value);
297
+ setSelectedPath(void 0);
298
+ }
299
+ }), /* @__PURE__ */ require_jsx_runtime.jsx("input", {
300
+ list: "power-search-namespaces",
301
+ "aria-label": "Filter by exact namespace",
302
+ placeholder: "Namespace (all)",
303
+ value: namespaceFilter,
304
+ maxLength: 253,
305
+ onChange: (event) => {
306
+ setNamespaceFilter(event.target.value);
307
+ setSelectedPath(void 0);
308
+ }
309
+ })]
310
+ }),
311
+ /* @__PURE__ */ require_jsx_runtime.jsx("datalist", {
312
+ id: "power-search-kinds",
313
+ children: (coverage.kinds ?? []).map((kind) => /* @__PURE__ */ require_jsx_runtime.jsx("option", { value: kind }, kind))
314
+ }),
315
+ /* @__PURE__ */ require_jsx_runtime.jsxs("datalist", {
316
+ id: "power-search-namespaces",
317
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx("option", {
318
+ value: "(cluster)",
319
+ children: "Cluster scoped"
320
+ }), (coverage.namespaces ?? []).map((namespace) => /* @__PURE__ */ require_jsx_runtime.jsx("option", { value: namespace }, namespace))]
321
+ }),
243
322
  /* @__PURE__ */ require_jsx_runtime.jsxs("p", {
244
323
  role: "status",
245
324
  children: [
246
- loading ? "Loading supported kinds; cached results are available. " : "Supported kinds loaded. ",
247
- "Partial inventory: up to 500 objects per kind, subject to permissions. Secrets, ConfigMaps and custom resources are excluded. Full-text index covers safe metadata, images, selectors and status only.",
248
- failed > 0 && ` ${failed} kinds unavailable; cached results retained.`
325
+ coverage.indexing ? `Indexing ${coverage.scanned}/${coverage.discovered} types` : coverage.completedAt ? `Updated ${new Date(coverage.completedAt).toLocaleTimeString([], {
326
+ hour: "2-digit",
327
+ minute: "2-digit"
328
+ })}` : "Cached inventory",
329
+ cachedCount !== void 0 && ` · ${cachedCount.toLocaleString()} resources`,
330
+ coverage.denied > 0 && ` · ${coverage.denied} restricted scopes`,
331
+ coverage.unavailable + coverage.unsupported > 0 && ` · ${coverage.unavailable + coverage.unsupported} APIs unavailable`,
332
+ failed > 0 && " · Search cache unavailable"
249
333
  ]
250
334
  }),
335
+ (coverage.message || coverage.denied > 0 || coverage.unavailable + coverage.unsupported > 0) && /* @__PURE__ */ require_jsx_runtime.jsxs("details", { children: [/* @__PURE__ */ require_jsx_runtime.jsx("summary", { children: "Inventory coverage" }), /* @__PURE__ */ require_jsx_runtime.jsx("p", { children: coverage.message ?? "Results include the resources your current permissions and available APIs allow." })] }),
336
+ coverage.storageFull && /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
337
+ role: "alert",
338
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx("p", { children: "Local search storage is full. Cached results are partial; indexing is paused." }), /* @__PURE__ */ require_jsx_runtime.jsx("button", {
339
+ type: "button",
340
+ onClick: clearCache,
341
+ children: "Clear local search cache"
342
+ })]
343
+ }),
251
344
  error && /* @__PURE__ */ require_jsx_runtime.jsx("p", {
252
345
  role: "alert",
253
346
  children: error
@@ -289,7 +382,7 @@ function SearchDialog({ close, clusterId }) {
289
382
  require_ai_diagnostic_handoff.explainDiagnosticFinding({
290
383
  clusterId,
291
384
  title: `${resource.kind}: ${resource.name}`,
292
- summary: `Resource selected from search. Kind: ${resource.kind}; namespace: ${resource.namespace ?? "cluster scoped"}; name: ${resource.name}. No live diagnostic data was collected.`,
385
+ summary: `Resource selected from search. API group: ${resource.group || "core"}; version: ${resource.version}; resource: ${resource.resource}; kind: ${resource.kind}; namespace: ${resource.namespace ?? "cluster scoped"}; name: ${resource.name}; UID: ${resource.uid ?? "unknown"}; path: ${resource.path}. No live diagnostic data was collected.`,
293
386
  resource: {
294
387
  kind: resource.kind,
295
388
  name: resource.name,
@@ -301,7 +394,43 @@ function SearchDialog({ close, clusterId }) {
301
394
  })]
302
395
  }, resource.path)), !results.length && /* @__PURE__ */ require_jsx_runtime.jsx("p", { children: loading ? "Searching…" : "No matching resources in the available inventory" })]
303
396
  }),
304
- matches.length > 100 && /* @__PURE__ */ require_jsx_runtime.jsx("p", { children: "Showing 100 matches. Refine your search." }),
397
+ hasMore && /* @__PURE__ */ require_jsx_runtime.jsx("button", {
398
+ type: "button",
399
+ disabled: limit >= 1e3,
400
+ onClick: () => setLimit((value) => Math.min(1e3, value + 100)),
401
+ children: "Show more matches"
402
+ }),
403
+ /* @__PURE__ */ require_jsx_runtime.jsxs("details", { children: [
404
+ /* @__PURE__ */ require_jsx_runtime.jsx("summary", { children: "Search options" }),
405
+ /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
406
+ style: {
407
+ display: "flex",
408
+ gap: 8,
409
+ flexWrap: "wrap"
410
+ },
411
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx("input", {
412
+ "aria-label": "Known namespace for restricted access",
413
+ placeholder: "Known namespace",
414
+ value: knownNamespace,
415
+ maxLength: 63,
416
+ onChange: (event) => setKnownNamespace(event.target.value)
417
+ }), /* @__PURE__ */ require_jsx_runtime.jsx("button", {
418
+ type: "button",
419
+ disabled: !/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/.test(knownNamespace),
420
+ onClick: () => {
421
+ extraNamespaces.add(knownNamespace);
422
+ restartSearchSync();
423
+ },
424
+ children: "Index namespace"
425
+ })]
426
+ }),
427
+ hasMore && limit >= 1e3 && /* @__PURE__ */ require_jsx_runtime.jsx("p", { children: "Showing up to 1000 candidates. Refine the name, type or namespace to continue." }),
428
+ !coverage.storageFull && /* @__PURE__ */ require_jsx_runtime.jsx("button", {
429
+ type: "button",
430
+ onClick: clearCache,
431
+ children: "Clear local search cache"
432
+ })
433
+ ] }),
305
434
  /* @__PURE__ */ require_jsx_runtime.jsx("p", {
306
435
  className: "power-resource-search-shortcuts",
307
436
  children: "↑↓ Navigate · Enter Open · Esc Close"
@@ -310,10 +439,98 @@ function SearchDialog({ close, clusterId }) {
310
439
  })
311
440
  });
312
441
  }
442
+ var currentCoverage = {
443
+ scanned: 0,
444
+ discovered: 0,
445
+ denied: 0,
446
+ unavailable: 0,
447
+ unsupported: 0,
448
+ indexing: true
449
+ };
450
+ var restartSearchSync = () => {};
451
+ var extraNamespaces = /* @__PURE__ */ new Set();
313
452
  function startResourceSearch() {
453
+ const preferences = require_monitoring_preferences_store.MonitoringPreferencesStore.getInstanceOrCreate();
454
+ let stop = preferences.resourceSearchEnabled ? startEnabledResourceSearch() : void 0;
455
+ const dispose = require__global_external_mobx.reaction(() => preferences.resourceSearchEnabled, (enabled) => {
456
+ stop?.();
457
+ stop = enabled ? startEnabledResourceSearch() : void 0;
458
+ });
459
+ return () => {
460
+ dispose();
461
+ stop?.();
462
+ stop = void 0;
463
+ };
464
+ }
465
+ function startEnabledResourceSearch() {
466
+ let syncAbort;
467
+ const sync = () => {
468
+ syncAbort?.abort();
469
+ require_resource_search_routing.closeSearchResourceFallback();
470
+ const clusterId = activeId();
471
+ if (!clusterId || window.top === window) return;
472
+ const phase = require_extensions.Renderer.Catalog.activeCluster.get()?.status?.phase;
473
+ if (phase && phase !== "connected") {
474
+ currentCoverage = {
475
+ ...currentCoverage,
476
+ indexing: false,
477
+ message: "Cluster disconnected; cached results may be stale"
478
+ };
479
+ window.dispatchEvent(new Event("power-search-coverage"));
480
+ return;
481
+ }
482
+ const controller = new AbortController();
483
+ syncAbort = controller;
484
+ const client = require_resource_search_client.createResourceSearchClient(clusterId, require_resource_search_client.searchContextId());
485
+ const liveStores = new Map(inventory().map(([kind, store]) => [kind, store]));
486
+ const liveObjects = /* @__PURE__ */ new Map();
487
+ const sink = {
488
+ ...client,
489
+ append: (scope, scanId, documents) => client.append(scope, scanId, documents.map((item) => {
490
+ const store = liveStores.get(item.kind);
491
+ if (!store) return item;
492
+ let objects = liveObjects.get(item.kind);
493
+ if (!objects) {
494
+ objects = new Map(store.items.map((object) => [object.selfLink, object]));
495
+ liveObjects.set(item.kind, objects);
496
+ }
497
+ const object = objects.get(item.path);
498
+ return object ? {
499
+ ...item,
500
+ fields: require_resource_search_fields.projectSearchFields(item.kind, object)
501
+ } : item;
502
+ }))
503
+ };
504
+ let wasIndexing = false;
505
+ require_resource_search_sync.createSearchSynchronizer({
506
+ transport: require_resource_search_client.createResourceSearchTransport(clusterId, controller.signal),
507
+ sink,
508
+ signal: controller.signal,
509
+ namespaces: () => [.../* @__PURE__ */ new Set([...require_extensions.Renderer.Catalog.activeCluster.get()?.spec?.accessibleNamespaces ?? [], ...extraNamespaces])],
510
+ onStatus: (status) => {
511
+ if (!controller.signal.aborted) {
512
+ if (status.indexing && !wasIndexing) liveObjects.clear();
513
+ wasIndexing = status.indexing;
514
+ currentCoverage = status;
515
+ window.dispatchEvent(new Event("power-search-coverage"));
516
+ }
517
+ }
518
+ }).run().catch(() => {
519
+ if (!controller.signal.aborted) {
520
+ currentCoverage = {
521
+ ...currentCoverage,
522
+ indexing: false,
523
+ unavailable: currentCoverage.unavailable + 1
524
+ };
525
+ window.dispatchEvent(new Event("power-search-coverage"));
526
+ }
527
+ });
528
+ };
529
+ restartSearchSync = sync;
530
+ sync();
314
531
  const container = document.createElement("div");
315
532
  const style = document.createElement("style");
316
- style.textContent = `.power-resource-search-backdrop{position:fixed;inset:0;z-index:10000;background:#0007;display:flex;align-items:flex-start;justify-content:center;padding:12vh 20px}.power-resource-search-dialog{width:680px;max-width:100%;max-height:75vh;display:flex;flex-direction:column;padding:20px;gap:12px;background:var(--mainBackground,#222);color:var(--textColorPrimary,#eee);border:1px solid var(--borderColor,#555);border-radius:8px;box-shadow:0 16px 60px #0008}.power-resource-search-heading{display:flex;align-items:center;justify-content:space-between}.power-resource-search-dialog input{padding:12px;font-size:16px;background:var(--inputControlBackground,#333);color:inherit;border:1px solid var(--borderColor,#666);border-radius:4px}.power-resource-search-dialog p{margin:0;font-size:12px;color:var(--textColorSecondary,#bbb)}.power-resource-search-results{overflow:auto;min-height:60px}.power-resource-search-row{display:flex;align-items:center;gap:12px;padding:4px;border-radius:4px}.power-resource-search-row{border-left:3px solid transparent}.power-resource-search-row.selected{background:var(--tableSelectedBackground,#394b66);border-left-color:var(--primary,#668fff)}.power-resource-search-row.selected .Icon{color:var(--primary,#8daaff)}.power-resource-search-row:hover{background:var(--tableHoveredBackground,#ffffff0b)}.power-resource-search-dialog button:hover:not(:disabled){background:#ffffff12;border-radius:4px}.power-resource-search-row>button:last-child{border:1px solid var(--borderColor,#555);border-radius:4px}.power-resource-search-shortcuts{padding-top:8px;border-top:1px solid var(--borderColor,#555)}.power-resource-search-dialog button{min-width:32px;min-height:32px;padding:6px;color:inherit;background:transparent;border:0;cursor:pointer}.power-resource-search-dialog button:disabled{opacity:.45;cursor:default}.power-resource-search-open{display:flex;align-items:center;gap:12px;flex:1;text-align:left;min-width:0}.power-resource-search-open span{min-width:0}.power-resource-search-open strong,.power-resource-search-open small{display:block;overflow-wrap:anywhere}.power-resource-search-open small{opacity:.7}.power-resource-search-row>button.power-resource-search-ai{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:34px;height:34px;min-width:34px;padding:0;background:var(--contentColor,#292d32);border:1px solid var(--borderColor,#666)}.power-resource-search-row.selected>button.power-resource-search-ai{background:var(--mainBackground,#202326);border-color:color-mix(in srgb,var(--textColorPrimary,#ddd) 55%,var(--borderColor,#666));box-shadow:0 1px 3px #0002}.power-resource-search-row.selected>button.power-resource-search-ai .Icon{color:var(--textColorAccent,#eee)}.power-resource-search-row>button.power-resource-search-ai:hover:not(:disabled){background:var(--contentColor,#30343a);border-color:var(--primary,#8daaff);color:var(--primary,#8daaff)}.power-resource-search-row>button.power-resource-search-ai:hover:not(:disabled) .Icon{color:inherit}.power-resource-search-dialog button:focus-visible{outline:2px solid var(--primary,#668fff)}`;
533
+ style.textContent = `.power-resource-search-backdrop{position:fixed;inset:0;z-index:10000;background:#0007;display:flex;align-items:flex-start;justify-content:center;padding:8vh 20px}.power-resource-search-dialog{width:680px;max-width:100%;max-height:75vh;display:flex;flex-direction:column;padding:20px;gap:12px;background:var(--mainBackground,#222);color:var(--textColorPrimary,#eee);border:1px solid var(--borderColor,#555);border-radius:8px;box-shadow:0 16px 60px #0008}.power-resource-search-heading{display:flex;align-items:center;justify-content:space-between}.power-resource-search-filters input{flex:1;min-width:180px}.power-resource-search-dialog input{padding:12px;font-size:16px;background:var(--inputControlBackground,#333);color:inherit;border:1px solid var(--borderColor,#666);border-radius:4px}.power-resource-search-dialog p{margin:0;font-size:12px;color:var(--textColorSecondary,#bbb)}.power-resource-search-results{overflow:auto;min-height:180px;flex:1}.power-resource-search-row{display:flex;align-items:center;gap:12px;padding:4px;border-radius:4px}.power-resource-search-row{border-left:3px solid transparent}.power-resource-search-row.selected{background:color-mix(in srgb,var(--primary,#668fff) 16%,var(--mainBackground,#222));color:var(--tableSelectedRowColor,var(--textColorPrimary,#eee));border-left-color:var(--primary,#668fff)}.power-resource-search-row.selected .Icon{color:var(--primary,#8daaff)}.power-resource-search-row:hover{background:var(--tableBgcSelected,var(--contentColor,#292d32))}.power-resource-search-dialog button:hover:not(:disabled){background:#ffffff12;border-radius:4px}.power-resource-search-row>button:last-child{border:1px solid var(--borderColor,#555);border-radius:4px}.power-resource-search-shortcuts{padding-top:8px;border-top:1px solid var(--borderColor,#555)}.power-resource-search-dialog button{min-width:32px;min-height:32px;padding:6px;color:inherit;background:transparent;border:0;cursor:pointer}.power-resource-search-dialog button:disabled{opacity:.45;cursor:default}.power-resource-search-open{display:flex;align-items:center;gap:12px;flex:1;text-align:left;min-width:0}.power-resource-search-open span{min-width:0}.power-resource-search-open strong,.power-resource-search-open small{display:block;overflow-wrap:anywhere}.power-resource-search-open small{opacity:.7}.power-resource-search-row>button.power-resource-search-ai{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:34px;height:34px;min-width:34px;padding:0;background:var(--contentColor,#292d32);border:1px solid var(--borderColor,#666)}.power-resource-search-row.selected>button.power-resource-search-ai{background:var(--mainBackground,#202326);border-color:color-mix(in srgb,var(--textColorPrimary,#ddd) 55%,var(--borderColor,#666));box-shadow:0 1px 3px #0002}.power-resource-search-row.selected>button.power-resource-search-ai .Icon{color:var(--textColorAccent,#eee)}.power-resource-search-row>button.power-resource-search-ai:hover:not(:disabled){background:var(--contentColor,#30343a);border-color:var(--primary,#8daaff);color:var(--primary,#8daaff)}.power-resource-search-row>button.power-resource-search-ai:hover:not(:disabled) .Icon{color:inherit}.power-resource-search-dialog button:focus-visible{outline:2px solid var(--primary,#668fff)}`;
317
534
  document.head.append(style);
318
535
  document.body.append(container);
319
536
  let visible = false;
@@ -347,9 +564,17 @@ function startResourceSearch() {
347
564
  window.addEventListener("keydown", key);
348
565
  window.addEventListener("keyup", key);
349
566
  window.addEventListener(OPEN, show);
350
- const dispose = require__global_external_mobx.reaction(activeId, close);
567
+ const dispose = require__global_external_mobx.reaction(() => `${require_resource_search_client.searchContextId()}/${require_extensions.Renderer.Catalog.activeCluster.get()?.status?.phase}`, () => {
568
+ close();
569
+ extraNamespaces.clear();
570
+ sync();
571
+ });
351
572
  return () => {
352
573
  close();
574
+ syncAbort?.abort();
575
+ require_resource_search_routing.closeSearchResourceFallback();
576
+ restartSearchSync = () => {};
577
+ extraNamespaces.clear();
353
578
  dispose();
354
579
  window.removeEventListener("blur", resetShift);
355
580
  window.removeEventListener("keydown", key);
@@ -1 +1 @@
1
- {"version":3,"file":"resource-search.js","names":[],"sources":["../../src/renderer/resource-search.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { reaction } from \"mobx\";\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport ReactDOM from \"react-dom\";\nimport { explainDiagnosticFinding } from \"./ai-diagnostic-handoff\";\nimport { AiIcon } from \"./ai-icon\";\nimport { DiagnosticExplainButton } from \"./diagnostic-explain-button\";\nimport { projectSearchFields } from \"./resource-search-fields\";\nimport {\n createDoubleShift,\n createResourceSearchIndex,\n queryTokens,\n type SearchResource,\n} from \"./resource-search-model\";\n\nconst OPEN = \"power-resource-search\";\nconst activeId = () => Renderer.Catalog.activeCluster.get()?.metadata.uid;\nconst inventory = () =>\n [\n [\"Pod\", Renderer.K8sApi.podsStore, \"view_in_ar\"],\n [\"Node\", Renderer.K8sApi.nodesStore, \"dns\"],\n [\"Deployment\", Renderer.K8sApi.deploymentStore, \"layers\"],\n [\"StatefulSet\", Renderer.K8sApi.statefulSetStore, \"layers\"],\n [\"DaemonSet\", Renderer.K8sApi.daemonSetStore, \"layers\"],\n [\"ReplicaSet\", Renderer.K8sApi.replicaSetStore, \"layers\"],\n [\"Job\", Renderer.K8sApi.jobStore, \"work\"],\n [\"CronJob\", Renderer.K8sApi.cronJobStore, \"schedule\"],\n [\"Service\", Renderer.K8sApi.serviceStore, \"settings_ethernet\"],\n [\"Ingress\", Renderer.K8sApi.ingressStore, \"alt_route\"],\n [\"Namespace\", Renderer.K8sApi.namespaceStore, \"folder\"],\n [\"HorizontalPodAutoscaler\", Renderer.K8sApi.hpaStore, \"swap_vert\"],\n ] as const;\nconst icons = new Map(inventory().map(([kind, , icon]) => [kind as string, icon]));\nfunction project(\n kind: string,\n objects: {\n metadata: { name?: string; namespace?: string; labels?: Partial<Record<string, string>> };\n selfLink: string;\n }[],\n): SearchResource[] {\n return objects.slice(0, 500).flatMap((object) =>\n object.metadata.name\n ? [\n {\n kind,\n name: object.metadata.name,\n namespace: object.metadata.namespace,\n labels: Object.fromEntries(\n Object.entries(object.metadata.labels ?? {}).filter(\n (entry): entry is [string, string] => typeof entry[1] === \"string\",\n ),\n ),\n path: object.selfLink,\n fields: projectSearchFields(kind, object),\n },\n ]\n : [],\n );\n}\n\nexport function ResourceSearchLauncher() {\n return (\n <button\n type=\"button\"\n title=\"Search resources (double Shift)\"\n aria-label=\"Search resources\"\n onClick={() => window.dispatchEvent(new Event(OPEN))}\n >\n <AiIcon material=\"search\" />\n </button>\n );\n}\n\nfunction SearchSnippet({ field, query }: { field?: { label: string; text: string }; query: string }) {\n if (!field) return null;\n const token = queryTokens(query).find((token) => field.text.toLocaleLowerCase().includes(token));\n const offset = token ? field.text.toLocaleLowerCase().indexOf(token) : -1;\n return (\n <small className=\"power-resource-search-snippet\">\n {field.label}:{\" \"}\n {offset < 0 || !token ? (\n field.text\n ) : (\n <>\n {field.text.slice(0, offset)}\n <mark>{field.text.slice(offset, offset + token.length)}</mark>\n {field.text.slice(offset + token.length)}\n </>\n )}\n </small>\n );\n}\n\nfunction SearchDialog({ close, clusterId }: { close: () => void; clusterId: string }) {\n const [query, setQuery] = useState(\"\");\n const [resources, setResources] = useState<SearchResource[]>([]);\n const [loading, setLoading] = useState(true);\n const [failed, setFailed] = useState(0);\n const [selectedPath, setSelectedPath] = useState<string>();\n const [error, setError] = useState(\"\");\n const dialog = useRef<HTMLDivElement>(null);\n const input = useRef<HTMLInputElement>(null);\n const searchIndex = useMemo(() => createResourceSearchIndex(resources), [resources]);\n const matches = searchIndex.search(query);\n const selectedResource = matches.find((resource) => resource.path === selectedPath) ?? matches[0];\n const results = matches.slice(0, 100);\n if (selectedResource && !results.includes(selectedResource)) results[results.length - 1] = selectedResource;\n const selected = results.findIndex((resource) => resource.path === selectedResource?.path);\n useEffect(() => {\n if (selectedPath !== undefined && selectedPath !== selectedResource?.path) setSelectedPath(undefined);\n }, [selectedPath, selectedResource?.path]);\n useEffect(() => {\n const previous = document.activeElement as HTMLElement | null;\n input.current?.focus();\n const controller = new AbortController();\n const timeout = window.setTimeout(() => controller.abort(), 15000);\n let live = true;\n const sources = inventory();\n const parts = sources.map(([kind, store]) => project(kind, store.items));\n setResources(parts.flat());\n let next = 0;\n const worker = async () => {\n while (next < sources.length && live && activeId() === clusterId) {\n const index = next++;\n const [kind, store] = sources[index];\n try {\n const objects = await store.api.list(\n { namespace: \"\", reqInit: { signal: controller.signal } },\n { limit: 500 },\n );\n if (!live || activeId() !== clusterId) return;\n parts[index] = project(kind, objects ?? []);\n setResources(parts.flat());\n } catch {\n if (live && activeId() === clusterId) setFailed((count) => count + 1);\n }\n }\n };\n void Promise.all([worker(), worker()]).then(() => {\n if (live) setLoading(false);\n });\n return () => {\n live = false;\n window.clearTimeout(timeout);\n controller.abort();\n if (previous?.isConnected) previous.focus();\n };\n }, [clusterId]);\n useEffect(() => {\n document.getElementById(`power-resource-result-${selected}`)?.scrollIntoView?.({ block: \"nearest\" });\n }, [selected, selectedResource?.path]);\n const open = (resource: SearchResource) => {\n if (activeId() !== clusterId) return;\n void Promise.resolve()\n .then(() => Renderer.Navigation.showDetails(resource.path))\n .then(() => {\n if (activeId() === clusterId) close();\n })\n .catch(() => setError(\"Could not open this resource. Try again or refresh the inventory.\"));\n };\n return (\n <div\n className=\"power-resource-search-backdrop\"\n data-freelens-power-ai-overlay=\"\"\n onClick={(event) => event.stopPropagation()}\n onMouseDown={(event) => {\n event.stopPropagation();\n if (event.target === event.currentTarget) close();\n }}\n >\n <div\n ref={dialog}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"power-resource-search-title\"\n className=\"power-resource-search-dialog\"\n onKeyDown={(event) => {\n if (event.key === \"Escape\") {\n event.stopPropagation();\n close();\n }\n if (event.key === \"Tab\") {\n const controls = Array.from(\n dialog.current?.querySelectorAll<HTMLElement>(\"input, button:not(:disabled)\") ?? [],\n );\n const index = controls.indexOf(document.activeElement as HTMLElement);\n if (event.shiftKey && index <= 0) {\n event.preventDefault();\n controls.at(-1)?.focus();\n } else if (!event.shiftKey && index === controls.length - 1) {\n event.preventDefault();\n controls[0]?.focus();\n }\n }\n }}\n >\n <div className=\"power-resource-search-heading\">\n <strong id=\"power-resource-search-title\" style={{ display: \"flex\", alignItems: \"center\", gap: 8 }}>\n <AiIcon material=\"search\" size={18} />\n Search resources\n </strong>\n <button type=\"button\" aria-label=\"Close resource search\" onClick={close}>\n <AiIcon material=\"close\" />\n </button>\n </div>\n <input\n ref={input}\n aria-label=\"Resource name, namespace, kind, label or indexed text\"\n aria-controls=\"power-resource-search-results\"\n placeholder=\"Name, namespace, label, image, condition…\"\n maxLength={300}\n value={query}\n onChange={(event) => {\n setQuery(event.target.value);\n setSelectedPath(undefined);\n }}\n onKeyDown={(event) => {\n if (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n event.preventDefault();\n const next = results.length\n ? (selected + (event.key === \"ArrowDown\" ? 1 : -1) + results.length) % results.length\n : 0;\n setSelectedPath(results[next]?.path);\n }\n if (event.key === \"Enter\" && !event.nativeEvent.isComposing && results[selected]) {\n event.preventDefault();\n open(results[selected]);\n }\n }}\n />\n <p role=\"status\">\n {loading ? \"Loading supported kinds; cached results are available. \" : \"Supported kinds loaded. \"}Partial\n inventory: up to 500 objects per kind, subject to permissions. Secrets, ConfigMaps and custom resources are\n excluded. Full-text index covers safe metadata, images, selectors and status only.\n {failed > 0 && ` ${failed} kinds unavailable; cached results retained.`}\n </p>\n {error && <p role=\"alert\">{error}</p>}\n <div\n id=\"power-resource-search-results\"\n role=\"list\"\n aria-label=\"Matching resources\"\n className=\"power-resource-search-results\"\n >\n {results.map((resource, index) => (\n <div\n key={resource.path}\n id={`power-resource-result-${index}`}\n role=\"listitem\"\n className={`power-resource-search-row ${selected === index ? \"selected\" : \"\"}`}\n >\n <button className=\"power-resource-search-open\" type=\"button\" onClick={() => open(resource)}>\n <AiIcon material={icons.get(resource.kind) ?? \"view_in_ar\"} />\n <span>\n <strong>{resource.name}</strong>\n <small>\n {resource.kind} · {resource.namespace ?? \"cluster scoped\"}\n </small>\n {searchIndex.nameParts(resource) && <small>Name parts</small>}\n {searchIndex.similarName(resource) && <small>Similar name</small>}\n <SearchSnippet field={searchIndex.snippet(resource, query)} query={query} />\n </span>\n </button>\n <DiagnosticExplainButton\n className=\"power-resource-search-ai\"\n aria-label={`Ask AI about ${resource.kind} ${resource.name}`}\n title=\"Ask AI about this resource\"\n onClick={() => {\n if (activeId() !== clusterId) return;\n close();\n explainDiagnosticFinding({\n clusterId,\n title: `${resource.kind}: ${resource.name}`,\n summary: `Resource selected from search. Kind: ${resource.kind}; namespace: ${resource.namespace ?? \"cluster scoped\"}; name: ${resource.name}. No live diagnostic data was collected.`,\n resource: { kind: resource.kind, name: resource.name, namespace: resource.namespace },\n });\n }}\n >\n <AiIcon material=\"auto_awesome\" />\n </DiagnosticExplainButton>\n </div>\n ))}\n {!results.length && <p>{loading ? \"Searching…\" : \"No matching resources in the available inventory\"}</p>}\n </div>\n {matches.length > 100 && <p>Showing 100 matches. Refine your search.</p>}\n <p className=\"power-resource-search-shortcuts\">↑↓ Navigate · Enter Open · Esc Close</p>\n </div>\n </div>\n );\n}\n\nexport function startResourceSearch(): () => void {\n const container = document.createElement(\"div\");\n const style = document.createElement(\"style\");\n style.textContent = `.power-resource-search-backdrop{position:fixed;inset:0;z-index:10000;background:#0007;display:flex;align-items:flex-start;justify-content:center;padding:12vh 20px}.power-resource-search-dialog{width:680px;max-width:100%;max-height:75vh;display:flex;flex-direction:column;padding:20px;gap:12px;background:var(--mainBackground,#222);color:var(--textColorPrimary,#eee);border:1px solid var(--borderColor,#555);border-radius:8px;box-shadow:0 16px 60px #0008}.power-resource-search-heading{display:flex;align-items:center;justify-content:space-between}.power-resource-search-dialog input{padding:12px;font-size:16px;background:var(--inputControlBackground,#333);color:inherit;border:1px solid var(--borderColor,#666);border-radius:4px}.power-resource-search-dialog p{margin:0;font-size:12px;color:var(--textColorSecondary,#bbb)}.power-resource-search-results{overflow:auto;min-height:60px}.power-resource-search-row{display:flex;align-items:center;gap:12px;padding:4px;border-radius:4px}.power-resource-search-row{border-left:3px solid transparent}.power-resource-search-row.selected{background:var(--tableSelectedBackground,#394b66);border-left-color:var(--primary,#668fff)}.power-resource-search-row.selected .Icon{color:var(--primary,#8daaff)}.power-resource-search-row:hover{background:var(--tableHoveredBackground,#ffffff0b)}.power-resource-search-dialog button:hover:not(:disabled){background:#ffffff12;border-radius:4px}.power-resource-search-row>button:last-child{border:1px solid var(--borderColor,#555);border-radius:4px}.power-resource-search-shortcuts{padding-top:8px;border-top:1px solid var(--borderColor,#555)}.power-resource-search-dialog button{min-width:32px;min-height:32px;padding:6px;color:inherit;background:transparent;border:0;cursor:pointer}.power-resource-search-dialog button:disabled{opacity:.45;cursor:default}.power-resource-search-open{display:flex;align-items:center;gap:12px;flex:1;text-align:left;min-width:0}.power-resource-search-open span{min-width:0}.power-resource-search-open strong,.power-resource-search-open small{display:block;overflow-wrap:anywhere}.power-resource-search-open small{opacity:.7}.power-resource-search-row>button.power-resource-search-ai{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:34px;height:34px;min-width:34px;padding:0;background:var(--contentColor,#292d32);border:1px solid var(--borderColor,#666)}.power-resource-search-row.selected>button.power-resource-search-ai{background:var(--mainBackground,#202326);border-color:color-mix(in srgb,var(--textColorPrimary,#ddd) 55%,var(--borderColor,#666));box-shadow:0 1px 3px #0002}.power-resource-search-row.selected>button.power-resource-search-ai .Icon{color:var(--textColorAccent,#eee)}.power-resource-search-row>button.power-resource-search-ai:hover:not(:disabled){background:var(--contentColor,#30343a);border-color:var(--primary,#8daaff);color:var(--primary,#8daaff)}.power-resource-search-row>button.power-resource-search-ai:hover:not(:disabled) .Icon{color:inherit}.power-resource-search-dialog button:focus-visible{outline:2px solid var(--primary,#668fff)}`;\n document.head.append(style);\n document.body.append(container);\n let visible = false;\n const close = () => {\n if (visible) {\n visible = false;\n ReactDOM.unmountComponentAtNode(container);\n }\n };\n const show = () => {\n const clusterId = activeId();\n if (\n window.top === window ||\n !clusterId ||\n visible ||\n document.querySelector('[aria-modal=\"true\"], .Dialog.is-opened')\n )\n return;\n visible = true;\n ReactDOM.render(<SearchDialog close={close} clusterId={clusterId} />, container);\n };\n let shift = createDoubleShift();\n const resetShift = () => {\n shift = createDoubleShift();\n };\n const key = (event: KeyboardEvent) => {\n if (event.isComposing || event.key === \"Process\") {\n resetShift();\n return;\n }\n if (shift(event, performance.now())) show();\n };\n window.addEventListener(\"blur\", resetShift);\n window.addEventListener(\"keydown\", key);\n window.addEventListener(\"keyup\", key);\n window.addEventListener(OPEN, show);\n const dispose = reaction(activeId, close);\n return () => {\n close();\n dispose();\n window.removeEventListener(\"blur\", resetShift);\n window.removeEventListener(\"keydown\", key);\n window.removeEventListener(\"keyup\", key);\n window.removeEventListener(OPEN, show);\n container.remove();\n style.remove();\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"resource-search.js","names":[],"sources":["../../src/renderer/resource-search.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { reaction } from \"mobx\";\nimport { observer } from \"mobx-react\";\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport ReactDOM from \"react-dom\";\nimport { MonitoringPreferencesStore } from \"../common/store/monitoring-preferences-store\";\nimport { explainDiagnosticFinding } from \"./ai-diagnostic-handoff\";\nimport { AiIcon } from \"./ai-icon\";\nimport { DiagnosticExplainButton } from \"./diagnostic-explain-button\";\nimport { createResourceSearchClient, createResourceSearchTransport, searchContextId } from \"./resource-search-client\";\nimport { projectSearchFields } from \"./resource-search-fields\";\nimport {\n createDoubleShift,\n createResourceSearchIndex,\n queryTokens,\n type SearchResource,\n} from \"./resource-search-model\";\nimport { closeSearchResourceFallback, openSearchResource } from \"./resource-search-routing\";\nimport { createSearchSynchronizer, type SearchCoverage } from \"./resource-search-sync\";\n\nimport type { SearchDocument } from \"../common/resource-search\";\n\nconst OPEN = \"power-resource-search\";\nconst activeId = () => Renderer.Catalog.activeCluster.get()?.metadata.uid;\nconst inventory = () =>\n [\n [\"Pod\", Renderer.K8sApi.podsStore, \"view_in_ar\"],\n [\"Node\", Renderer.K8sApi.nodesStore, \"dns\"],\n [\"Deployment\", Renderer.K8sApi.deploymentStore, \"layers\"],\n [\"StatefulSet\", Renderer.K8sApi.statefulSetStore, \"layers\"],\n [\"DaemonSet\", Renderer.K8sApi.daemonSetStore, \"layers\"],\n [\"ReplicaSet\", Renderer.K8sApi.replicaSetStore, \"layers\"],\n [\"Job\", Renderer.K8sApi.jobStore, \"work\"],\n [\"CronJob\", Renderer.K8sApi.cronJobStore, \"schedule\"],\n [\"Service\", Renderer.K8sApi.serviceStore, \"settings_ethernet\"],\n [\"Ingress\", Renderer.K8sApi.ingressStore, \"alt_route\"],\n [\"Namespace\", Renderer.K8sApi.namespaceStore, \"folder\"],\n [\"HorizontalPodAutoscaler\", Renderer.K8sApi.hpaStore, \"swap_vert\"],\n ] as const;\nconst icons = new Map(inventory().map(([kind, , icon]) => [kind as string, icon]));\n\nexport const ResourceSearchLauncher = observer(() => {\n if (!MonitoringPreferencesStore.getInstanceOrCreate<MonitoringPreferencesStore>().resourceSearchEnabled) return null;\n return (\n <button\n type=\"button\"\n title=\"Search resources (double Shift)\"\n aria-label=\"Search resources\"\n onClick={() => window.dispatchEvent(new Event(OPEN))}\n >\n <AiIcon material=\"search\" />\n </button>\n );\n});\n\nfunction SearchSnippet({ field, query }: { field?: { label: string; text: string }; query: string }) {\n if (!field) return null;\n const token = queryTokens(query).find((token) => field.text.toLocaleLowerCase().includes(token));\n const offset = token ? field.text.toLocaleLowerCase().indexOf(token) : -1;\n return (\n <small className=\"power-resource-search-snippet\">\n {field.label}:{\" \"}\n {offset < 0 || !token ? (\n field.text\n ) : (\n <>\n {field.text.slice(0, offset)}\n <mark>{field.text.slice(offset, offset + token.length)}</mark>\n {field.text.slice(offset + token.length)}\n </>\n )}\n </small>\n );\n}\n\nfunction SearchDialog({ close, clusterId }: { close: () => void; clusterId: string }) {\n const [query, setQuery] = useState(\"\");\n const [knownNamespace, setKnownNamespace] = useState(\"\");\n const [kindFilter, setKindFilter] = useState(\"\");\n const [namespaceFilter, setNamespaceFilter] = useState(\"\");\n const [resources, setResources] = useState<SearchResource[]>([]);\n const [loading, setLoading] = useState(true);\n const [failed, setFailed] = useState(0);\n const [cachedCount, setCachedCount] = useState<number>();\n const [selectedPath, setSelectedPath] = useState<string>();\n const [error, setError] = useState(\"\");\n const dialog = useRef<HTMLDivElement>(null);\n const input = useRef<HTMLInputElement>(null);\n const searchIndex = useMemo(() => createResourceSearchIndex(resources), [resources]);\n const matches = searchIndex.search(query);\n const selectedResource = matches.find((resource) => resource.path === selectedPath) ?? matches[0];\n const results = matches;\n if (selectedResource && !results.includes(selectedResource)) results[results.length - 1] = selectedResource;\n const selected = results.findIndex((resource) => resource.path === selectedResource?.path);\n useEffect(() => {\n if (selectedPath !== undefined && selectedPath !== selectedResource?.path) setSelectedPath(undefined);\n }, [selectedPath, selectedResource?.path]);\n const [coverage, setCoverage] = useState<SearchCoverage>(currentCoverage);\n const [limit, setLimit] = useState(100);\n const [hasMore, setHasMore] = useState(false);\n useEffect(() => {\n const previous = document.activeElement as HTMLElement | null;\n input.current?.focus();\n const update = () => setCoverage({ ...currentCoverage });\n window.addEventListener(\"power-search-coverage\", update);\n return () => {\n window.removeEventListener(\"power-search-coverage\", update);\n if (previous?.isConnected) previous.focus();\n };\n }, []);\n useEffect(() => {\n let live = true;\n const client = createResourceSearchClient(clusterId, searchContextId());\n const timer = window.setTimeout(() => {\n void client\n .query(query, limit, {\n ...(kindFilter\n ? {\n kind:\n coverage.kinds?.find((kind) => kind.toLocaleLowerCase() === kindFilter.toLocaleLowerCase()) ??\n kindFilter,\n }\n : {}),\n ...(namespaceFilter ? { namespace: namespaceFilter === \"(cluster)\" ? \"\" : namespaceFilter } : {}),\n })\n .then((result) => {\n if (!live || activeId() !== clusterId) return;\n setResources(result.documents);\n setHasMore(result.hasMore);\n setLoading(false);\n setFailed(0);\n void client\n .status?.()\n .then((status) => {\n if (live) setCachedCount(status.count);\n })\n .catch(() => {});\n })\n .catch(() => {\n if (live) {\n setFailed(1);\n setLoading(false);\n }\n });\n }, 100);\n return () => {\n live = false;\n window.clearTimeout(timer);\n };\n }, [clusterId, query, limit, kindFilter, namespaceFilter, coverage.scanned, coverage.indexing]);\n useEffect(() => {\n document.getElementById(`power-resource-result-${selected}`)?.scrollIntoView?.({ block: \"nearest\" });\n }, [selected, selectedResource?.path]);\n const clearCache = () => {\n void createResourceSearchClient(clusterId, searchContextId())\n .clear()\n .then(() => {\n setResources([]);\n restartSearchSync();\n })\n .catch(() => setError(\"Could not clear the local search cache. Try again.\"));\n };\n const open = (resource: SearchResource) => {\n if (activeId() !== clusterId) return;\n void Promise.resolve()\n .then(() => openSearchResource(resource as SearchDocument))\n .then(() => {\n if (activeId() === clusterId) close();\n })\n .catch(() => setError(\"Could not open this resource. Try again or refresh the inventory.\"));\n };\n return (\n <div\n className=\"power-resource-search-backdrop\"\n data-freelens-power-ai-overlay=\"\"\n onClick={(event) => event.stopPropagation()}\n onMouseDown={(event) => {\n event.stopPropagation();\n if (event.target === event.currentTarget) close();\n }}\n >\n <div\n ref={dialog}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"power-resource-search-title\"\n className=\"power-resource-search-dialog\"\n onKeyDown={(event) => {\n if (event.key === \"Escape\") {\n event.stopPropagation();\n close();\n }\n if (event.key === \"Tab\") {\n const controls = Array.from(\n dialog.current?.querySelectorAll<HTMLElement>(\"input, button:not(:disabled), select\") ?? [],\n );\n const index = controls.indexOf(document.activeElement as HTMLElement);\n if (event.shiftKey && index <= 0) {\n event.preventDefault();\n controls.at(-1)?.focus();\n } else if (!event.shiftKey && index === controls.length - 1) {\n event.preventDefault();\n controls[0]?.focus();\n }\n }\n }}\n >\n <div className=\"power-resource-search-heading\">\n <strong id=\"power-resource-search-title\" style={{ display: \"flex\", alignItems: \"center\", gap: 8 }}>\n <AiIcon material=\"search\" size={18} />\n Search resources\n </strong>\n <div style={{ display: \"flex\", gap: 4 }}>\n <button\n type=\"button\"\n aria-label=\"Refresh resource inventory\"\n title=\"Refresh resource inventory\"\n onClick={() => restartSearchSync()}\n >\n <AiIcon material=\"refresh\" />\n </button>\n <button type=\"button\" aria-label=\"Close resource search\" onClick={close}>\n <AiIcon material=\"close\" />\n </button>\n </div>\n </div>\n <input\n ref={input}\n aria-label=\"Resource name, namespace, kind, label or indexed text\"\n aria-controls=\"power-resource-search-results\"\n placeholder=\"Name, namespace, label, image, condition…\"\n maxLength={300}\n value={query}\n onChange={(event) => {\n setQuery(event.target.value);\n setSelectedPath(undefined);\n }}\n onKeyDown={(event) => {\n if (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n event.preventDefault();\n const next = results.length\n ? (selected + (event.key === \"ArrowDown\" ? 1 : -1) + results.length) % results.length\n : 0;\n setSelectedPath(results[next]?.path);\n }\n if (event.key === \"Enter\" && !event.nativeEvent.isComposing && results[selected]) {\n event.preventDefault();\n open(results[selected]);\n }\n }}\n />\n <div className=\"power-resource-search-filters\" style={{ display: \"flex\", gap: 8, flexWrap: \"wrap\" }}>\n <input\n list=\"power-search-kinds\"\n aria-label=\"Filter by exact resource kind\"\n placeholder=\"Kind (all types)\"\n value={kindFilter}\n maxLength={253}\n onChange={(event) => {\n setKindFilter(event.target.value);\n setSelectedPath(undefined);\n }}\n />\n <input\n list=\"power-search-namespaces\"\n aria-label=\"Filter by exact namespace\"\n placeholder=\"Namespace (all)\"\n value={namespaceFilter}\n maxLength={253}\n onChange={(event) => {\n setNamespaceFilter(event.target.value);\n setSelectedPath(undefined);\n }}\n />\n </div>\n <datalist id=\"power-search-kinds\">\n {(coverage.kinds ?? []).map((kind) => (\n <option key={kind} value={kind} />\n ))}\n </datalist>\n <datalist id=\"power-search-namespaces\">\n <option value=\"(cluster)\">Cluster scoped</option>\n {(coverage.namespaces ?? []).map((namespace) => (\n <option key={namespace} value={namespace} />\n ))}\n </datalist>\n <p role=\"status\">\n {coverage.indexing\n ? `Indexing ${coverage.scanned}/${coverage.discovered} types`\n : coverage.completedAt\n ? `Updated ${new Date(coverage.completedAt).toLocaleTimeString([], { hour: \"2-digit\", minute: \"2-digit\" })}`\n : \"Cached inventory\"}\n {cachedCount !== undefined && ` · ${cachedCount.toLocaleString()} resources`}\n {coverage.denied > 0 && ` · ${coverage.denied} restricted scopes`}\n {coverage.unavailable + coverage.unsupported > 0 &&\n ` · ${coverage.unavailable + coverage.unsupported} APIs unavailable`}\n {failed > 0 && \" · Search cache unavailable\"}\n </p>\n {(coverage.message || coverage.denied > 0 || coverage.unavailable + coverage.unsupported > 0) && (\n <details>\n <summary>Inventory coverage</summary>\n <p>\n {coverage.message ?? \"Results include the resources your current permissions and available APIs allow.\"}\n </p>\n </details>\n )}\n {coverage.storageFull && (\n <div role=\"alert\">\n <p>Local search storage is full. Cached results are partial; indexing is paused.</p>\n <button type=\"button\" onClick={clearCache}>\n Clear local search cache\n </button>\n </div>\n )}\n {error && <p role=\"alert\">{error}</p>}\n <div\n id=\"power-resource-search-results\"\n role=\"list\"\n aria-label=\"Matching resources\"\n className=\"power-resource-search-results\"\n >\n {results.map((resource, index) => (\n <div\n key={resource.path}\n id={`power-resource-result-${index}`}\n role=\"listitem\"\n className={`power-resource-search-row ${selected === index ? \"selected\" : \"\"}`}\n >\n <button className=\"power-resource-search-open\" type=\"button\" onClick={() => open(resource)}>\n <AiIcon material={icons.get(resource.kind) ?? \"view_in_ar\"} />\n <span>\n <strong>{resource.name}</strong>\n <small>\n {resource.kind} · {resource.namespace ?? \"cluster scoped\"}\n </small>\n {searchIndex.nameParts(resource) && <small>Name parts</small>}\n {searchIndex.similarName(resource) && <small>Similar name</small>}\n <SearchSnippet field={searchIndex.snippet(resource, query)} query={query} />\n </span>\n </button>\n <DiagnosticExplainButton\n className=\"power-resource-search-ai\"\n aria-label={`Ask AI about ${resource.kind} ${resource.name}`}\n title=\"Ask AI about this resource\"\n onClick={() => {\n if (activeId() !== clusterId) return;\n close();\n explainDiagnosticFinding({\n clusterId,\n title: `${resource.kind}: ${resource.name}`,\n summary: `Resource selected from search. API group: ${(resource as SearchDocument).group || \"core\"}; version: ${(resource as SearchDocument).version}; resource: ${(resource as SearchDocument).resource}; kind: ${resource.kind}; namespace: ${resource.namespace ?? \"cluster scoped\"}; name: ${resource.name}; UID: ${(resource as SearchDocument).uid ?? \"unknown\"}; path: ${resource.path}. No live diagnostic data was collected.`,\n resource: { kind: resource.kind, name: resource.name, namespace: resource.namespace },\n });\n }}\n >\n <AiIcon material=\"auto_awesome\" />\n </DiagnosticExplainButton>\n </div>\n ))}\n {!results.length && <p>{loading ? \"Searching…\" : \"No matching resources in the available inventory\"}</p>}\n </div>\n {hasMore && (\n <button\n type=\"button\"\n disabled={limit >= 1000}\n onClick={() => setLimit((value) => Math.min(1000, value + 100))}\n >\n Show more matches\n </button>\n )}\n <details>\n <summary>Search options</summary>\n <div style={{ display: \"flex\", gap: 8, flexWrap: \"wrap\" }}>\n <input\n aria-label=\"Known namespace for restricted access\"\n placeholder=\"Known namespace\"\n value={knownNamespace}\n maxLength={63}\n onChange={(event) => setKnownNamespace(event.target.value)}\n />\n <button\n type=\"button\"\n disabled={!/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/.test(knownNamespace)}\n onClick={() => {\n extraNamespaces.add(knownNamespace);\n restartSearchSync();\n }}\n >\n Index namespace\n </button>\n </div>\n {hasMore && limit >= 1000 && (\n <p>Showing up to 1000 candidates. Refine the name, type or namespace to continue.</p>\n )}\n {!coverage.storageFull && (\n <button type=\"button\" onClick={clearCache}>\n Clear local search cache\n </button>\n )}\n </details>\n <p className=\"power-resource-search-shortcuts\">↑↓ Navigate · Enter Open · Esc Close</p>\n </div>\n </div>\n );\n}\n\nlet currentCoverage: SearchCoverage = {\n scanned: 0,\n discovered: 0,\n denied: 0,\n unavailable: 0,\n unsupported: 0,\n indexing: true,\n};\nlet restartSearchSync = () => {};\nconst extraNamespaces = new Set<string>();\n\nexport function startResourceSearch(): () => void {\n const preferences = MonitoringPreferencesStore.getInstanceOrCreate<MonitoringPreferencesStore>();\n let stop = preferences.resourceSearchEnabled ? startEnabledResourceSearch() : undefined;\n const dispose = reaction(\n () => preferences.resourceSearchEnabled,\n (enabled) => {\n stop?.();\n stop = enabled ? startEnabledResourceSearch() : undefined;\n },\n );\n return () => {\n dispose();\n stop?.();\n stop = undefined;\n };\n}\n\nfunction startEnabledResourceSearch(): () => void {\n let syncAbort: AbortController | undefined;\n const sync = () => {\n syncAbort?.abort();\n closeSearchResourceFallback();\n const clusterId = activeId();\n if (!clusterId || window.top === window) return;\n const phase = Renderer.Catalog.activeCluster.get()?.status?.phase;\n if (phase && phase !== \"connected\") {\n currentCoverage = {\n ...currentCoverage,\n indexing: false,\n message: \"Cluster disconnected; cached results may be stale\",\n };\n window.dispatchEvent(new Event(\"power-search-coverage\"));\n return;\n }\n const controller = new AbortController();\n syncAbort = controller;\n const client = createResourceSearchClient(clusterId, searchContextId());\n const liveStores = new Map(inventory().map(([kind, store]) => [kind as string, store]));\n const liveObjects = new Map<string, Map<string, unknown>>();\n const sink = {\n ...client,\n append: (scope: string, scanId: string, documents: SearchDocument[]) =>\n client.append(\n scope,\n scanId,\n documents.map((item) => {\n const store = liveStores.get(item.kind);\n if (!store) return item;\n let objects = liveObjects.get(item.kind);\n if (!objects) {\n objects = new Map(store.items.map((object) => [object.selfLink, object] as const));\n liveObjects.set(item.kind, objects);\n }\n const object = objects.get(item.path);\n return object ? { ...item, fields: projectSearchFields(item.kind, object) } : item;\n }),\n ),\n };\n let wasIndexing = false;\n const task = createSearchSynchronizer({\n transport: createResourceSearchTransport(clusterId, controller.signal),\n sink,\n signal: controller.signal,\n namespaces: () => [\n ...new Set([...(Renderer.Catalog.activeCluster.get()?.spec?.accessibleNamespaces ?? []), ...extraNamespaces]),\n ],\n onStatus: (status) => {\n if (!controller.signal.aborted) {\n if (status.indexing && !wasIndexing) liveObjects.clear();\n wasIndexing = status.indexing;\n currentCoverage = status;\n window.dispatchEvent(new Event(\"power-search-coverage\"));\n }\n },\n });\n void task.run().catch(() => {\n if (!controller.signal.aborted) {\n currentCoverage = { ...currentCoverage, indexing: false, unavailable: currentCoverage.unavailable + 1 };\n window.dispatchEvent(new Event(\"power-search-coverage\"));\n }\n });\n };\n restartSearchSync = sync;\n sync();\n const container = document.createElement(\"div\");\n const style = document.createElement(\"style\");\n style.textContent = `.power-resource-search-backdrop{position:fixed;inset:0;z-index:10000;background:#0007;display:flex;align-items:flex-start;justify-content:center;padding:8vh 20px}.power-resource-search-dialog{width:680px;max-width:100%;max-height:75vh;display:flex;flex-direction:column;padding:20px;gap:12px;background:var(--mainBackground,#222);color:var(--textColorPrimary,#eee);border:1px solid var(--borderColor,#555);border-radius:8px;box-shadow:0 16px 60px #0008}.power-resource-search-heading{display:flex;align-items:center;justify-content:space-between}.power-resource-search-filters input{flex:1;min-width:180px}.power-resource-search-dialog input{padding:12px;font-size:16px;background:var(--inputControlBackground,#333);color:inherit;border:1px solid var(--borderColor,#666);border-radius:4px}.power-resource-search-dialog p{margin:0;font-size:12px;color:var(--textColorSecondary,#bbb)}.power-resource-search-results{overflow:auto;min-height:180px;flex:1}.power-resource-search-row{display:flex;align-items:center;gap:12px;padding:4px;border-radius:4px}.power-resource-search-row{border-left:3px solid transparent}.power-resource-search-row.selected{background:color-mix(in srgb,var(--primary,#668fff) 16%,var(--mainBackground,#222));color:var(--tableSelectedRowColor,var(--textColorPrimary,#eee));border-left-color:var(--primary,#668fff)}.power-resource-search-row.selected .Icon{color:var(--primary,#8daaff)}.power-resource-search-row:hover{background:var(--tableBgcSelected,var(--contentColor,#292d32))}.power-resource-search-dialog button:hover:not(:disabled){background:#ffffff12;border-radius:4px}.power-resource-search-row>button:last-child{border:1px solid var(--borderColor,#555);border-radius:4px}.power-resource-search-shortcuts{padding-top:8px;border-top:1px solid var(--borderColor,#555)}.power-resource-search-dialog button{min-width:32px;min-height:32px;padding:6px;color:inherit;background:transparent;border:0;cursor:pointer}.power-resource-search-dialog button:disabled{opacity:.45;cursor:default}.power-resource-search-open{display:flex;align-items:center;gap:12px;flex:1;text-align:left;min-width:0}.power-resource-search-open span{min-width:0}.power-resource-search-open strong,.power-resource-search-open small{display:block;overflow-wrap:anywhere}.power-resource-search-open small{opacity:.7}.power-resource-search-row>button.power-resource-search-ai{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:34px;height:34px;min-width:34px;padding:0;background:var(--contentColor,#292d32);border:1px solid var(--borderColor,#666)}.power-resource-search-row.selected>button.power-resource-search-ai{background:var(--mainBackground,#202326);border-color:color-mix(in srgb,var(--textColorPrimary,#ddd) 55%,var(--borderColor,#666));box-shadow:0 1px 3px #0002}.power-resource-search-row.selected>button.power-resource-search-ai .Icon{color:var(--textColorAccent,#eee)}.power-resource-search-row>button.power-resource-search-ai:hover:not(:disabled){background:var(--contentColor,#30343a);border-color:var(--primary,#8daaff);color:var(--primary,#8daaff)}.power-resource-search-row>button.power-resource-search-ai:hover:not(:disabled) .Icon{color:inherit}.power-resource-search-dialog button:focus-visible{outline:2px solid var(--primary,#668fff)}`;\n document.head.append(style);\n document.body.append(container);\n let visible = false;\n const close = () => {\n if (visible) {\n visible = false;\n ReactDOM.unmountComponentAtNode(container);\n }\n };\n const show = () => {\n const clusterId = activeId();\n if (\n window.top === window ||\n !clusterId ||\n visible ||\n document.querySelector('[aria-modal=\"true\"], .Dialog.is-opened')\n )\n return;\n visible = true;\n ReactDOM.render(<SearchDialog close={close} clusterId={clusterId} />, container);\n };\n let shift = createDoubleShift();\n const resetShift = () => {\n shift = createDoubleShift();\n };\n const key = (event: KeyboardEvent) => {\n if (event.isComposing || event.key === \"Process\") {\n resetShift();\n return;\n }\n if (shift(event, performance.now())) show();\n };\n window.addEventListener(\"blur\", resetShift);\n window.addEventListener(\"keydown\", key);\n window.addEventListener(\"keyup\", key);\n window.addEventListener(OPEN, show);\n const dispose = reaction(\n () => `${searchContextId()}/${Renderer.Catalog.activeCluster.get()?.status?.phase}`,\n () => {\n close();\n extraNamespaces.clear();\n sync();\n },\n );\n return () => {\n close();\n syncAbort?.abort();\n closeSearchResourceFallback();\n restartSearchSync = () => {};\n extraNamespaces.clear();\n dispose();\n window.removeEventListener(\"blur\", resetShift);\n window.removeEventListener(\"keydown\", key);\n window.removeEventListener(\"keyup\", key);\n window.removeEventListener(OPEN, show);\n container.remove();\n style.remove();\n };\n}\n"],"mappings":"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"}