@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
@@ -0,0 +1,155 @@
1
+ const require_extensions = require("./_virtual/_global-external_@freelensapp/extensions.js");
2
+ const require__global_external_mobx = require("./_virtual/_global-external_mobx.js");
3
+ const require_monitoring_preferences_store = require("./common/store/monitoring-preferences-store.js");
4
+ const require_resource_search = require("./common/resource-search.js");
5
+ const require_resource_search_worker = require("./resource-search-worker.js");
6
+ let electron = require("electron");
7
+ let node_path = require("node:path");
8
+ let node_fs = require("node:fs");
9
+ let node_crypto = require("node:crypto");
10
+ //#region src/main/resource-search.ts
11
+ function trustedSearchSender(event, clusterId) {
12
+ const window = electron.BrowserWindow.fromWebContents(event.sender);
13
+ const frame = event.senderFrame;
14
+ if (!window || window.isDestroyed() || !frame) return false;
15
+ try {
16
+ const url = new URL(frame.url);
17
+ const root = new URL(event.sender.getURL());
18
+ if (url.protocol !== "https:" || root.protocol !== "https:" || url.username || url.password || root.username || root.password) return false;
19
+ if (url.hostname !== `${clusterId}.renderer.freelens.app` || root.port !== url.port) return false;
20
+ if (frame === event.sender.mainFrame) return root.origin === url.origin;
21
+ return root.hostname === "renderer.freelens.app" && frame.parent === event.sender.mainFrame && frame.top === event.sender.mainFrame;
22
+ } catch {
23
+ return false;
24
+ }
25
+ }
26
+ function validateSearchRequest(input) {
27
+ if (!require_resource_search.validSearchPartition(input)) throw new Error("Invalid search partition");
28
+ const request = input;
29
+ const partition = {
30
+ clusterId: request.clusterId,
31
+ contextId: request.contextId
32
+ };
33
+ if (request.operation === "reconcile") {
34
+ if (!Array.isArray(request.resources) || request.resources.length > 1e4 || request.resources.some((resource) => typeof resource !== "string" || !/^[a-zA-Z0-9_.-]{0,253}\/[a-zA-Z0-9_.-]{1,253}$/.test(resource))) throw new Error("Invalid search resource catalog");
35
+ return {
36
+ ...partition,
37
+ operation: "reconcile",
38
+ resources: [...new Set(request.resources)]
39
+ };
40
+ }
41
+ if ([
42
+ "begin",
43
+ "append",
44
+ "complete",
45
+ "invalidate"
46
+ ].includes(request.operation)) {
47
+ const scoped = request;
48
+ if (typeof scoped.scope !== "string" || !scoped.scope.length || scoped.scope.length > 512 || /[\x00-\x1f]/.test(scoped.scope)) throw new Error("Invalid search scope");
49
+ if (scoped.operation !== "invalidate" && (typeof scoped.scanId !== "string" || !/^[a-zA-Z0-9_.:-]{1,128}$/.test(scoped.scanId))) throw new Error("Invalid search scan");
50
+ if (scoped.operation === "invalidate") return {
51
+ ...partition,
52
+ operation: "invalidate",
53
+ scope: scoped.scope
54
+ };
55
+ const scan = {
56
+ ...partition,
57
+ scope: scoped.scope,
58
+ scanId: scoped.scanId
59
+ };
60
+ return scoped.operation === "append" ? {
61
+ ...scan,
62
+ operation: "append",
63
+ documents: require_resource_search.projectSearchDocuments(scoped.documents)
64
+ } : {
65
+ ...scan,
66
+ operation: scoped.operation
67
+ };
68
+ }
69
+ if (request.operation === "query") {
70
+ if (typeof request.query !== "string" || request.query.length > 300 || request.limit !== void 0 && (!Number.isSafeInteger(request.limit) || request.limit < 1 || request.limit > 1e3) || request.kind !== void 0 && !/^[a-zA-Z0-9_.:-]{1,253}$/.test(request.kind) || request.namespace !== void 0 && !/^[a-zA-Z0-9_.:-]{0,253}$/.test(request.namespace)) throw new Error("Invalid search query");
71
+ return {
72
+ ...partition,
73
+ operation: "query",
74
+ query: request.query,
75
+ limit: request.limit,
76
+ kind: request.kind,
77
+ namespace: request.namespace
78
+ };
79
+ }
80
+ if (request.operation !== "status" && request.operation !== "clear" && request.operation !== "permit") throw new Error("Invalid search operation");
81
+ return {
82
+ ...partition,
83
+ operation: request.operation
84
+ };
85
+ }
86
+ function startResourceSearchIndex() {
87
+ let active = true;
88
+ let worker;
89
+ const preferences = require_monitoring_preferences_store.MonitoringPreferencesStore.getInstanceOrCreate();
90
+ const stopPreference = require__global_external_mobx.reaction(() => preferences.resourceSearchEnabled, (enabled) => {
91
+ if (!enabled) {
92
+ worker?.close();
93
+ worker = void 0;
94
+ }
95
+ });
96
+ const nextRequest = /* @__PURE__ */ new Map();
97
+ const waits = /* @__PURE__ */ new Map();
98
+ const contextFingerprint = (request) => {
99
+ const cluster = require_extensions.Main.Catalog.getClusterById(request.clusterId);
100
+ if (!cluster) throw new Error("Unknown search cluster");
101
+ return (0, node_crypto.createHash)("sha256").update(request.contextId).update("\0").update(cluster.contextName).update("\0").update(cluster.kubeConfigPath).update("\0").update((0, node_fs.readFileSync)(cluster.kubeConfigPath)).digest("hex");
102
+ };
103
+ electron.ipcMain.handle(require_resource_search.RESOURCE_SEARCH_CHANNEL, async (event, input) => {
104
+ const request = validateSearchRequest(input);
105
+ if (!active || !trustedSearchSender(event, request.clusterId)) throw new Error("Search index unavailable");
106
+ const contextId = contextFingerprint(request);
107
+ if (request.operation === "permit") {
108
+ const start = Math.max(Date.now(), nextRequest.get(request.clusterId) ?? 0);
109
+ nextRequest.set(request.clusterId, start + 500);
110
+ if (start > Date.now()) await new Promise((resolve) => {
111
+ const timer = setTimeout(() => {
112
+ waits.delete(timer);
113
+ resolve();
114
+ }, start - Date.now());
115
+ waits.set(timer, resolve);
116
+ });
117
+ if (!active || !trustedSearchSender(event, request.clusterId) || contextFingerprint(request) !== contextId) throw new Error("Search context changed");
118
+ return true;
119
+ }
120
+ if (!preferences.resourceSearchEnabled) throw new Error("Resource search is disabled");
121
+ if (!worker) {
122
+ const directory = (0, node_path.join)(electron.app.getPath("userData"), "freelens-power-search");
123
+ (0, node_fs.mkdirSync)(directory, {
124
+ recursive: true,
125
+ mode: 448
126
+ });
127
+ worker = new require_resource_search_worker.ResourceSearchWorker((0, node_path.join)(directory, "resources.sqlite"));
128
+ }
129
+ const result = await worker.call({
130
+ ...request,
131
+ contextId
132
+ });
133
+ if (!active || !preferences.resourceSearchEnabled || !trustedSearchSender(event, request.clusterId) || contextFingerprint(request) !== contextId) throw new Error("Search context changed");
134
+ return result;
135
+ });
136
+ return () => {
137
+ active = false;
138
+ stopPreference();
139
+ electron.ipcMain.removeHandler(require_resource_search.RESOURCE_SEARCH_CHANNEL);
140
+ for (const [timer, resolve] of waits) {
141
+ clearTimeout(timer);
142
+ resolve();
143
+ }
144
+ waits.clear();
145
+ nextRequest.clear();
146
+ worker?.close();
147
+ worker = void 0;
148
+ };
149
+ }
150
+ //#endregion
151
+ exports.startResourceSearchIndex = startResourceSearchIndex;
152
+ exports.trustedSearchSender = trustedSearchSender;
153
+ exports.validateSearchRequest = validateSearchRequest;
154
+
155
+ //# sourceMappingURL=resource-search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-search.js","names":[],"sources":["../../src/main/resource-search.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { mkdirSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { Main } from \"@freelensapp/extensions\";\nimport { app, BrowserWindow, type IpcMainInvokeEvent, ipcMain } from \"electron\";\nimport { reaction } from \"mobx\";\nimport {\n projectSearchDocuments,\n RESOURCE_SEARCH_CHANNEL,\n type SearchIndexRequest,\n validSearchPartition,\n} from \"../common/resource-search\";\nimport { MonitoringPreferencesStore } from \"../common/store/monitoring-preferences-store\";\nimport { ResourceSearchWorker } from \"./resource-search-worker\";\n\nexport function trustedSearchSender(event: IpcMainInvokeEvent, clusterId: string): boolean {\n const window = BrowserWindow.fromWebContents(event.sender);\n const frame = event.senderFrame;\n if (!window || window.isDestroyed() || !frame) return false;\n try {\n const url = new URL(frame.url);\n const root = new URL(event.sender.getURL());\n if (\n url.protocol !== \"https:\" ||\n root.protocol !== \"https:\" ||\n url.username ||\n url.password ||\n root.username ||\n root.password\n )\n return false;\n if (url.hostname !== `${clusterId}.renderer.freelens.app` || root.port !== url.port) return false;\n if (frame === event.sender.mainFrame) return root.origin === url.origin;\n return (\n root.hostname === \"renderer.freelens.app\" &&\n frame.parent === event.sender.mainFrame &&\n frame.top === event.sender.mainFrame\n );\n } catch {\n return false;\n }\n}\n\nexport function validateSearchRequest(input: unknown): SearchIndexRequest {\n if (!validSearchPartition(input)) throw new Error(\"Invalid search partition\");\n const request = input as SearchIndexRequest;\n const partition = { clusterId: request.clusterId, contextId: request.contextId };\n if (request.operation === \"reconcile\") {\n if (\n !Array.isArray(request.resources) ||\n request.resources.length > 10000 ||\n request.resources.some(\n (resource) => typeof resource !== \"string\" || !/^[a-zA-Z0-9_.-]{0,253}\\/[a-zA-Z0-9_.-]{1,253}$/.test(resource),\n )\n )\n throw new Error(\"Invalid search resource catalog\");\n return { ...partition, operation: \"reconcile\", resources: [...new Set(request.resources)] };\n }\n if ([\"begin\", \"append\", \"complete\", \"invalidate\"].includes(request.operation)) {\n const scoped = request as Extract<SearchIndexRequest, { scope: string }>;\n if (\n typeof scoped.scope !== \"string\" ||\n !scoped.scope.length ||\n scoped.scope.length > 512 ||\n /[\\x00-\\x1f]/.test(scoped.scope)\n )\n throw new Error(\"Invalid search scope\");\n if (\n scoped.operation !== \"invalidate\" &&\n (typeof scoped.scanId !== \"string\" || !/^[a-zA-Z0-9_.:-]{1,128}$/.test(scoped.scanId))\n )\n throw new Error(\"Invalid search scan\");\n if (scoped.operation === \"invalidate\") return { ...partition, operation: \"invalidate\", scope: scoped.scope };\n const scan = { ...partition, scope: scoped.scope, scanId: scoped.scanId };\n return scoped.operation === \"append\"\n ? { ...scan, operation: \"append\", documents: projectSearchDocuments(scoped.documents) }\n : { ...scan, operation: scoped.operation };\n }\n if (request.operation === \"query\") {\n if (\n typeof request.query !== \"string\" ||\n request.query.length > 300 ||\n (request.limit !== undefined &&\n (!Number.isSafeInteger(request.limit) || request.limit < 1 || request.limit > 1000)) ||\n (request.kind !== undefined && !/^[a-zA-Z0-9_.:-]{1,253}$/.test(request.kind)) ||\n (request.namespace !== undefined && !/^[a-zA-Z0-9_.:-]{0,253}$/.test(request.namespace))\n )\n throw new Error(\"Invalid search query\");\n return {\n ...partition,\n operation: \"query\",\n query: request.query,\n limit: request.limit,\n kind: request.kind,\n namespace: request.namespace,\n };\n }\n if (request.operation !== \"status\" && request.operation !== \"clear\" && request.operation !== \"permit\")\n throw new Error(\"Invalid search operation\");\n return { ...partition, operation: request.operation };\n}\n\nexport function startResourceSearchIndex(): () => void {\n let active = true;\n let worker: ResourceSearchWorker | undefined;\n const preferences = MonitoringPreferencesStore.getInstanceOrCreate<MonitoringPreferencesStore>();\n const stopPreference = reaction(\n () => preferences.resourceSearchEnabled,\n (enabled) => {\n if (!enabled) {\n worker?.close();\n worker = undefined;\n }\n },\n );\n const nextRequest = new Map<string, number>();\n const waits = new Map<ReturnType<typeof setTimeout>, () => void>();\n const contextFingerprint = (request: SearchIndexRequest) => {\n const cluster = Main.Catalog.getClusterById(request.clusterId);\n if (!cluster) throw new Error(\"Unknown search cluster\");\n // only a digest of the local configuration reaches the database partition key\n return createHash(\"sha256\")\n .update(request.contextId)\n .update(\"\\0\")\n .update(cluster.contextName)\n .update(\"\\0\")\n .update(cluster.kubeConfigPath)\n .update(\"\\0\")\n .update(readFileSync(cluster.kubeConfigPath))\n .digest(\"hex\");\n };\n ipcMain.handle(RESOURCE_SEARCH_CHANNEL, async (event, input: unknown) => {\n const request = validateSearchRequest(input);\n if (!active || !trustedSearchSender(event, request.clusterId)) throw new Error(\"Search index unavailable\");\n const contextId = contextFingerprint(request);\n if (request.operation === \"permit\") {\n const start = Math.max(Date.now(), nextRequest.get(request.clusterId) ?? 0);\n nextRequest.set(request.clusterId, start + 500);\n if (start > Date.now())\n await new Promise<void>((resolve) => {\n const timer = setTimeout(() => {\n waits.delete(timer);\n resolve();\n }, start - Date.now());\n waits.set(timer, resolve);\n });\n if (!active || !trustedSearchSender(event, request.clusterId) || contextFingerprint(request) !== contextId)\n throw new Error(\"Search context changed\");\n return true;\n }\n if (!preferences.resourceSearchEnabled) throw new Error(\"Resource search is disabled\");\n if (!worker) {\n const directory = join(app.getPath(\"userData\"), \"freelens-power-search\");\n mkdirSync(directory, { recursive: true, mode: 0o700 });\n worker = new ResourceSearchWorker(join(directory, \"resources.sqlite\"));\n }\n const result = await worker.call({ ...request, contextId });\n if (\n !active ||\n !preferences.resourceSearchEnabled ||\n !trustedSearchSender(event, request.clusterId) ||\n contextFingerprint(request) !== contextId\n )\n throw new Error(\"Search context changed\");\n return result;\n });\n return () => {\n active = false;\n stopPreference();\n ipcMain.removeHandler(RESOURCE_SEARCH_CHANNEL);\n for (const [timer, resolve] of waits) {\n clearTimeout(timer);\n resolve();\n }\n waits.clear();\n nextRequest.clear();\n worker?.close();\n worker = undefined;\n };\n}\n"],"mappings":"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"}
@@ -16,7 +16,7 @@ function startAppearanceOverride() {
16
16
  }
17
17
  style.textContent = `${color === "default" ? "" : `
18
18
  :root { --primary: ${color} !important; --buttonPrimaryBackground: ${color} !important; --menuSelectedOptionBgc: ${color} !important; --select-option-focused-bgc: color-mix(in srgb, ${color} 24%, var(--contentColor)) !important; --icon-focus-color: ${color} !important; }
19
- :root .Checkbox, :root .Drawer .drawer-title, :root .DrawerParamToggler .param-link { --blue: var(--primary); }
19
+ :root .Checkbox, :root .Drawer .drawer-title, :root .DrawerParamToggler .param-link, :root .Drawer .ResizingAnchor { --blue: var(--primary); }
20
20
  :root [role="treeitem"][class*="catalog-tree-module__selected--"] { --blue: var(--primary); }
21
21
  :root .Select:not(.Select--is-disabled) .Select__control--is-focused:not(.Select__control--is-disabled) { border-color: ${color} !important; box-shadow: 0 0 0 1px ${color} !important; }
22
22
  :root .Select__option--is-selected { background: var(--primary) !important; color: var(--textColorAccent) !important; }
@@ -1 +1 @@
1
- {"version":3,"file":"appearance.js","names":[],"sources":["../../src/renderer/appearance.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { reaction } from \"mobx\";\nimport { AppearancePreferencesStore, isAccentSelection } from \"../common/store/appearance-preferences-store\";\n\nexport function startAppearanceOverride(): () => void {\n const preferences = AppearancePreferencesStore.getInstanceOrCreate<AppearancePreferencesStore>();\n const style = document.createElement(\"style\");\n style.dataset.freelensPower = \"accent-color\";\n\n const stop = reaction(\n () => [preferences.enabled, preferences.accentColor, preferences.recolorAppIcon] as const,\n ([enabled, color, recolorAppIcon]) => {\n if (!enabled || !isAccentSelection(color)) {\n style.remove();\n return;\n }\n\n // [!important] takes precedence over the host theme's inline variables\n const accent =\n color === \"default\"\n ? \"\"\n : `\n :root { --primary: ${color} !important; --buttonPrimaryBackground: ${color} !important; --menuSelectedOptionBgc: ${color} !important; --select-option-focused-bgc: color-mix(in srgb, ${color} 24%, var(--contentColor)) !important; --icon-focus-color: ${color} !important; }\n :root .Checkbox, :root .Drawer .drawer-title, :root .DrawerParamToggler .param-link { --blue: var(--primary); }\n :root [role=\"treeitem\"][class*=\"catalog-tree-module__selected--\"] { --blue: var(--primary); }\n :root .Select:not(.Select--is-disabled) .Select__control--is-focused:not(.Select__control--is-disabled) { border-color: ${color} !important; box-shadow: 0 0 0 1px ${color} !important; }\n :root .Select__option--is-selected { background: var(--primary) !important; color: var(--textColorAccent) !important; }\n :root .NamespaceSelectMenu .Select__option { margin-block: 2px; }\n :root .NamespaceSelectMenu .Select__option--is-selected { background: color-mix(in srgb, var(--primary) 10%, var(--contentColor)) !important; color: var(--textColorAccent) !important; }\n :root .NamespaceSelectMenu .Select__option--is-selected.Select__option--is-focused { background: color-mix(in srgb, var(--primary) 24%, var(--contentColor)) !important; }\n :root .NamespaceSelectMenu .Select__option--is-selected .Icon { color: var(--primary); }\n :root a.link { color: var(--primary); }\n ${recolorAppIcon ? '.Welcome [data-testid=\"no-welcome-banners-icon\"] svg path[fill=\"#23a49c\"] { fill: var(--primary); }' : \"\"}\n `;\n style.textContent = `${accent}\\ninput[role=\"switch\"] { --track-color-active: var(--primary) !important; }`;\n\n if (!style.isConnected) {\n document.head.append(style);\n }\n },\n { fireImmediately: true },\n );\n\n return () => {\n stop();\n style.remove();\n };\n}\n"],"mappings":";;;AAIA,SAAgB,0BAAsC;CACpD,MAAM,cAAc,qCAAA,2BAA2B,oBAAgD;CAC/F,MAAM,QAAQ,SAAS,cAAc,OAAO;CAC5C,MAAM,QAAQ,gBAAgB;CAE9B,MAAM,OAAO,8BAAA,eACL;EAAC,YAAY;EAAS,YAAY;EAAa,YAAY;CAAc,IAC9E,CAAC,SAAS,OAAO,oBAAoB;EACpC,IAAI,CAAC,WAAW,CAAC,qCAAA,kBAAkB,KAAK,GAAG;GACzC,MAAM,OAAO;GACb;EACF;EAmBA,MAAM,cAAc,GAflB,UAAU,YACN,KACA;6BACiB,MAAM,0CAA0C,MAAM,wCAAwC,MAAM,+DAA+D,MAAM,6DAA6D,MAAM;;;kIAGvI,MAAM,qCAAqC,MAAM;;;;;;;UAOzK,iBAAiB,4GAAwG,GAAG;QAElG;EAE9B,IAAI,CAAC,MAAM,aACT,SAAS,KAAK,OAAO,KAAK;CAE9B,GACA,EAAE,iBAAiB,KAAK,CAC1B;CAEA,aAAa;EACX,KAAK;EACL,MAAM,OAAO;CACf;AACF"}
1
+ {"version":3,"file":"appearance.js","names":[],"sources":["../../src/renderer/appearance.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { reaction } from \"mobx\";\nimport { AppearancePreferencesStore, isAccentSelection } from \"../common/store/appearance-preferences-store\";\n\nexport function startAppearanceOverride(): () => void {\n const preferences = AppearancePreferencesStore.getInstanceOrCreate<AppearancePreferencesStore>();\n const style = document.createElement(\"style\");\n style.dataset.freelensPower = \"accent-color\";\n\n const stop = reaction(\n () => [preferences.enabled, preferences.accentColor, preferences.recolorAppIcon] as const,\n ([enabled, color, recolorAppIcon]) => {\n if (!enabled || !isAccentSelection(color)) {\n style.remove();\n return;\n }\n\n // [!important] takes precedence over the host theme's inline variables\n const accent =\n color === \"default\"\n ? \"\"\n : `\n :root { --primary: ${color} !important; --buttonPrimaryBackground: ${color} !important; --menuSelectedOptionBgc: ${color} !important; --select-option-focused-bgc: color-mix(in srgb, ${color} 24%, var(--contentColor)) !important; --icon-focus-color: ${color} !important; }\n :root .Checkbox, :root .Drawer .drawer-title, :root .DrawerParamToggler .param-link, :root .Drawer .ResizingAnchor { --blue: var(--primary); }\n :root [role=\"treeitem\"][class*=\"catalog-tree-module__selected--\"] { --blue: var(--primary); }\n :root .Select:not(.Select--is-disabled) .Select__control--is-focused:not(.Select__control--is-disabled) { border-color: ${color} !important; box-shadow: 0 0 0 1px ${color} !important; }\n :root .Select__option--is-selected { background: var(--primary) !important; color: var(--textColorAccent) !important; }\n :root .NamespaceSelectMenu .Select__option { margin-block: 2px; }\n :root .NamespaceSelectMenu .Select__option--is-selected { background: color-mix(in srgb, var(--primary) 10%, var(--contentColor)) !important; color: var(--textColorAccent) !important; }\n :root .NamespaceSelectMenu .Select__option--is-selected.Select__option--is-focused { background: color-mix(in srgb, var(--primary) 24%, var(--contentColor)) !important; }\n :root .NamespaceSelectMenu .Select__option--is-selected .Icon { color: var(--primary); }\n :root a.link { color: var(--primary); }\n ${recolorAppIcon ? '.Welcome [data-testid=\"no-welcome-banners-icon\"] svg path[fill=\"#23a49c\"] { fill: var(--primary); }' : \"\"}\n `;\n style.textContent = `${accent}\\ninput[role=\"switch\"] { --track-color-active: var(--primary) !important; }`;\n\n if (!style.isConnected) {\n document.head.append(style);\n }\n },\n { fireImmediately: true },\n );\n\n return () => {\n stop();\n style.remove();\n };\n}\n"],"mappings":";;;AAIA,SAAgB,0BAAsC;CACpD,MAAM,cAAc,qCAAA,2BAA2B,oBAAgD;CAC/F,MAAM,QAAQ,SAAS,cAAc,OAAO;CAC5C,MAAM,QAAQ,gBAAgB;CAE9B,MAAM,OAAO,8BAAA,eACL;EAAC,YAAY;EAAS,YAAY;EAAa,YAAY;CAAc,IAC9E,CAAC,SAAS,OAAO,oBAAoB;EACpC,IAAI,CAAC,WAAW,CAAC,qCAAA,kBAAkB,KAAK,GAAG;GACzC,MAAM,OAAO;GACb;EACF;EAmBA,MAAM,cAAc,GAflB,UAAU,YACN,KACA;6BACiB,MAAM,0CAA0C,MAAM,wCAAwC,MAAM,+DAA+D,MAAM,6DAA6D,MAAM;;;kIAGvI,MAAM,qCAAqC,MAAM;;;;;;;UAOzK,iBAAiB,4GAAwG,GAAG;QAElG;EAE9B,IAAI,CAAC,MAAM,aACT,SAAS,KAAK,OAAO,KAAK;CAE9B,GACA,EAAE,iBAAiB,KAAK,CAC1B;CAEA,aAAa;EACX,KAAK;EACL,MAAM,OAAO;CACf;AACF"}
@@ -0,0 +1,107 @@
1
+ //#region src/renderer/gateway-relationships-client.ts
2
+ var group = "gateway.networking.k8s.io";
3
+ var kinds = /* @__PURE__ */ new Set([
4
+ "GatewayClass",
5
+ "Gateway",
6
+ "HTTPRoute",
7
+ "GRPCRoute",
8
+ "TCPRoute",
9
+ "TLSRoute",
10
+ "UDPRoute",
11
+ "ReferenceGrant"
12
+ ]);
13
+ async function loadGatewayInventory(transport, signal, scope = {}) {
14
+ const inventory = {
15
+ objects: [],
16
+ gaps: []
17
+ };
18
+ const status = (error) => error?.status ?? error?.statusCode ?? error?.code;
19
+ let discovery;
20
+ try {
21
+ discovery = await transport.get(`/apis/${group}`);
22
+ } catch (error) {
23
+ if (!signal.aborted && status(error) !== 404) inventory.gaps.push("Gateway API discovery unavailable; upstream evidence is unknown");
24
+ return inventory;
25
+ }
26
+ const resources = /* @__PURE__ */ new Map();
27
+ const versions = [...new Set([discovery.preferredVersion?.version, ...(discovery.versions ?? []).map((item) => item.version)].filter((version) => !!version))];
28
+ for (const version of versions) {
29
+ if (signal.aborted) return inventory;
30
+ try {
31
+ const found = await transport.get(`/apis/${group}/${version}`);
32
+ for (const resource of found.resources ?? []) if (kinds.has(resource.kind) && !resource.name.includes("/") && resource.verbs?.includes("list") && !resources.has(resource.kind)) resources.set(resource.kind, {
33
+ ...resource,
34
+ version
35
+ });
36
+ } catch {
37
+ inventory.gaps.push(`Gateway API ${version} discovery unavailable; coverage may be partial`);
38
+ }
39
+ }
40
+ if (!resources.size) return inventory;
41
+ let namespacesComplete = false;
42
+ try {
43
+ const namespaces = await transport.get("/api/v1/namespaces", { limit: 100 });
44
+ inventory.namespaces = namespaces.items;
45
+ namespacesComplete = !namespaces.metadata?.continue;
46
+ if (!namespacesComplete) inventory.gaps.push("Namespace labels partially observed; Gateway namespace selectors may be unknown");
47
+ } catch {
48
+ inventory.gaps.push("Namespace labels unavailable; Gateway namespace selectors may be unknown");
49
+ }
50
+ const namespaces = [...new Set([
51
+ scope.focusedNamespace,
52
+ ...scope.knownNamespaces ?? [],
53
+ ...(inventory.namespaces ?? []).map((item) => item.metadata.name)
54
+ ].filter((name) => !!name))];
55
+ for (const resource of resources.values()) {
56
+ if (signal.aborted) return inventory;
57
+ const items = [];
58
+ let remaining = 20;
59
+ const base = `/apis/${group}/${resource.version}`;
60
+ const scan = async (namespace) => {
61
+ let continuation = "";
62
+ do {
63
+ if (signal.aborted || remaining-- <= 0) return false;
64
+ const list = await transport.get(`${base}/${namespace ? `namespaces/${encodeURIComponent(namespace)}/` : ""}${resource.name}`, {
65
+ limit: 100,
66
+ ...continuation ? { continue: continuation } : {}
67
+ });
68
+ for (const item of list.items ?? []) items.push({
69
+ ...item,
70
+ kind: resource.kind,
71
+ apiVersion: `${group}/${resource.version}`,
72
+ selfLink: `${base}/${resource.namespaced ? `namespaces/${encodeURIComponent(item.metadata.namespace ?? namespace ?? "")}/` : ""}${resource.name}/${encodeURIComponent(item.metadata.name ?? "")}`
73
+ });
74
+ continuation = list.metadata?.continue ?? "";
75
+ } while (continuation);
76
+ return true;
77
+ };
78
+ let complete = false;
79
+ try {
80
+ complete = await scan();
81
+ } catch (error) {
82
+ if (status(error) === 403 && resource.namespaced) {
83
+ const completedNamespaces = [];
84
+ for (const namespace of namespaces) {
85
+ if (signal.aborted || remaining <= 0) break;
86
+ try {
87
+ if (await scan(namespace)) completedNamespaces.push(namespace);
88
+ } catch {
89
+ inventory.gaps.push(`${resource.kind} unavailable in namespace ${namespace}; evidence is unknown`);
90
+ }
91
+ }
92
+ complete = namespacesComplete && completedNamespaces.length === namespaces.length;
93
+ if (resource.kind === "ReferenceGrant") inventory.grantNamespaces = completedNamespaces;
94
+ if (!complete) inventory.gaps.push(`${resource.kind} namespace fallback is partial; unlisted or denied namespaces remain unknown`);
95
+ } else if (!signal.aborted) inventory.gaps.push(`${resource.kind} inventory unavailable; references and acceptance may be unknown`);
96
+ }
97
+ if (!complete && remaining <= 0) inventory.gaps.push(`${resource.kind} inventory partial; relationship request budget reached`);
98
+ if (resource.kind === "ReferenceGrant") {
99
+ if (complete || inventory.grantNamespaces?.length) inventory.grants = items;
100
+ } else inventory.objects.push(...items);
101
+ }
102
+ return inventory;
103
+ }
104
+ //#endregion
105
+ exports.loadGatewayInventory = loadGatewayInventory;
106
+
107
+ //# sourceMappingURL=gateway-relationships-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gateway-relationships-client.js","names":[],"sources":["../../src/renderer/gateway-relationships-client.ts"],"sourcesContent":["import type { GatewayInventory, GatewayObject } from \"../common/gateway-relationships\";\n\ninterface GatewayTransport {\n get<T>(path: string, query?: Record<string, string | number>): Promise<T>;\n}\ninterface GatewayScope {\n focusedNamespace?: string;\n knownNamespaces?: string[];\n}\nconst group = \"gateway.networking.k8s.io\";\nconst kinds = new Set([\n \"GatewayClass\",\n \"Gateway\",\n \"HTTPRoute\",\n \"GRPCRoute\",\n \"TCPRoute\",\n \"TLSRoute\",\n \"UDPRoute\",\n \"ReferenceGrant\",\n]);\nexport async function loadGatewayInventory(\n transport: GatewayTransport,\n signal: AbortSignal,\n scope: GatewayScope = {},\n): Promise<GatewayInventory> {\n const inventory: GatewayInventory = { objects: [], gaps: [] };\n const status = (error: unknown) =>\n (error as { status?: number; code?: number })?.status ??\n (error as { statusCode?: number })?.statusCode ??\n (error as { code?: number })?.code;\n let discovery: { versions?: { version: string }[]; preferredVersion?: { version: string } };\n try {\n discovery = await transport.get(`/apis/${group}`);\n } catch (error) {\n if (!signal.aborted && status(error) !== 404)\n inventory.gaps.push(\"Gateway API discovery unavailable; upstream evidence is unknown\");\n return inventory;\n }\n const resources = new Map<string, { version: string; name: string; kind: string; namespaced: boolean }>();\n const versions = [\n ...new Set(\n [discovery.preferredVersion?.version, ...(discovery.versions ?? []).map((item) => item.version)].filter(\n (version): version is string => !!version,\n ),\n ),\n ];\n for (const version of versions) {\n if (signal.aborted) return inventory;\n try {\n const found = await transport.get<{\n resources?: { name: string; kind: string; namespaced: boolean; verbs?: string[] }[];\n }>(`/apis/${group}/${version}`);\n for (const resource of found.resources ?? [])\n if (\n kinds.has(resource.kind) &&\n !resource.name.includes(\"/\") &&\n resource.verbs?.includes(\"list\") &&\n !resources.has(resource.kind)\n )\n resources.set(resource.kind, { ...resource, version });\n } catch {\n inventory.gaps.push(`Gateway API ${version} discovery unavailable; coverage may be partial`);\n }\n }\n if (!resources.size) return inventory;\n let namespacesComplete = false;\n try {\n const namespaces = await transport.get<{ items?: GatewayObject[]; metadata?: { continue?: string } }>(\n \"/api/v1/namespaces\",\n { limit: 100 },\n );\n inventory.namespaces = namespaces.items;\n namespacesComplete = !namespaces.metadata?.continue;\n if (!namespacesComplete)\n inventory.gaps.push(\"Namespace labels partially observed; Gateway namespace selectors may be unknown\");\n } catch {\n inventory.gaps.push(\"Namespace labels unavailable; Gateway namespace selectors may be unknown\");\n }\n const namespaces = [\n ...new Set(\n [\n scope.focusedNamespace,\n ...(scope.knownNamespaces ?? []),\n ...(inventory.namespaces ?? []).map((item) => item.metadata.name),\n ].filter((name): name is string => !!name),\n ),\n ];\n for (const resource of resources.values()) {\n if (signal.aborted) return inventory;\n const items: GatewayObject[] = [];\n let remaining = 20;\n const base = `/apis/${group}/${resource.version}`;\n const scan = async (namespace?: string) => {\n let continuation = \"\";\n do {\n if (signal.aborted || remaining-- <= 0) return false;\n const list = await transport.get<{ items?: GatewayObject[]; metadata?: { continue?: string } }>(\n `${base}/${namespace ? `namespaces/${encodeURIComponent(namespace)}/` : \"\"}${resource.name}`,\n { limit: 100, ...(continuation ? { continue: continuation } : {}) },\n );\n for (const item of list.items ?? [])\n items.push({\n ...item,\n kind: resource.kind,\n apiVersion: `${group}/${resource.version}`,\n selfLink: `${base}/${resource.namespaced ? `namespaces/${encodeURIComponent(item.metadata.namespace ?? namespace ?? \"\")}/` : \"\"}${resource.name}/${encodeURIComponent(item.metadata.name ?? \"\")}`,\n });\n continuation = list.metadata?.continue ?? \"\";\n } while (continuation);\n return true;\n };\n let complete = false;\n try {\n complete = await scan();\n } catch (error) {\n if (status(error) === 403 && resource.namespaced) {\n const completedNamespaces: string[] = [];\n for (const namespace of namespaces) {\n if (signal.aborted || remaining <= 0) break;\n try {\n if (await scan(namespace)) completedNamespaces.push(namespace);\n } catch {\n inventory.gaps.push(`${resource.kind} unavailable in namespace ${namespace}; evidence is unknown`);\n }\n }\n complete = namespacesComplete && completedNamespaces.length === namespaces.length;\n if (resource.kind === \"ReferenceGrant\") inventory.grantNamespaces = completedNamespaces;\n if (!complete)\n inventory.gaps.push(\n `${resource.kind} namespace fallback is partial; unlisted or denied namespaces remain unknown`,\n );\n } else if (!signal.aborted)\n inventory.gaps.push(`${resource.kind} inventory unavailable; references and acceptance may be unknown`);\n }\n if (!complete && remaining <= 0)\n inventory.gaps.push(`${resource.kind} inventory partial; relationship request budget reached`);\n if (resource.kind === \"ReferenceGrant\") {\n if (complete || inventory.grantNamespaces?.length) inventory.grants = items;\n } else inventory.objects.push(...items);\n }\n return inventory;\n}\n"],"mappings":"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"}
@@ -46,6 +46,16 @@ var MonitoringPreferences = require__global_external_mobx_react.observer(() => {
46
46
  className: "power-settings-hint",
47
47
  children: "Focused resource connections from API evidence, with links to related objects."
48
48
  })] }),
49
+ /* @__PURE__ */ require_jsx_runtime.jsxs("div", { children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_extensions.Renderer.Component.Switch, {
50
+ checked: preferences.resourceSearchEnabled,
51
+ onChange: (value) => {
52
+ preferences.resourceSearchEnabled = value;
53
+ },
54
+ children: "Enable resource search (Shift–Shift)"
55
+ }), /* @__PURE__ */ require_jsx_runtime.jsx("div", {
56
+ className: "power-settings-hint",
57
+ children: "Search resources with Shift–Shift. Turning this off stops keyboard search and background indexing."
58
+ })] }),
49
59
  /* @__PURE__ */ require_jsx_runtime.jsxs("div", { children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_extensions.Renderer.Component.Switch, {
50
60
  checked: preferences.enabled,
51
61
  onChange: (value) => {
@@ -1 +1 @@
1
- {"version":3,"file":"monitoring-preferences.js","names":[],"sources":["../../../src/renderer/preferences/monitoring-preferences.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { observer } from \"mobx-react\";\nimport { useEffect, useState } from \"react\";\nimport { HISTORY_MAX_BYTES, type HistoryStatus } from \"../../common/monitoring-history\";\nimport { MonitoringPreferencesStore } from \"../../common/store/monitoring-preferences-store\";\nimport { monitoringHistory } from \"../monitoring-history\";\n\nexport const MonitoringPreferences = observer(() => {\n const preferences = MonitoringPreferencesStore.getInstanceOrCreate<MonitoringPreferencesStore>();\n const [status, setStatus] = useState<HistoryStatus>();\n const refresh = () => {\n void monitoringHistory\n .status()\n .then(setStatus)\n .catch(() =>\n setStatus({\n available: false,\n enabled: preferences.historyEnabled,\n bytes: 0,\n samples: 0,\n error: \"History service unavailable\",\n }),\n );\n };\n useEffect(refresh, [preferences.historyEnabled]);\n return (\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 20 }}>\n <div>\n <Renderer.Component.Switch\n checked={preferences.diagnosticsVisible}\n onChange={(value) => {\n preferences.diagnosticsVisible = value;\n }}\n >\n Show Diagnostics in sidebar\n </Renderer.Component.Switch>\n <div className=\"power-settings-hint\">Resource consumers, application events and Pending diagnostics.</div>\n </div>\n <div>\n <Renderer.Component.Switch\n checked={preferences.relationshipsVisible}\n onChange={(value) => {\n preferences.relationshipsVisible = value;\n }}\n >\n Show Relationships in sidebar\n </Renderer.Component.Switch>\n <div className=\"power-settings-hint\">\n Focused resource connections from API evidence, with links to related objects.\n </div>\n </div>\n <div>\n <Renderer.Component.Switch\n checked={preferences.enabled}\n onChange={(value) => {\n preferences.enabled = value;\n }}\n >\n Metrics-server monitoring\n </Renderer.Component.Switch>\n <div className=\"power-settings-hint\">\n CPU/RAM charts for clusters, nodes and pods. Keeps 30 minutes of live samples.\n </div>\n </div>\n <div>\n <Renderer.Component.Switch\n checked={preferences.historyEnabled}\n onChange={(value) => {\n preferences.historyEnabled = value;\n }}\n >\n Keep local history\n </Renderer.Component.Switch>\n <div\n className=\"power-settings-hint\"\n title=\"SQLite storage. Collects while the cluster view is active. Resolution: 30 seconds for 2 hours, 5 minutes for 24 hours, 30 minutes for 7 days. Disabling preserves saved history.\"\n >\n Up to 7 days, limited to 100 MiB.\n </div>\n </div>\n <div style={{ display: \"flex\", alignItems: \"center\", justifyContent: \"space-between\", gap: 12 }}>\n <div style={{ flex: 1, minWidth: 0 }} role=\"status\">\n <div style={{ display: \"flex\", justifyContent: \"space-between\", alignItems: \"baseline\", gap: 12 }}>\n <span>Local storage</span>\n <span style={{ fontVariantNumeric: \"tabular-nums\", color: \"var(--textColorSecondary)\" }}>\n {status?.available\n ? `${(status.bytes / 1024 / 1024).toFixed(2)} / ${HISTORY_MAX_BYTES / 1024 / 1024} MiB`\n : \"— / 100 MiB\"}\n </span>\n </div>\n {status?.available && (\n <div\n role=\"progressbar\"\n aria-label=\"Local history storage\"\n aria-valuemin={0}\n aria-valuemax={HISTORY_MAX_BYTES}\n aria-valuenow={status.bytes}\n aria-valuetext={`${(status.bytes / 1024 / 1024).toFixed(2)} of 100 MiB`}\n style={{ height: 4, marginTop: 8, borderRadius: 2, overflow: \"hidden\", background: \"var(--borderColor)\" }}\n >\n <div\n style={{\n height: \"100%\",\n width: `${Math.min(100, Math.max(0, (status.bytes / HISTORY_MAX_BYTES) * 100))}%`,\n background: \"var(--primary)\",\n transition: \"width 180ms ease\",\n }}\n />\n </div>\n )}\n <div className=\"power-settings-hint\" style={{ marginTop: 6 }}>\n {status?.error ??\n (status?.available\n ? `${status.samples.toLocaleString()} metric buckets · 7-day retention`\n : \"Checking history storage…\")}\n </div>\n </div>\n <Renderer.Component.Button\n style={{\n flexShrink: 0,\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n gap: 6,\n height: 30,\n padding: \"0 10px\",\n fontSize: 12,\n lineHeight: \"16px\",\n border: \"1px solid var(--borderFaintColor)\",\n borderRadius: 6,\n background: \"var(--contentColor)\",\n color: \"var(--textColorSecondary)\",\n }}\n title=\"Delete locally collected metric history without changing other settings\"\n onClick={() => {\n void monitoringHistory\n .clear()\n .then(refresh)\n .catch(() =>\n setStatus((previous) => ({\n available: previous?.available ?? false,\n enabled: preferences.historyEnabled,\n bytes: previous?.bytes ?? 0,\n samples: previous?.samples ?? 0,\n error: \"Unable to clear local history. Try again.\",\n })),\n );\n }}\n >\n <Renderer.Component.Icon material=\"delete_outline\" size={14} />\n <span>Clear</span>\n </Renderer.Component.Button>\n </div>\n </div>\n );\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"monitoring-preferences.js","names":[],"sources":["../../../src/renderer/preferences/monitoring-preferences.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { observer } from \"mobx-react\";\nimport { useEffect, useState } from \"react\";\nimport { HISTORY_MAX_BYTES, type HistoryStatus } from \"../../common/monitoring-history\";\nimport { MonitoringPreferencesStore } from \"../../common/store/monitoring-preferences-store\";\nimport { monitoringHistory } from \"../monitoring-history\";\n\nexport const MonitoringPreferences = observer(() => {\n const preferences = MonitoringPreferencesStore.getInstanceOrCreate<MonitoringPreferencesStore>();\n const [status, setStatus] = useState<HistoryStatus>();\n const refresh = () => {\n void monitoringHistory\n .status()\n .then(setStatus)\n .catch(() =>\n setStatus({\n available: false,\n enabled: preferences.historyEnabled,\n bytes: 0,\n samples: 0,\n error: \"History service unavailable\",\n }),\n );\n };\n useEffect(refresh, [preferences.historyEnabled]);\n return (\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 20 }}>\n <div>\n <Renderer.Component.Switch\n checked={preferences.diagnosticsVisible}\n onChange={(value) => {\n preferences.diagnosticsVisible = value;\n }}\n >\n Show Diagnostics in sidebar\n </Renderer.Component.Switch>\n <div className=\"power-settings-hint\">Resource consumers, application events and Pending diagnostics.</div>\n </div>\n <div>\n <Renderer.Component.Switch\n checked={preferences.relationshipsVisible}\n onChange={(value) => {\n preferences.relationshipsVisible = value;\n }}\n >\n Show Relationships in sidebar\n </Renderer.Component.Switch>\n <div className=\"power-settings-hint\">\n Focused resource connections from API evidence, with links to related objects.\n </div>\n </div>\n <div>\n <Renderer.Component.Switch\n checked={preferences.resourceSearchEnabled}\n onChange={(value) => {\n preferences.resourceSearchEnabled = value;\n }}\n >\n Enable resource search (Shift–Shift)\n </Renderer.Component.Switch>\n <div className=\"power-settings-hint\">\n Search resources with Shift–Shift. Turning this off stops keyboard search and background indexing.\n </div>\n </div>\n <div>\n <Renderer.Component.Switch\n checked={preferences.enabled}\n onChange={(value) => {\n preferences.enabled = value;\n }}\n >\n Metrics-server monitoring\n </Renderer.Component.Switch>\n <div className=\"power-settings-hint\">\n CPU/RAM charts for clusters, nodes and pods. Keeps 30 minutes of live samples.\n </div>\n </div>\n <div>\n <Renderer.Component.Switch\n checked={preferences.historyEnabled}\n onChange={(value) => {\n preferences.historyEnabled = value;\n }}\n >\n Keep local history\n </Renderer.Component.Switch>\n <div\n className=\"power-settings-hint\"\n title=\"SQLite storage. Collects while the cluster view is active. Resolution: 30 seconds for 2 hours, 5 minutes for 24 hours, 30 minutes for 7 days. Disabling preserves saved history.\"\n >\n Up to 7 days, limited to 100 MiB.\n </div>\n </div>\n <div style={{ display: \"flex\", alignItems: \"center\", justifyContent: \"space-between\", gap: 12 }}>\n <div style={{ flex: 1, minWidth: 0 }} role=\"status\">\n <div style={{ display: \"flex\", justifyContent: \"space-between\", alignItems: \"baseline\", gap: 12 }}>\n <span>Local storage</span>\n <span style={{ fontVariantNumeric: \"tabular-nums\", color: \"var(--textColorSecondary)\" }}>\n {status?.available\n ? `${(status.bytes / 1024 / 1024).toFixed(2)} / ${HISTORY_MAX_BYTES / 1024 / 1024} MiB`\n : \"— / 100 MiB\"}\n </span>\n </div>\n {status?.available && (\n <div\n role=\"progressbar\"\n aria-label=\"Local history storage\"\n aria-valuemin={0}\n aria-valuemax={HISTORY_MAX_BYTES}\n aria-valuenow={status.bytes}\n aria-valuetext={`${(status.bytes / 1024 / 1024).toFixed(2)} of 100 MiB`}\n style={{ height: 4, marginTop: 8, borderRadius: 2, overflow: \"hidden\", background: \"var(--borderColor)\" }}\n >\n <div\n style={{\n height: \"100%\",\n width: `${Math.min(100, Math.max(0, (status.bytes / HISTORY_MAX_BYTES) * 100))}%`,\n background: \"var(--primary)\",\n transition: \"width 180ms ease\",\n }}\n />\n </div>\n )}\n <div className=\"power-settings-hint\" style={{ marginTop: 6 }}>\n {status?.error ??\n (status?.available\n ? `${status.samples.toLocaleString()} metric buckets · 7-day retention`\n : \"Checking history storage…\")}\n </div>\n </div>\n <Renderer.Component.Button\n style={{\n flexShrink: 0,\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n gap: 6,\n height: 30,\n padding: \"0 10px\",\n fontSize: 12,\n lineHeight: \"16px\",\n border: \"1px solid var(--borderFaintColor)\",\n borderRadius: 6,\n background: \"var(--contentColor)\",\n color: \"var(--textColorSecondary)\",\n }}\n title=\"Delete locally collected metric history without changing other settings\"\n onClick={() => {\n void monitoringHistory\n .clear()\n .then(refresh)\n .catch(() =>\n setStatus((previous) => ({\n available: previous?.available ?? false,\n enabled: preferences.historyEnabled,\n bytes: previous?.bytes ?? 0,\n samples: previous?.samples ?? 0,\n error: \"Unable to clear local history. Try again.\",\n })),\n );\n }}\n >\n <Renderer.Component.Icon material=\"delete_outline\" size={14} />\n <span>Clear</span>\n </Renderer.Component.Button>\n </div>\n </div>\n );\n});\n"],"mappings":"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"}
@@ -16,7 +16,7 @@ var styles = `
16
16
  #freelens-power-settings > .SubTitle,
17
17
  #freelens-power-settings > .hint,
18
18
  #freelens-power-settings + hr { display: none; }
19
- .freelens-power-preferences { display: grid; gap: 24px; max-width: 760px; }
19
+ .freelens-power-preferences { display: grid; gap: 24px; max-width: 760px; container: power-preferences / inline-size; }
20
20
  .freelens-power-preferences .power-preferences-section-links {
21
21
  display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); width: 100%; gap: 8px;
22
22
  }
@@ -36,10 +36,10 @@ var styles = `
36
36
  color: var(--primary); background: transparent; box-shadow: none;
37
37
  outline: 2px solid var(--primary); outline-offset: 2px;
38
38
  }
39
- @media (max-width: 600px) {
39
+ @container power-preferences (max-width: 660px) {
40
40
  .freelens-power-preferences .power-preferences-section-links { grid-template-columns: repeat(3, minmax(0, 1fr)); }
41
41
  }
42
- @media (max-width: 360px) {
42
+ @container power-preferences (max-width: 360px) {
43
43
  .freelens-power-preferences .power-preferences-section-links { grid-template-columns: repeat(2, minmax(0, 1fr)); }
44
44
  }
45
45
  .freelens-power-preferences section[id] { scroll-margin-top: 16px; }
@@ -1 +1 @@
1
- {"version":3,"file":"preferences.js","names":[],"sources":["../../../src/renderer/preferences/preferences.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { EXTENSION_ID } from \"../../common/extension-info\";\nimport { AiPreferences } from \"./ai-preferences\";\nimport { AppearancePreferences } from \"./appearance-preferences\";\nimport { DashboardPreferences } from \"./dashboard-preferences\";\nimport { MonitoringPreferences } from \"./monitoring-preferences\";\nimport { NodeShellPreferences } from \"./node-shell-preferences\";\nimport { PowerPreferencesToggle } from \"./power-preferences\";\nimport { ReadOnlyPreferences } from \"./read-only-preferences\";\nimport { SettingsExport } from \"./settings-export\";\n\nconst styles = `\n .power-preferences-heading { display: flex; align-items: center; justify-content: space-between; gap: 16px; }\n .power-preferences-header-actions { display: inline-flex; margin-left: auto; flex-shrink: 0; }\n #freelens-power-settings > .SubTitle,\n #freelens-power-settings > .hint,\n #freelens-power-settings + hr { display: none; }\n .freelens-power-preferences { display: grid; gap: 24px; max-width: 760px; }\n .freelens-power-preferences .power-preferences-section-links {\n display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); width: 100%; gap: 8px;\n }\n :root .freelens-power-preferences .power-preferences-section-links button.power-settings-section-link {\n display: inline-flex; align-items: center; justify-content: center; gap: 6px;\n width: 100%; min-width: 0; min-height: 32px; padding: 4px 2px;\n border: 0; border-radius: 4px; background: transparent; box-shadow: none;\n color: var(--textColorSecondary); text-transform: none; white-space: nowrap; cursor: pointer;\n }\n :root .freelens-power-preferences .power-preferences-section-links button.power-settings-section-link:hover {\n color: var(--primary); background: transparent; box-shadow: none;\n }\n :root .freelens-power-preferences .power-preferences-section-links button.power-settings-section-link::before {\n content: none;\n }\n :root .freelens-power-preferences .power-preferences-section-links button.power-settings-section-link:focus-visible {\n color: var(--primary); background: transparent; box-shadow: none;\n outline: 2px solid var(--primary); outline-offset: 2px;\n }\n @media (max-width: 600px) {\n .freelens-power-preferences .power-preferences-section-links { grid-template-columns: repeat(3, minmax(0, 1fr)); }\n }\n @media (max-width: 360px) {\n .freelens-power-preferences .power-preferences-section-links { grid-template-columns: repeat(2, minmax(0, 1fr)); }\n }\n .freelens-power-preferences section[id] { scroll-margin-top: 16px; }\n .freelens-power-preferences section {\n padding: 24px; border: 1px solid var(--borderFaintColor);\n border-radius: 12px; background: var(--contentColor);\n }\n .freelens-power-preferences button, .freelens-power-preferences .Button { transition: color 160ms ease, background-color 160ms ease, border-color 160ms ease; }\n :root .freelens-power-preferences button.power-settings-icon-action,\n :root .power-preferences-header-actions button.power-settings-icon-action {\n display: inline-flex; align-items: center; justify-content: center;\n width: 32px; height: 32px; min-width: 32px; padding: 0; flex-shrink: 0;\n border: 0; border-radius: 6px; background: transparent; color: var(--textColorSecondary); cursor: pointer;\n transition: color 160ms ease, background-color 160ms ease;\n }\n :root .freelens-power-preferences button.power-settings-icon-action:not(:disabled):hover,\n :root .power-preferences-header-actions button.power-settings-icon-action:not(:disabled):hover {\n color: var(--primary); background: color-mix(in srgb, var(--primary) 10%, transparent);\n }\n :root .freelens-power-preferences button.power-settings-icon-action:focus-visible,\n :root .power-preferences-header-actions button.power-settings-icon-action:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }\n :root .freelens-power-preferences button.power-settings-icon-action:disabled,\n :root .power-preferences-header-actions button.power-settings-icon-action:disabled { opacity: 0.4; cursor: default; }\n .freelens-power-preferences h3 {\n display: flex; align-items: center; gap: 10px; margin: 0 0 24px; font-size: 16px; font-weight: 600; color: var(--textColorAccent);\n }\n .freelens-power-preferences label[data-testid=\"switch\"] {\n display: flex; width: 100%; align-items: center; justify-content: space-between;\n }\n .freelens-power-preferences .power-settings-hint {\n margin-top: 6px; font-size: 12px; line-height: 1.5; color: var(--textColorSecondary);\n }\n @media (max-width: 760px) {\n .freelens-power-preferences section { padding: 18px; }\n .freelens-power-preferences .power-settings-row { flex-wrap: wrap; }\n }\n`;\n\nconst sectionLinks = [\n { id: \"safety\", title: \"Safety\", icon: \"lock_outline\" },\n { id: \"appearance\", title: \"Appearance\", icon: \"palette\" },\n { id: \"monitoring\", title: \"Monitoring\", icon: \"show_chart\" },\n { id: \"shell\", title: \"Node shell\", icon: \"vpn_key\" },\n { id: \"ai\", title: \"AI\", icon: \"chat_bubble_outline\" },\n { id: \"dashboard\", title: \"Dashboard\", icon: \"dashboard\" },\n];\n\nexport const PowerPreferences = () => (\n <div className=\"freelens-power-preferences\">\n <style>{styles}</style>\n <SettingsExport />\n <section aria-label=\"Freelens Power\">\n <PowerPreferencesToggle />\n </section>\n <nav className=\"power-preferences-section-links\" aria-label=\"Settings sections\">\n {sectionLinks.map(({ id, title, icon }) => (\n <Renderer.Component.Button\n key={id}\n className=\"power-settings-section-link\"\n title={`Jump to ${title} settings`}\n onClick={(event) => {\n event.preventDefault();\n const section = document.getElementById(`freelens-power-${id}-section`);\n requestAnimationFrame(() => {\n if (!section?.isConnected) return;\n section.focus({ preventScroll: true });\n section.scrollIntoView({ block: \"start\", behavior: \"instant\" });\n });\n }}\n >\n <Renderer.Component.Icon material={icon} size={16} /> {title}\n </Renderer.Component.Button>\n ))}\n </nav>\n <section id=\"freelens-power-safety-section\" tabIndex={-1} aria-labelledby=\"freelens-power-safety-title\">\n <h3 id=\"freelens-power-safety-title\">\n <Renderer.Component.Icon material=\"lock_outline\" size={20} /> Read-only mode\n </h3>\n <ReadOnlyPreferences />\n </section>\n <section id=\"freelens-power-appearance-section\" tabIndex={-1} aria-labelledby=\"freelens-power-appearance-title\">\n <h3 id=\"freelens-power-appearance-title\">\n <Renderer.Component.Icon material=\"palette\" size={20} /> Appearance\n </h3>\n <AppearancePreferences />\n <div style={{ display: \"flex\", justifyContent: \"flex-end\", marginTop: 20 }}>\n <Renderer.Component.Button\n style={{ display: \"inline-flex\", alignItems: \"center\", gap: 6 }}\n onClick={() => Renderer.Navigation.navigate(`/extension/${EXTENSION_ID}/cluster-icons`)}\n title=\"Create matching icons for your saved clusters\"\n >\n <Renderer.Component.Icon material=\"palette\" size={16} /> Cluster icons\n </Renderer.Component.Button>\n </div>\n </section>\n <section id=\"freelens-power-monitoring-section\" tabIndex={-1} aria-labelledby=\"freelens-power-monitoring-title\">\n <h3 id=\"freelens-power-monitoring-title\">\n <Renderer.Component.Icon material=\"show_chart\" size={20} /> Monitoring\n </h3>\n <MonitoringPreferences />\n </section>\n <section id=\"freelens-power-shell-section\" tabIndex={-1} aria-labelledby=\"freelens-power-shell-title\">\n <h3 id=\"freelens-power-shell-title\">\n <Renderer.Component.Icon material=\"vpn_key\" size={20} /> Secure Node Shell\n </h3>\n <NodeShellPreferences />\n </section>\n <section id=\"freelens-power-ai-section\" tabIndex={-1} aria-labelledby=\"freelens-power-ai-title\">\n <h3 id=\"freelens-power-ai-title\">\n <Renderer.Component.Icon material=\"chat_bubble_outline\" size={20} /> AI assistant\n </h3>\n <AiPreferences />\n </section>\n <section id=\"freelens-power-dashboard-section\" tabIndex={-1} aria-labelledby=\"freelens-power-dashboard-title\">\n <h3 id=\"freelens-power-dashboard-title\">\n <Renderer.Component.Icon material=\"dashboard\" size={20} /> Dashboard\n </h3>\n <DashboardPreferences />\n </section>\n </div>\n);\n"],"mappings":"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"}
1
+ {"version":3,"file":"preferences.js","names":[],"sources":["../../../src/renderer/preferences/preferences.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { EXTENSION_ID } from \"../../common/extension-info\";\nimport { AiPreferences } from \"./ai-preferences\";\nimport { AppearancePreferences } from \"./appearance-preferences\";\nimport { DashboardPreferences } from \"./dashboard-preferences\";\nimport { MonitoringPreferences } from \"./monitoring-preferences\";\nimport { NodeShellPreferences } from \"./node-shell-preferences\";\nimport { PowerPreferencesToggle } from \"./power-preferences\";\nimport { ReadOnlyPreferences } from \"./read-only-preferences\";\nimport { SettingsExport } from \"./settings-export\";\n\nconst styles = `\n .power-preferences-heading { display: flex; align-items: center; justify-content: space-between; gap: 16px; }\n .power-preferences-header-actions { display: inline-flex; margin-left: auto; flex-shrink: 0; }\n #freelens-power-settings > .SubTitle,\n #freelens-power-settings > .hint,\n #freelens-power-settings + hr { display: none; }\n .freelens-power-preferences { display: grid; gap: 24px; max-width: 760px; container: power-preferences / inline-size; }\n .freelens-power-preferences .power-preferences-section-links {\n display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); width: 100%; gap: 8px;\n }\n :root .freelens-power-preferences .power-preferences-section-links button.power-settings-section-link {\n display: inline-flex; align-items: center; justify-content: center; gap: 6px;\n width: 100%; min-width: 0; min-height: 32px; padding: 4px 2px;\n border: 0; border-radius: 4px; background: transparent; box-shadow: none;\n color: var(--textColorSecondary); text-transform: none; white-space: nowrap; cursor: pointer;\n }\n :root .freelens-power-preferences .power-preferences-section-links button.power-settings-section-link:hover {\n color: var(--primary); background: transparent; box-shadow: none;\n }\n :root .freelens-power-preferences .power-preferences-section-links button.power-settings-section-link::before {\n content: none;\n }\n :root .freelens-power-preferences .power-preferences-section-links button.power-settings-section-link:focus-visible {\n color: var(--primary); background: transparent; box-shadow: none;\n outline: 2px solid var(--primary); outline-offset: 2px;\n }\n @container power-preferences (max-width: 660px) {\n .freelens-power-preferences .power-preferences-section-links { grid-template-columns: repeat(3, minmax(0, 1fr)); }\n }\n @container power-preferences (max-width: 360px) {\n .freelens-power-preferences .power-preferences-section-links { grid-template-columns: repeat(2, minmax(0, 1fr)); }\n }\n .freelens-power-preferences section[id] { scroll-margin-top: 16px; }\n .freelens-power-preferences section {\n padding: 24px; border: 1px solid var(--borderFaintColor);\n border-radius: 12px; background: var(--contentColor);\n }\n .freelens-power-preferences button, .freelens-power-preferences .Button { transition: color 160ms ease, background-color 160ms ease, border-color 160ms ease; }\n :root .freelens-power-preferences button.power-settings-icon-action,\n :root .power-preferences-header-actions button.power-settings-icon-action {\n display: inline-flex; align-items: center; justify-content: center;\n width: 32px; height: 32px; min-width: 32px; padding: 0; flex-shrink: 0;\n border: 0; border-radius: 6px; background: transparent; color: var(--textColorSecondary); cursor: pointer;\n transition: color 160ms ease, background-color 160ms ease;\n }\n :root .freelens-power-preferences button.power-settings-icon-action:not(:disabled):hover,\n :root .power-preferences-header-actions button.power-settings-icon-action:not(:disabled):hover {\n color: var(--primary); background: color-mix(in srgb, var(--primary) 10%, transparent);\n }\n :root .freelens-power-preferences button.power-settings-icon-action:focus-visible,\n :root .power-preferences-header-actions button.power-settings-icon-action:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }\n :root .freelens-power-preferences button.power-settings-icon-action:disabled,\n :root .power-preferences-header-actions button.power-settings-icon-action:disabled { opacity: 0.4; cursor: default; }\n .freelens-power-preferences h3 {\n display: flex; align-items: center; gap: 10px; margin: 0 0 24px; font-size: 16px; font-weight: 600; color: var(--textColorAccent);\n }\n .freelens-power-preferences label[data-testid=\"switch\"] {\n display: flex; width: 100%; align-items: center; justify-content: space-between;\n }\n .freelens-power-preferences .power-settings-hint {\n margin-top: 6px; font-size: 12px; line-height: 1.5; color: var(--textColorSecondary);\n }\n @media (max-width: 760px) {\n .freelens-power-preferences section { padding: 18px; }\n .freelens-power-preferences .power-settings-row { flex-wrap: wrap; }\n }\n`;\n\nconst sectionLinks = [\n { id: \"safety\", title: \"Safety\", icon: \"lock_outline\" },\n { id: \"appearance\", title: \"Appearance\", icon: \"palette\" },\n { id: \"monitoring\", title: \"Monitoring\", icon: \"show_chart\" },\n { id: \"shell\", title: \"Node shell\", icon: \"vpn_key\" },\n { id: \"ai\", title: \"AI\", icon: \"chat_bubble_outline\" },\n { id: \"dashboard\", title: \"Dashboard\", icon: \"dashboard\" },\n];\n\nexport const PowerPreferences = () => (\n <div className=\"freelens-power-preferences\">\n <style>{styles}</style>\n <SettingsExport />\n <section aria-label=\"Freelens Power\">\n <PowerPreferencesToggle />\n </section>\n <nav className=\"power-preferences-section-links\" aria-label=\"Settings sections\">\n {sectionLinks.map(({ id, title, icon }) => (\n <Renderer.Component.Button\n key={id}\n className=\"power-settings-section-link\"\n title={`Jump to ${title} settings`}\n onClick={(event) => {\n event.preventDefault();\n const section = document.getElementById(`freelens-power-${id}-section`);\n requestAnimationFrame(() => {\n if (!section?.isConnected) return;\n section.focus({ preventScroll: true });\n section.scrollIntoView({ block: \"start\", behavior: \"instant\" });\n });\n }}\n >\n <Renderer.Component.Icon material={icon} size={16} /> {title}\n </Renderer.Component.Button>\n ))}\n </nav>\n <section id=\"freelens-power-safety-section\" tabIndex={-1} aria-labelledby=\"freelens-power-safety-title\">\n <h3 id=\"freelens-power-safety-title\">\n <Renderer.Component.Icon material=\"lock_outline\" size={20} /> Read-only mode\n </h3>\n <ReadOnlyPreferences />\n </section>\n <section id=\"freelens-power-appearance-section\" tabIndex={-1} aria-labelledby=\"freelens-power-appearance-title\">\n <h3 id=\"freelens-power-appearance-title\">\n <Renderer.Component.Icon material=\"palette\" size={20} /> Appearance\n </h3>\n <AppearancePreferences />\n <div style={{ display: \"flex\", justifyContent: \"flex-end\", marginTop: 20 }}>\n <Renderer.Component.Button\n style={{ display: \"inline-flex\", alignItems: \"center\", gap: 6 }}\n onClick={() => Renderer.Navigation.navigate(`/extension/${EXTENSION_ID}/cluster-icons`)}\n title=\"Create matching icons for your saved clusters\"\n >\n <Renderer.Component.Icon material=\"palette\" size={16} /> Cluster icons\n </Renderer.Component.Button>\n </div>\n </section>\n <section id=\"freelens-power-monitoring-section\" tabIndex={-1} aria-labelledby=\"freelens-power-monitoring-title\">\n <h3 id=\"freelens-power-monitoring-title\">\n <Renderer.Component.Icon material=\"show_chart\" size={20} /> Monitoring\n </h3>\n <MonitoringPreferences />\n </section>\n <section id=\"freelens-power-shell-section\" tabIndex={-1} aria-labelledby=\"freelens-power-shell-title\">\n <h3 id=\"freelens-power-shell-title\">\n <Renderer.Component.Icon material=\"vpn_key\" size={20} /> Secure Node Shell\n </h3>\n <NodeShellPreferences />\n </section>\n <section id=\"freelens-power-ai-section\" tabIndex={-1} aria-labelledby=\"freelens-power-ai-title\">\n <h3 id=\"freelens-power-ai-title\">\n <Renderer.Component.Icon material=\"chat_bubble_outline\" size={20} /> AI assistant\n </h3>\n <AiPreferences />\n </section>\n <section id=\"freelens-power-dashboard-section\" tabIndex={-1} aria-labelledby=\"freelens-power-dashboard-title\">\n <h3 id=\"freelens-power-dashboard-title\">\n <Renderer.Component.Icon material=\"dashboard\" size={20} /> Dashboard\n </h3>\n <DashboardPreferences />\n </section>\n </div>\n);\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  //#region src/renderer/resource-relationships.css.ts
2
2
  var resourceRelationshipsStyles = `
3
- .power-relationships{height:100%;overflow:auto;box-sizing:border-box;min-width:0;padding:24px;max-width:1550px;margin:auto;color:var(--textColorPrimary)}.power-relationships h2{margin:0}.power-relationships .page-heading{display:flex;justify-content:space-between;align-items:center;gap:16px;flex-wrap:wrap;margin-bottom:8px}.power-relationships .intro{color:var(--textColorSecondary);max-width:900px;line-height:1.6}.power-relationships .controls{display:flex;gap:24px;align-items:end;flex-wrap:wrap;margin:24px 0}.power-relationships .control{min-width:200px;flex:1;max-width:340px}.power-relationships .control>span{display:block;font-size:12px;margin-bottom:8px;color:var(--textColorSecondary)}.power-relationships button{min-height:32px;cursor:pointer}.power-relationships .refresh{background:transparent;border:1px solid var(--borderColor);border-radius:5px;padding:5px 10px;color:var(--textColorPrimary);display:flex;align-items:center;gap:8px}.power-relationships .notice{padding:14px 16px;background:var(--contentColor);border:1px solid var(--borderColor);border-radius:7px;margin:16px 0;line-height:1.6}.power-relationships .warning{color:var(--colorWarning)}.power-relationships .healthy{color:var(--colorOk)}.power-relationships .neutral{color:var(--textColorSecondary)}.power-relationships .graph-scroll{min-width:0;max-width:100%;overflow:auto;margin-top:24px}.power-relationships .graph{min-width:1000px;display:grid;grid-template-columns:repeat(4,minmax(180px,1fr));column-gap:64px;row-gap:16px;position:relative;padding:6px 2px 56px}.power-relationships .graph.hide-upstreams{min-width:740px;grid-template-columns:repeat(3,minmax(180px,1fr))}.power-relationships .graph-option{display:inline-flex;align-items:center;justify-content:center;gap:8px;flex-shrink:0;min-height:32px;padding:5px 10px;border:1px solid var(--borderColor);border-radius:5px;background:transparent;color:var(--textColorPrimary);font:inherit;cursor:pointer;transition:background-color .15s,border-color .15s}.power-relationships .graph-option[aria-pressed="true"]{background:color-mix(in srgb,var(--primary) 14%,var(--contentColor));border-color:var(--primary);color:var(--primary)}.power-relationships .graph-option:hover,.power-relationships .refresh:hover{background:color-mix(in srgb,var(--primary) 10%,var(--contentColor))}.power-relationships .graph-option:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.power-relationships .column{position:relative;min-width:0;display:grid;grid-row:1/-1;grid-template-rows:subgrid}.power-relationships .column>h3{font-size:13px;margin:0;color:var(--textColorSecondary);font-weight:500}.power-relationships .card{background:var(--contentColor);border:1px solid var(--borderColor);border-radius:8px;padding:14px;margin:0;position:relative;z-index:1;overflow-wrap:anywhere}.power-relationships .card-kind{display:flex;align-items:center;gap:7px;font-size:11px;color:var(--textColorSecondary);margin-bottom:6px}.power-relationships .resource-name{font-size:14px;font-weight:600;color:var(--textColorPrimary);text-align:left;background:transparent;border:0;padding:0;max-width:100%}.power-relationships .resource-name:hover{color:var(--primary)}.power-relationships .card p{font-size:12px;line-height:1.5;margin:6px 0 0}.power-relationships .relation-label{font-size:10px;text-transform:uppercase;letter-spacing:.04em;color:var(--textColorSecondary);margin-bottom:6px}.power-relationships .resource-connections{position:absolute;inset:0;overflow:visible;pointer-events:none;color:var(--textColorSecondary)}.power-relationships .connection{fill:none;stroke:currentColor;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;opacity:.55;transition:opacity .15s,stroke .15s,stroke-width .15s}.power-relationships .connection.highlighted{opacity:1;stroke:var(--primary);stroke-width:2.5}.power-relationships .connection.dimmed{opacity:.12}.power-relationships .scope{max-width:68ch;margin-top:8px;color:var(--textColorSecondary);font-size:12px;line-height:1.6}.power-relationships .scope>span{display:block;margin-top:3px}.power-relationships details{margin-top:16px}.power-relationships summary{cursor:pointer;min-height:32px}.power-relationships .edge-list{columns:2;line-height:1.7;font-size:12px;list-style:none;padding:0}.power-relationships .empty{padding:28px;border:1px dashed var(--borderColor);border-radius:8px;color:var(--textColorSecondary);text-align:center}.power-relationships .relationship-summary{padding:16px;display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:12px 24px}.power-relationships .ports{margin-left:auto;justify-content:flex-end;display:flex;gap:12px;flex-wrap:wrap;font-size:12px}.power-relationships .ports span{padding:4px 8px;border-radius:4px;background:var(--contentColor);border:1px solid var(--borderColor)}@media(max-width:900px){.power-relationships{padding:16px}.power-relationships .controls{gap:16px}.power-relationships .edge-list{columns:1}}`;
3
+ .power-relationships{height:100%;overflow:auto;box-sizing:border-box;min-width:0;padding:24px;max-width:1550px;margin:auto;color:var(--textColorPrimary)}.power-relationships h2{margin:0}.power-relationships .page-heading{display:flex;justify-content:space-between;align-items:center;gap:16px;flex-wrap:wrap;margin-bottom:8px}.power-relationships .intro{color:var(--textColorSecondary);max-width:900px;line-height:1.6}.power-relationships .controls{display:flex;gap:24px;align-items:end;flex-wrap:wrap;margin:24px 0}.power-relationships .control{min-width:200px;flex:1;max-width:340px}.power-relationships .control>span{display:block;font-size:12px;margin-bottom:8px;color:var(--textColorSecondary)}.power-relationships button{min-height:32px;cursor:pointer}.power-relationships .refresh{background:transparent;border:1px solid var(--borderColor);border-radius:5px;padding:5px 10px;color:var(--textColorPrimary);display:flex;align-items:center;gap:8px}.power-relationships .notice{padding:14px 16px;background:var(--contentColor);border:1px solid var(--borderColor);border-radius:7px;margin:16px 0;line-height:1.6}.power-relationships .warning{color:var(--colorWarning)}.power-relationships .healthy{color:var(--colorOk)}.power-relationships .neutral{color:var(--textColorSecondary)}.power-relationships .graph-scroll{min-width:0;max-width:100%;overflow:auto;margin-top:24px}.power-relationships .graph{min-width:1000px;display:grid;grid-template-columns:repeat(4,minmax(180px,1fr));column-gap:64px;row-gap:16px;position:relative;padding:6px 2px 56px}.power-relationships .graph.upstream-gutter{padding-left:42px}.power-relationships .gateway-evidence{font-size:11px}.power-relationships .gateway-evidence summary{min-height:24px}.power-relationships .graph.hide-upstreams{min-width:740px;grid-template-columns:repeat(3,minmax(180px,1fr))}.power-relationships .graph-option{display:inline-flex;align-items:center;justify-content:center;gap:8px;flex-shrink:0;min-height:32px;padding:5px 10px;border:1px solid var(--borderColor);border-radius:5px;background:transparent;color:var(--textColorPrimary);font:inherit;cursor:pointer;transition:background-color .15s,border-color .15s}.power-relationships .graph-option[aria-pressed="true"]{background:color-mix(in srgb,var(--primary) 14%,var(--contentColor));border-color:var(--primary);color:var(--primary)}.power-relationships .graph-option:hover,.power-relationships .refresh:hover{background:color-mix(in srgb,var(--primary) 10%,var(--contentColor))}.power-relationships .graph-option:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.power-relationships .column{position:relative;min-width:0;display:grid;grid-row:1/-1;grid-template-rows:subgrid}.power-relationships .column>h3{font-size:13px;margin:0;color:var(--textColorSecondary);font-weight:500}.power-relationships .card{background:var(--contentColor);border:1px solid var(--borderColor);border-radius:8px;padding:14px;margin:0;position:relative;z-index:1;overflow-wrap:anywhere}.power-relationships .card-kind{display:flex;align-items:center;gap:7px;font-size:11px;color:var(--textColorSecondary);margin-bottom:6px}.power-relationships .resource-name{font-size:14px;font-weight:600;color:var(--textColorPrimary);text-align:left;background:transparent;border:0;padding:0;max-width:100%}.power-relationships .resource-name:hover{color:var(--primary)}.power-relationships .card p{font-size:12px;line-height:1.5;margin:6px 0 0}.power-relationships .relation-label{font-size:10px;text-transform:uppercase;letter-spacing:.04em;color:var(--textColorSecondary);margin-bottom:6px}.power-relationships .resource-connections{position:absolute;inset:0;overflow:visible;pointer-events:none;color:var(--textColorSecondary)}.power-relationships .connection{fill:none;stroke:currentColor;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;opacity:.55;transition:opacity .15s,stroke .15s,stroke-width .15s}.power-relationships .connection.highlighted{opacity:1;stroke:var(--primary);stroke-width:2.5}.power-relationships .connection.dimmed{opacity:.12}.power-relationships .scope{max-width:68ch;margin-top:8px;color:var(--textColorSecondary);font-size:12px;line-height:1.6}.power-relationships .scope>span{display:block;margin-top:3px}.power-relationships details{margin-top:16px}.power-relationships summary{cursor:pointer;min-height:32px}.power-relationships .edge-list{columns:2;line-height:1.7;font-size:12px;list-style:none;padding:0}.power-relationships .empty{padding:28px;border:1px dashed var(--borderColor);border-radius:8px;color:var(--textColorSecondary);text-align:center}.power-relationships .relationship-summary{padding:16px;display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:12px 24px}.power-relationships .ports{margin-left:auto;justify-content:flex-end;display:flex;gap:12px;flex-wrap:wrap;font-size:12px}.power-relationships .ports span{padding:4px 8px;border-radius:4px;background:var(--contentColor);border:1px solid var(--borderColor)}@media(max-width:900px){.power-relationships{padding:16px}.power-relationships .controls{gap:16px}.power-relationships .edge-list{columns:1}}`;
4
4
  //#endregion
5
5
  exports.resourceRelationshipsStyles = resourceRelationshipsStyles;
6
6
 
@@ -1 +1 @@
1
- {"version":3,"file":"resource-relationships.css.js","names":[],"sources":["../../src/renderer/resource-relationships.css.ts"],"sourcesContent":["export const resourceRelationshipsStyles = `\n.power-relationships{height:100%;overflow:auto;box-sizing:border-box;min-width:0;padding:24px;max-width:1550px;margin:auto;color:var(--textColorPrimary)}.power-relationships h2{margin:0}.power-relationships .page-heading{display:flex;justify-content:space-between;align-items:center;gap:16px;flex-wrap:wrap;margin-bottom:8px}.power-relationships .intro{color:var(--textColorSecondary);max-width:900px;line-height:1.6}.power-relationships .controls{display:flex;gap:24px;align-items:end;flex-wrap:wrap;margin:24px 0}.power-relationships .control{min-width:200px;flex:1;max-width:340px}.power-relationships .control>span{display:block;font-size:12px;margin-bottom:8px;color:var(--textColorSecondary)}.power-relationships button{min-height:32px;cursor:pointer}.power-relationships .refresh{background:transparent;border:1px solid var(--borderColor);border-radius:5px;padding:5px 10px;color:var(--textColorPrimary);display:flex;align-items:center;gap:8px}.power-relationships .notice{padding:14px 16px;background:var(--contentColor);border:1px solid var(--borderColor);border-radius:7px;margin:16px 0;line-height:1.6}.power-relationships .warning{color:var(--colorWarning)}.power-relationships .healthy{color:var(--colorOk)}.power-relationships .neutral{color:var(--textColorSecondary)}.power-relationships .graph-scroll{min-width:0;max-width:100%;overflow:auto;margin-top:24px}.power-relationships .graph{min-width:1000px;display:grid;grid-template-columns:repeat(4,minmax(180px,1fr));column-gap:64px;row-gap:16px;position:relative;padding:6px 2px 56px}.power-relationships .graph.hide-upstreams{min-width:740px;grid-template-columns:repeat(3,minmax(180px,1fr))}.power-relationships .graph-option{display:inline-flex;align-items:center;justify-content:center;gap:8px;flex-shrink:0;min-height:32px;padding:5px 10px;border:1px solid var(--borderColor);border-radius:5px;background:transparent;color:var(--textColorPrimary);font:inherit;cursor:pointer;transition:background-color .15s,border-color .15s}.power-relationships .graph-option[aria-pressed=\"true\"]{background:color-mix(in srgb,var(--primary) 14%,var(--contentColor));border-color:var(--primary);color:var(--primary)}.power-relationships .graph-option:hover,.power-relationships .refresh:hover{background:color-mix(in srgb,var(--primary) 10%,var(--contentColor))}.power-relationships .graph-option:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.power-relationships .column{position:relative;min-width:0;display:grid;grid-row:1/-1;grid-template-rows:subgrid}.power-relationships .column>h3{font-size:13px;margin:0;color:var(--textColorSecondary);font-weight:500}.power-relationships .card{background:var(--contentColor);border:1px solid var(--borderColor);border-radius:8px;padding:14px;margin:0;position:relative;z-index:1;overflow-wrap:anywhere}.power-relationships .card-kind{display:flex;align-items:center;gap:7px;font-size:11px;color:var(--textColorSecondary);margin-bottom:6px}.power-relationships .resource-name{font-size:14px;font-weight:600;color:var(--textColorPrimary);text-align:left;background:transparent;border:0;padding:0;max-width:100%}.power-relationships .resource-name:hover{color:var(--primary)}.power-relationships .card p{font-size:12px;line-height:1.5;margin:6px 0 0}.power-relationships .relation-label{font-size:10px;text-transform:uppercase;letter-spacing:.04em;color:var(--textColorSecondary);margin-bottom:6px}.power-relationships .resource-connections{position:absolute;inset:0;overflow:visible;pointer-events:none;color:var(--textColorSecondary)}.power-relationships .connection{fill:none;stroke:currentColor;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;opacity:.55;transition:opacity .15s,stroke .15s,stroke-width .15s}.power-relationships .connection.highlighted{opacity:1;stroke:var(--primary);stroke-width:2.5}.power-relationships .connection.dimmed{opacity:.12}.power-relationships .scope{max-width:68ch;margin-top:8px;color:var(--textColorSecondary);font-size:12px;line-height:1.6}.power-relationships .scope>span{display:block;margin-top:3px}.power-relationships details{margin-top:16px}.power-relationships summary{cursor:pointer;min-height:32px}.power-relationships .edge-list{columns:2;line-height:1.7;font-size:12px;list-style:none;padding:0}.power-relationships .empty{padding:28px;border:1px dashed var(--borderColor);border-radius:8px;color:var(--textColorSecondary);text-align:center}.power-relationships .relationship-summary{padding:16px;display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:12px 24px}.power-relationships .ports{margin-left:auto;justify-content:flex-end;display:flex;gap:12px;flex-wrap:wrap;font-size:12px}.power-relationships .ports span{padding:4px 8px;border-radius:4px;background:var(--contentColor);border:1px solid var(--borderColor)}@media(max-width:900px){.power-relationships{padding:16px}.power-relationships .controls{gap:16px}.power-relationships .edge-list{columns:1}}`;\n"],"mappings":";AAAA,IAAa,8BAA8B"}
1
+ {"version":3,"file":"resource-relationships.css.js","names":[],"sources":["../../src/renderer/resource-relationships.css.ts"],"sourcesContent":["export const resourceRelationshipsStyles = `\n.power-relationships{height:100%;overflow:auto;box-sizing:border-box;min-width:0;padding:24px;max-width:1550px;margin:auto;color:var(--textColorPrimary)}.power-relationships h2{margin:0}.power-relationships .page-heading{display:flex;justify-content:space-between;align-items:center;gap:16px;flex-wrap:wrap;margin-bottom:8px}.power-relationships .intro{color:var(--textColorSecondary);max-width:900px;line-height:1.6}.power-relationships .controls{display:flex;gap:24px;align-items:end;flex-wrap:wrap;margin:24px 0}.power-relationships .control{min-width:200px;flex:1;max-width:340px}.power-relationships .control>span{display:block;font-size:12px;margin-bottom:8px;color:var(--textColorSecondary)}.power-relationships button{min-height:32px;cursor:pointer}.power-relationships .refresh{background:transparent;border:1px solid var(--borderColor);border-radius:5px;padding:5px 10px;color:var(--textColorPrimary);display:flex;align-items:center;gap:8px}.power-relationships .notice{padding:14px 16px;background:var(--contentColor);border:1px solid var(--borderColor);border-radius:7px;margin:16px 0;line-height:1.6}.power-relationships .warning{color:var(--colorWarning)}.power-relationships .healthy{color:var(--colorOk)}.power-relationships .neutral{color:var(--textColorSecondary)}.power-relationships .graph-scroll{min-width:0;max-width:100%;overflow:auto;margin-top:24px}.power-relationships .graph{min-width:1000px;display:grid;grid-template-columns:repeat(4,minmax(180px,1fr));column-gap:64px;row-gap:16px;position:relative;padding:6px 2px 56px}.power-relationships .graph.upstream-gutter{padding-left:42px}.power-relationships .gateway-evidence{font-size:11px}.power-relationships .gateway-evidence summary{min-height:24px}.power-relationships .graph.hide-upstreams{min-width:740px;grid-template-columns:repeat(3,minmax(180px,1fr))}.power-relationships .graph-option{display:inline-flex;align-items:center;justify-content:center;gap:8px;flex-shrink:0;min-height:32px;padding:5px 10px;border:1px solid var(--borderColor);border-radius:5px;background:transparent;color:var(--textColorPrimary);font:inherit;cursor:pointer;transition:background-color .15s,border-color .15s}.power-relationships .graph-option[aria-pressed=\"true\"]{background:color-mix(in srgb,var(--primary) 14%,var(--contentColor));border-color:var(--primary);color:var(--primary)}.power-relationships .graph-option:hover,.power-relationships .refresh:hover{background:color-mix(in srgb,var(--primary) 10%,var(--contentColor))}.power-relationships .graph-option:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.power-relationships .column{position:relative;min-width:0;display:grid;grid-row:1/-1;grid-template-rows:subgrid}.power-relationships .column>h3{font-size:13px;margin:0;color:var(--textColorSecondary);font-weight:500}.power-relationships .card{background:var(--contentColor);border:1px solid var(--borderColor);border-radius:8px;padding:14px;margin:0;position:relative;z-index:1;overflow-wrap:anywhere}.power-relationships .card-kind{display:flex;align-items:center;gap:7px;font-size:11px;color:var(--textColorSecondary);margin-bottom:6px}.power-relationships .resource-name{font-size:14px;font-weight:600;color:var(--textColorPrimary);text-align:left;background:transparent;border:0;padding:0;max-width:100%}.power-relationships .resource-name:hover{color:var(--primary)}.power-relationships .card p{font-size:12px;line-height:1.5;margin:6px 0 0}.power-relationships .relation-label{font-size:10px;text-transform:uppercase;letter-spacing:.04em;color:var(--textColorSecondary);margin-bottom:6px}.power-relationships .resource-connections{position:absolute;inset:0;overflow:visible;pointer-events:none;color:var(--textColorSecondary)}.power-relationships .connection{fill:none;stroke:currentColor;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;opacity:.55;transition:opacity .15s,stroke .15s,stroke-width .15s}.power-relationships .connection.highlighted{opacity:1;stroke:var(--primary);stroke-width:2.5}.power-relationships .connection.dimmed{opacity:.12}.power-relationships .scope{max-width:68ch;margin-top:8px;color:var(--textColorSecondary);font-size:12px;line-height:1.6}.power-relationships .scope>span{display:block;margin-top:3px}.power-relationships details{margin-top:16px}.power-relationships summary{cursor:pointer;min-height:32px}.power-relationships .edge-list{columns:2;line-height:1.7;font-size:12px;list-style:none;padding:0}.power-relationships .empty{padding:28px;border:1px dashed var(--borderColor);border-radius:8px;color:var(--textColorSecondary);text-align:center}.power-relationships .relationship-summary{padding:16px;display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:12px 24px}.power-relationships .ports{margin-left:auto;justify-content:flex-end;display:flex;gap:12px;flex-wrap:wrap;font-size:12px}.power-relationships .ports span{padding:4px 8px;border-radius:4px;background:var(--contentColor);border:1px solid var(--borderColor)}@media(max-width:900px){.power-relationships{padding:16px}.power-relationships .controls{gap:16px}.power-relationships .edge-list{columns:1}}`;\n"],"mappings":";AAAA,IAAa,8BAA8B"}