@64mb/freelens-power 1.2.1 → 1.3.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 (88) hide show
  1. package/README.md +3 -1
  2. package/out/main/common/dashboard-picker.js +4 -4
  3. package/out/main/common/dashboard-picker.js.map +1 -1
  4. package/out/main/common/dashboard-readiness.js +17 -47
  5. package/out/main/common/dashboard-readiness.js.map +1 -1
  6. package/out/main/common/dashboard-selector.js +40 -6
  7. package/out/main/common/dashboard-selector.js.map +1 -1
  8. package/out/main/common/store/monitoring-preferences-store.js +5 -0
  9. package/out/main/common/store/monitoring-preferences-store.js.map +1 -1
  10. package/out/main/dashboard-picker.js +1 -4
  11. package/out/main/dashboard-picker.js.map +1 -1
  12. package/out/main/dashboard-preload.js +110 -0
  13. package/out/main/dashboard-preload.js.map +1 -0
  14. package/out/main/dashboard-views.js +207 -62
  15. package/out/main/dashboard-views.js.map +1 -1
  16. package/out/main/dock-icon.js +25 -10
  17. package/out/main/dock-icon.js.map +1 -1
  18. package/out/main/index.js +2 -0
  19. package/out/main/index.js.map +1 -1
  20. package/out/main/splash-accent.js +84 -0
  21. package/out/main/splash-accent.js.map +1 -0
  22. package/out/renderer/ai-diagnostic-handoff.js +52 -0
  23. package/out/renderer/ai-diagnostic-handoff.js.map +1 -0
  24. package/out/renderer/ai-extras.js +40 -4
  25. package/out/renderer/ai-extras.js.map +1 -1
  26. package/out/renderer/ai.js +48 -56
  27. package/out/renderer/ai.js.map +1 -1
  28. package/out/renderer/appearance.js +6 -2
  29. package/out/renderer/appearance.js.map +1 -1
  30. package/out/renderer/dashboard.js +26 -6
  31. package/out/renderer/dashboard.js.map +1 -1
  32. package/out/renderer/details/hpa-diagnostics-details.js +46 -0
  33. package/out/renderer/details/hpa-diagnostics-details.js.map +1 -0
  34. package/out/renderer/details/namespace-health-details.js +91 -0
  35. package/out/renderer/details/namespace-health-details.js.map +1 -0
  36. package/out/renderer/details/resource-health-details.js +131 -0
  37. package/out/renderer/details/resource-health-details.js.map +1 -0
  38. package/out/renderer/details/service-diagnostics-details.js +163 -0
  39. package/out/renderer/details/service-diagnostics-details.js.map +1 -0
  40. package/out/renderer/diagnostic-explain-button.js +16 -0
  41. package/out/renderer/diagnostic-explain-button.js.map +1 -0
  42. package/out/renderer/diagnostics-state.js +51 -0
  43. package/out/renderer/diagnostics-state.js.map +1 -0
  44. package/out/renderer/diagnostics.css.js +9 -0
  45. package/out/renderer/diagnostics.css.js.map +1 -1
  46. package/out/renderer/diagnostics.js +369 -73
  47. package/out/renderer/diagnostics.js.map +1 -1
  48. package/out/renderer/incident-copy-button.js +50 -0
  49. package/out/renderer/incident-copy-button.js.map +1 -0
  50. package/out/renderer/index.js +91 -19
  51. package/out/renderer/index.js.map +1 -1
  52. package/out/renderer/monitoring-history-collection.js +1 -1
  53. package/out/renderer/preferences/dashboard-preferences.js +16 -16
  54. package/out/renderer/preferences/dashboard-preferences.js.map +1 -1
  55. package/out/renderer/preferences/monitoring-preferences.js +10 -0
  56. package/out/renderer/preferences/monitoring-preferences.js.map +1 -1
  57. package/out/renderer/preferences/preferences.js +88 -0
  58. package/out/renderer/preferences/preferences.js.map +1 -1
  59. package/out/renderer/preferences/settings-modal.js.map +1 -1
  60. package/out/renderer/resource-relationships.css.js +7 -0
  61. package/out/renderer/resource-relationships.css.js.map +1 -0
  62. package/out/renderer/resource-relationships.js +556 -0
  63. package/out/renderer/resource-relationships.js.map +1 -0
  64. package/out/renderer/resource-search-distance.js +24 -0
  65. package/out/renderer/resource-search-distance.js.map +1 -0
  66. package/out/renderer/resource-search-fields.js +55 -0
  67. package/out/renderer/resource-search-fields.js.map +1 -0
  68. package/out/renderer/resource-search-model.js +193 -0
  69. package/out/renderer/resource-search-model.js.map +1 -0
  70. package/out/renderer/resource-search.js +365 -0
  71. package/out/renderer/resource-search.js.map +1 -0
  72. package/out/renderer/src/common/dashboard-picker.js.map +1 -1
  73. package/out/renderer/src/common/dashboard-selector.js.map +1 -1
  74. package/out/renderer/src/common/hpa-diagnostics.js +24 -0
  75. package/out/renderer/src/common/hpa-diagnostics.js.map +1 -0
  76. package/out/renderer/src/common/incident-summary.js +73 -0
  77. package/out/renderer/src/common/incident-summary.js.map +1 -0
  78. package/out/renderer/src/common/resource-health.js +92 -0
  79. package/out/renderer/src/common/resource-health.js.map +1 -0
  80. package/out/renderer/src/common/resource-relationships.js +155 -0
  81. package/out/renderer/src/common/resource-relationships.js.map +1 -0
  82. package/out/renderer/src/common/service-diagnostics.js +50 -0
  83. package/out/renderer/src/common/service-diagnostics.js.map +1 -0
  84. package/out/renderer/src/common/store/monitoring-preferences-store.js +5 -0
  85. package/out/renderer/src/common/store/monitoring-preferences-store.js.map +1 -1
  86. package/out/renderer/src/common/timeline-groups.js +36 -0
  87. package/out/renderer/src/common/timeline-groups.js.map +1 -0
  88. package/package.json +1 -1
@@ -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 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\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 <section 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 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 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 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 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 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":";;;;;;;;;;;;AAWA,IAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0Cf,IAAa,yBACX,oCAAA,KAAC,OAAD;CAAK,WAAU;WAAf;EACE,oCAAA,IAAC,SAAD,EAAA,UAAQ,OAAc,CAAA;EACtB,oCAAA,IAAC,wBAAA,gBAAD,CAAiB,CAAA;EACjB,oCAAA,IAAC,WAAD;GAAS,cAAW;aAClB,oCAAA,IAAC,0BAAA,wBAAD,CAAyB,CAAA;EAClB,CAAA;EACT,oCAAA,KAAC,WAAD;GAAS,mBAAgB;aAAzB,CACE,oCAAA,KAAC,MAAD;IAAI,IAAG;cAAP,CACE,oCAAA,IAAC,mBAAA,SAAS,UAAU,MAApB;KAAyB,UAAS;KAAe,MAAM;IAAK,CAAA,GAAC,iBAC3D;OACJ,oCAAA,IAAC,8BAAA,qBAAD,CAAsB,CAAA,CACf;;EACT,oCAAA,KAAC,WAAD;GAAS,mBAAgB;aAAzB;IACE,oCAAA,KAAC,MAAD;KAAI,IAAG;eAAP,CACE,oCAAA,IAAC,mBAAA,SAAS,UAAU,MAApB;MAAyB,UAAS;MAAU,MAAM;KAAK,CAAA,GAAC,aACtD;;IACJ,oCAAA,IAAC,+BAAA,uBAAD,CAAwB,CAAA;IACxB,oCAAA,IAAC,OAAD;KAAK,OAAO;MAAE,SAAS;MAAQ,gBAAgB;MAAY,WAAW;KAAG;eACvE,oCAAA,KAAC,mBAAA,SAAS,UAAU,QAApB;MACE,OAAO;OAAE,SAAS;OAAe,YAAY;OAAU,KAAK;MAAE;MAC9D,eAAe,mBAAA,SAAS,WAAW,SAAS,cAAc,uBAAA,aAAa,eAAe;MACtF,OAAM;gBAHR,CAKE,oCAAA,IAAC,mBAAA,SAAS,UAAU,MAApB;OAAyB,UAAS;OAAU,MAAM;MAAK,CAAA,GAAC,gBAC/B;;IACxB,CAAA;GACE;;EACT,oCAAA,KAAC,WAAD;GAAS,mBAAgB;aAAzB,CACE,oCAAA,KAAC,MAAD;IAAI,IAAG;cAAP,CACE,oCAAA,IAAC,mBAAA,SAAS,UAAU,MAApB;KAAyB,UAAS;KAAa,MAAM;IAAK,CAAA,GAAC,aACzD;OACJ,oCAAA,IAAC,+BAAA,uBAAD,CAAwB,CAAA,CACjB;;EACT,oCAAA,KAAC,WAAD;GAAS,mBAAgB;aAAzB,CACE,oCAAA,KAAC,MAAD;IAAI,IAAG;cAAP,CACE,oCAAA,IAAC,mBAAA,SAAS,UAAU,MAApB;KAAyB,UAAS;KAAU,MAAM;IAAK,CAAA,GAAC,oBACtD;OACJ,oCAAA,IAAC,+BAAA,sBAAD,CAAuB,CAAA,CAChB;;EACT,oCAAA,KAAC,WAAD;GAAS,mBAAgB;aAAzB,CACE,oCAAA,KAAC,MAAD;IAAI,IAAG;cAAP,CACE,oCAAA,IAAC,mBAAA,SAAS,UAAU,MAApB;KAAyB,UAAS;KAAsB,MAAM;IAAK,CAAA,GAAC,eAClE;OACJ,oCAAA,IAAC,uBAAA,eAAD,CAAgB,CAAA,CACT;;EACT,oCAAA,KAAC,WAAD;GAAS,mBAAgB;aAAzB,CACE,oCAAA,KAAC,MAAD;IAAI,IAAG;cAAP,CACE,oCAAA,IAAC,mBAAA,SAAS,UAAU,MAApB;KAAyB,UAAS;KAAY,MAAM;IAAK,CAAA,GAAC,YACxD;OACJ,oCAAA,IAAC,8BAAA,sBAAD,CAAuB,CAAA,CAChB;;CACN"}
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: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={() => {\n const section = document.getElementById(`freelens-power-${id}-section`);\n section?.focus({ preventScroll: true });\n section?.scrollIntoView({ block: \"start\" });\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":";;;;;;;;;;;;AAWA,IAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiEf,IAAM,eAAe;CACnB;EAAE,IAAI;EAAU,OAAO;EAAU,MAAM;CAAe;CACtD;EAAE,IAAI;EAAc,OAAO;EAAc,MAAM;CAAU;CACzD;EAAE,IAAI;EAAc,OAAO;EAAc,MAAM;CAAa;CAC5D;EAAE,IAAI;EAAS,OAAO;EAAc,MAAM;CAAU;CACpD;EAAE,IAAI;EAAM,OAAO;EAAM,MAAM;CAAsB;CACrD;EAAE,IAAI;EAAa,OAAO;EAAa,MAAM;CAAY;AAC3D;AAEA,IAAa,yBACX,oCAAA,KAAC,OAAD;CAAK,WAAU;WAAf;EACE,oCAAA,IAAC,SAAD,EAAA,UAAQ,OAAc,CAAA;EACtB,oCAAA,IAAC,wBAAA,gBAAD,CAAiB,CAAA;EACjB,oCAAA,IAAC,WAAD;GAAS,cAAW;aAClB,oCAAA,IAAC,0BAAA,wBAAD,CAAyB,CAAA;EAClB,CAAA;EACT,oCAAA,IAAC,OAAD;GAAK,WAAU;GAAkC,cAAW;aACzD,aAAa,KAAK,EAAE,IAAI,OAAO,WAC9B,oCAAA,KAAC,mBAAA,SAAS,UAAU,QAApB;IAEE,WAAU;IACV,OAAO,WAAW,MAAM;IACxB,eAAe;KACb,MAAM,UAAU,SAAS,eAAe,kBAAkB,GAAG,SAAS;KACtE,SAAS,MAAM,EAAE,eAAe,KAAK,CAAC;KACtC,SAAS,eAAe,EAAE,OAAO,QAAQ,CAAC;IAC5C;cARF;KAUE,oCAAA,IAAC,mBAAA,SAAS,UAAU,MAApB;MAAyB,UAAU;MAAM,MAAM;KAAK,CAAA;KAAC;KAAE;IAC9B;MAVpB,EAUoB,CAC5B;EACE,CAAA;EACL,oCAAA,KAAC,WAAD;GAAS,IAAG;GAAgC,UAAU;GAAI,mBAAgB;aAA1E,CACE,oCAAA,KAAC,MAAD;IAAI,IAAG;cAAP,CACE,oCAAA,IAAC,mBAAA,SAAS,UAAU,MAApB;KAAyB,UAAS;KAAe,MAAM;IAAK,CAAA,GAAC,iBAC3D;OACJ,oCAAA,IAAC,8BAAA,qBAAD,CAAsB,CAAA,CACf;;EACT,oCAAA,KAAC,WAAD;GAAS,IAAG;GAAoC,UAAU;GAAI,mBAAgB;aAA9E;IACE,oCAAA,KAAC,MAAD;KAAI,IAAG;eAAP,CACE,oCAAA,IAAC,mBAAA,SAAS,UAAU,MAApB;MAAyB,UAAS;MAAU,MAAM;KAAK,CAAA,GAAC,aACtD;;IACJ,oCAAA,IAAC,+BAAA,uBAAD,CAAwB,CAAA;IACxB,oCAAA,IAAC,OAAD;KAAK,OAAO;MAAE,SAAS;MAAQ,gBAAgB;MAAY,WAAW;KAAG;eACvE,oCAAA,KAAC,mBAAA,SAAS,UAAU,QAApB;MACE,OAAO;OAAE,SAAS;OAAe,YAAY;OAAU,KAAK;MAAE;MAC9D,eAAe,mBAAA,SAAS,WAAW,SAAS,cAAc,uBAAA,aAAa,eAAe;MACtF,OAAM;gBAHR,CAKE,oCAAA,IAAC,mBAAA,SAAS,UAAU,MAApB;OAAyB,UAAS;OAAU,MAAM;MAAK,CAAA,GAAC,gBAC/B;;IACxB,CAAA;GACE;;EACT,oCAAA,KAAC,WAAD;GAAS,IAAG;GAAoC,UAAU;GAAI,mBAAgB;aAA9E,CACE,oCAAA,KAAC,MAAD;IAAI,IAAG;cAAP,CACE,oCAAA,IAAC,mBAAA,SAAS,UAAU,MAApB;KAAyB,UAAS;KAAa,MAAM;IAAK,CAAA,GAAC,aACzD;OACJ,oCAAA,IAAC,+BAAA,uBAAD,CAAwB,CAAA,CACjB;;EACT,oCAAA,KAAC,WAAD;GAAS,IAAG;GAA+B,UAAU;GAAI,mBAAgB;aAAzE,CACE,oCAAA,KAAC,MAAD;IAAI,IAAG;cAAP,CACE,oCAAA,IAAC,mBAAA,SAAS,UAAU,MAApB;KAAyB,UAAS;KAAU,MAAM;IAAK,CAAA,GAAC,oBACtD;OACJ,oCAAA,IAAC,+BAAA,sBAAD,CAAuB,CAAA,CAChB;;EACT,oCAAA,KAAC,WAAD;GAAS,IAAG;GAA4B,UAAU;GAAI,mBAAgB;aAAtE,CACE,oCAAA,KAAC,MAAD;IAAI,IAAG;cAAP,CACE,oCAAA,IAAC,mBAAA,SAAS,UAAU,MAApB;KAAyB,UAAS;KAAsB,MAAM;IAAK,CAAA,GAAC,eAClE;OACJ,oCAAA,IAAC,uBAAA,eAAD,CAAgB,CAAA,CACT;;EACT,oCAAA,KAAC,WAAD;GAAS,IAAG;GAAmC,UAAU;GAAI,mBAAgB;aAA7E,CACE,oCAAA,KAAC,MAAD;IAAI,IAAG;cAAP,CACE,oCAAA,IAAC,mBAAA,SAAS,UAAU,MAApB;KAAyB,UAAS;KAAY,MAAM;IAAK,CAAA,GAAC,YACxD;OACJ,oCAAA,IAAC,8BAAA,sBAAD,CAAuB,CAAA,CAChB;;CACN"}
@@ -1 +1 @@
1
- {"version":3,"file":"settings-modal.js","names":[],"sources":["../../../src/renderer/preferences/settings-modal.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { type ReactNode, useEffect } from \"react\";\n\nexport function SettingsModal({\n id,\n title,\n onClose,\n children,\n}: {\n id: string;\n title: string;\n onClose: () => void;\n children: ReactNode;\n}) {\n useEffect(() => {\n const closeOnEscape = (event: KeyboardEvent) => {\n if (event.key !== \"Escape\") return;\n event.preventDefault();\n event.stopPropagation();\n onClose();\n };\n document.addEventListener(\"keydown\", closeOnEscape, true);\n return () => document.removeEventListener(\"keydown\", closeOnEscape, true);\n }, [onClose]);\n return (\n <Renderer.Component.Dialog isOpen close={onClose} modal className=\"power-dashboard-dialog power-settings-dialog\">\n <style>{`\n .Dialog.power-settings-dialog { background: rgb(0 0 0 / 55%); z-index: 12000; }\n .power-settings-dialog > .box { border-radius: 14px; background: var(--mainBackground, #252a2e); border: 1px solid var(--borderFaintColor, #555); box-shadow: 0 20px 70px rgb(0 0 0 / 45%); overflow: visible; }\n .power-settings-dialog .power-ai-prompt { width: 100%; min-height: 120px; max-height: 260px; field-sizing: content; overflow-y: auto; padding: 12px; box-sizing: border-box; border: 1px solid var(--borderFaintColor); border-radius: 8px; background: var(--contentColor); color: var(--textColorPrimary); font: inherit; resize: none; }\n .power-settings-dialog .power-ai-prompt:focus { outline: 1px solid var(--primary); border-color: var(--primary); }\n .power-settings-dialog .power-dashboard-selector-editor { display: grid; gap: 12px; }\n .power-settings-dialog .power-dashboard-selector-field { display: flex; align-items: center; gap: 8px; }\n .power-settings-dialog .power-dashboard-selector-input { flex: 1; min-width: 0; border: 1px solid var(--borderFaintColor); border-radius: 7px; padding: 4px 8px; background: var(--contentColor); transition: border-color 160ms ease; }\n .power-settings-dialog .power-dashboard-selector-input:focus-within { border-color: var(--primary); }\n .power-settings-dialog .power-dashboard-selector-input .input-area { border-bottom: 0; }\n .power-settings-dialog .power-dashboard-selector-input input { font-family: var(--font-monospace, monospace); }\n .power-settings-dialog button.power-settings-icon-action { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; min-width: 30px; padding: 0; border: 0; border-radius: 6px; background: transparent; color: var(--textColorSecondary); cursor: pointer; transition: color 160ms ease, background-color 160ms ease; }\n .power-settings-dialog button.power-settings-icon-action:disabled { opacity: .45; cursor: default; }\n .power-settings-dialog button.power-settings-icon-action:hover:not(:disabled) { color: var(--primary); background: color-mix(in srgb, var(--primary) 10%, transparent); }\n .power-settings-dialog button.power-settings-icon-action:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }\n @media (max-width: 600px) { .power-settings-dialog .power-settings-row { align-items: stretch !important; flex-direction: column; gap: 8px !important; } .power-settings-dialog .power-settings-row > div { width: 100% !important; } }\n `}</style>\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={`${id}-heading`}\n style={{ display: \"grid\", gap: 16, padding: 24, width: 560, maxWidth: \"calc(100vw - 48px)\" }}\n >\n <h2 id={`${id}-heading`} style={{ margin: 0 }}>\n {title}\n </h2>\n {children}\n </div>\n </Renderer.Component.Dialog>\n );\n}\n\nexport function SettingsField({ id, label, children }: { id: string; label: string; children: ReactNode }) {\n return (\n <div\n className=\"power-settings-row\"\n style={{ display: \"flex\", alignItems: \"center\", justifyContent: \"space-between\", gap: 20 }}\n >\n <label htmlFor={id}>{label}</label>\n <div style={{ width: 340, maxWidth: \"100%\", flexShrink: 0 }}>{children}</div>\n </div>\n );\n}\n"],"mappings":";;;;AAGA,SAAgB,cAAc,EAC5B,IACA,OACA,SACA,YAMC;CACD,+BAAA,gBAAgB;EACd,MAAM,iBAAiB,UAAyB;GAC9C,IAAI,MAAM,QAAQ,UAAU;GAC5B,MAAM,eAAe;GACrB,MAAM,gBAAgB;GACtB,QAAQ;EACV;EACA,SAAS,iBAAiB,WAAW,eAAe,IAAI;EACxD,aAAa,SAAS,oBAAoB,WAAW,eAAe,IAAI;CAC1E,GAAG,CAAC,OAAO,CAAC;CACZ,OACE,oCAAA,KAAC,mBAAA,SAAS,UAAU,QAApB;EAA2B,QAAA;EAAO,OAAO;EAAS,OAAA;EAAM,WAAU;YAAlE,CACE,oCAAA,IAAC,SAAD,EAAA,UAAQ;;;;;;;;;;;;;;;;QAgBC,CAAA,GACT,oCAAA,KAAC,OAAD;GACE,MAAK;GACL,cAAW;GACX,mBAAiB,GAAG,GAAG;GACvB,OAAO;IAAE,SAAS;IAAQ,KAAK;IAAI,SAAS;IAAI,OAAO;IAAK,UAAU;GAAqB;aAJ7F,CAME,oCAAA,IAAC,MAAD;IAAI,IAAI,GAAG,GAAG;IAAW,OAAO,EAAE,QAAQ,EAAE;cACzC;GACC,CAAA,GACH,QACE;IACoB;;AAE/B;AAEA,SAAgB,cAAc,EAAE,IAAI,OAAO,YAAgE;CACzG,OACE,oCAAA,KAAC,OAAD;EACE,WAAU;EACV,OAAO;GAAE,SAAS;GAAQ,YAAY;GAAU,gBAAgB;GAAiB,KAAK;EAAG;YAF3F,CAIE,oCAAA,IAAC,SAAD;GAAO,SAAS;aAAK;EAAa,CAAA,GAClC,oCAAA,IAAC,OAAD;GAAK,OAAO;IAAE,OAAO;IAAK,UAAU;IAAQ,YAAY;GAAE;GAAI;EAAc,CAAA,CACzE;;AAET"}
1
+ {"version":3,"file":"settings-modal.js","names":[],"sources":["../../../src/renderer/preferences/settings-modal.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { type ReactNode, useEffect } from \"react\";\n\nexport function SettingsModal({\n id,\n title,\n onClose,\n children,\n}: {\n id: string;\n title: string;\n onClose: () => void;\n children: ReactNode;\n}) {\n useEffect(() => {\n const closeOnEscape = (event: KeyboardEvent) => {\n if (event.key !== \"Escape\") return;\n event.preventDefault();\n event.stopPropagation();\n onClose();\n };\n document.addEventListener(\"keydown\", closeOnEscape, true);\n return () => document.removeEventListener(\"keydown\", closeOnEscape, true);\n }, [onClose]);\n return (\n <Renderer.Component.Dialog isOpen close={onClose} modal className=\"power-dashboard-dialog power-settings-dialog\">\n <style>{`\n .Dialog.power-settings-dialog { background: rgb(0 0 0 / 55%); z-index: 12000; }\n .power-settings-dialog > .box { border-radius: 14px; background: var(--mainBackground, #252a2e); border: 1px solid var(--borderFaintColor, #555); box-shadow: 0 20px 70px rgb(0 0 0 / 45%); overflow: visible; }\n .power-settings-dialog .power-ai-prompt { width: 100%; min-height: 120px; max-height: 260px; field-sizing: content; overflow-y: auto; padding: 12px; box-sizing: border-box; border: 1px solid var(--borderFaintColor); border-radius: 8px; background: var(--contentColor); color: var(--textColorPrimary); font: inherit; resize: none; }\n .power-settings-dialog .power-ai-prompt:focus { outline: 1px solid var(--primary); border-color: var(--primary); }\n .power-settings-dialog .power-dashboard-selector-editor { display: grid; gap: 12px; }\n .power-settings-dialog .power-dashboard-selector-field { display: flex; align-items: center; gap: 8px; }\n .power-settings-dialog .power-dashboard-selector-input { flex: 1; min-width: 0; border: 1px solid var(--borderFaintColor); border-radius: 7px; padding: 4px 8px; background: var(--contentColor); transition: border-color 160ms ease; }\n .power-settings-dialog .power-dashboard-selector-input:focus-within { border-color: var(--primary); }\n .power-settings-dialog .power-dashboard-selector-input .input-area { border-bottom: 0; }\n .power-settings-dialog .power-dashboard-selector-input input { font-family: var(--font-monospace, monospace); }\n .power-settings-dialog button.power-settings-icon-action { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; min-width: 30px; padding: 0; border: 0; border-radius: 6px; background: transparent; color: var(--textColorSecondary); cursor: pointer; transition: color 160ms ease, background-color 160ms ease; }\n .power-settings-dialog button.power-settings-icon-action:disabled { opacity: .45; cursor: default; }\n .power-settings-dialog button.power-settings-icon-action:hover:not(:disabled) { color: var(--primary); background: color-mix(in srgb, var(--primary) 10%, transparent); }\n .power-settings-dialog button.power-settings-icon-action:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }\n @media (max-width: 600px) { .power-settings-dialog .power-settings-row { align-items: stretch !important; flex-direction: column; gap: 8px !important; } .power-settings-dialog .power-settings-row > div { width: 100% !important; } }\n `}</style>\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={`${id}-heading`}\n style={{ display: \"grid\", gap: 16, padding: 24, width: 560, maxWidth: \"calc(100vw - 48px)\" }}\n >\n <h2 id={`${id}-heading`} style={{ margin: 0 }}>\n {title}\n </h2>\n {children}\n </div>\n </Renderer.Component.Dialog>\n );\n}\n\nexport function SettingsField({ id, label, children }: { id: string; label: ReactNode; children: ReactNode }) {\n return (\n <div\n className=\"power-settings-row\"\n style={{ display: \"flex\", alignItems: \"center\", justifyContent: \"space-between\", gap: 20 }}\n >\n <label htmlFor={id}>{label}</label>\n <div style={{ width: 340, maxWidth: \"100%\", flexShrink: 0 }}>{children}</div>\n </div>\n );\n}\n"],"mappings":";;;;AAGA,SAAgB,cAAc,EAC5B,IACA,OACA,SACA,YAMC;CACD,+BAAA,gBAAgB;EACd,MAAM,iBAAiB,UAAyB;GAC9C,IAAI,MAAM,QAAQ,UAAU;GAC5B,MAAM,eAAe;GACrB,MAAM,gBAAgB;GACtB,QAAQ;EACV;EACA,SAAS,iBAAiB,WAAW,eAAe,IAAI;EACxD,aAAa,SAAS,oBAAoB,WAAW,eAAe,IAAI;CAC1E,GAAG,CAAC,OAAO,CAAC;CACZ,OACE,oCAAA,KAAC,mBAAA,SAAS,UAAU,QAApB;EAA2B,QAAA;EAAO,OAAO;EAAS,OAAA;EAAM,WAAU;YAAlE,CACE,oCAAA,IAAC,SAAD,EAAA,UAAQ;;;;;;;;;;;;;;;;QAgBC,CAAA,GACT,oCAAA,KAAC,OAAD;GACE,MAAK;GACL,cAAW;GACX,mBAAiB,GAAG,GAAG;GACvB,OAAO;IAAE,SAAS;IAAQ,KAAK;IAAI,SAAS;IAAI,OAAO;IAAK,UAAU;GAAqB;aAJ7F,CAME,oCAAA,IAAC,MAAD;IAAI,IAAI,GAAG,GAAG;IAAW,OAAO,EAAE,QAAQ,EAAE;cACzC;GACC,CAAA,GACH,QACE;IACoB;;AAE/B;AAEA,SAAgB,cAAc,EAAE,IAAI,OAAO,YAAmE;CAC5G,OACE,oCAAA,KAAC,OAAD;EACE,WAAU;EACV,OAAO;GAAE,SAAS;GAAQ,YAAY;GAAU,gBAAgB;GAAiB,KAAK;EAAG;YAF3F,CAIE,oCAAA,IAAC,SAAD;GAAO,SAAS;aAAK;EAAa,CAAA,GAClC,oCAAA,IAAC,OAAD;GAAK,OAAO;IAAE,OAAO;IAAK,UAAU;IAAQ,YAAY;GAAE;GAAI;EAAc,CAAA,CACzE;;AAET"}
@@ -0,0 +1,7 @@
1
+ //#region src/renderer/resource-relationships.css.ts
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:flex;align-items:center;gap:8px;width:fit-content;margin:0;cursor:pointer}.power-relationships .graph-option input{margin:0}.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{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
+ //#endregion
5
+ exports.resourceRelationshipsStyles = resourceRelationshipsStyles;
6
+
7
+ //# sourceMappingURL=resource-relationships.css.js.map
@@ -0,0 +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:flex;align-items:center;gap:8px;width:fit-content;margin:0;cursor:pointer}.power-relationships .graph-option input{margin:0}.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{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"}
@@ -0,0 +1,556 @@
1
+ const require_extensions = require("./_virtual/_global-external_@freelensapp/extensions.js");
2
+ const require__global_external_mobx_react = require("./_virtual/_global-external_mobx-react.js");
3
+ const require__global_external_react = require("./_virtual/_global-external_react.js");
4
+ const require_jsx_runtime = require("./_virtual/_global-external_react/jsx-runtime.js");
5
+ const require_resource_relationships = require("./src/common/resource-relationships.js");
6
+ const require_resource_relationships_css = require("./resource-relationships.css.js");
7
+ //#region src/renderer/resource-relationships.tsx
8
+ var { Icon, Select } = require_extensions.Renderer.Component;
9
+ var LIMIT = 100;
10
+ var roles = [
11
+ {
12
+ id: "upstream",
13
+ name: "Upstream references",
14
+ relation: "references backend"
15
+ },
16
+ {
17
+ id: "service",
18
+ name: "Focused Service",
19
+ relation: "API configuration"
20
+ },
21
+ {
22
+ id: "backend",
23
+ name: "Selected & published backends",
24
+ relation: "selector matches / published in"
25
+ },
26
+ {
27
+ id: "placement",
28
+ name: "Ownership & placement",
29
+ relation: "owned by / assigned to"
30
+ }
31
+ ];
32
+ var icons = {
33
+ Ingress: "login",
34
+ Service: "share",
35
+ Pod: "view_in_ar",
36
+ EndpointSlice: "hub",
37
+ Node: "dns",
38
+ ReplicaSet: "layers",
39
+ Deployment: "layers",
40
+ StatefulSet: "layers",
41
+ DaemonSet: "layers",
42
+ Job: "work",
43
+ CronJob: "schedule"
44
+ };
45
+ async function loadRelationshipInventory(namespace, service, signal) {
46
+ const gaps = [];
47
+ const list = async (name, request) => {
48
+ try {
49
+ const items = await request();
50
+ if (!items) {
51
+ gaps.push(`${name} inventory unavailable`);
52
+ return;
53
+ }
54
+ if (items.length > LIMIT) gaps.push(`${name} inventory capped at ${LIMIT} objects; additional relationships may be omitted`);
55
+ return items.slice(0, LIMIT);
56
+ } catch {
57
+ if (!signal.aborted) gaps.push(`${name} inventory unavailable; access or API availability may be restricted`);
58
+ return;
59
+ }
60
+ };
61
+ const options = {
62
+ namespace,
63
+ reqInit: { signal }
64
+ };
65
+ const query = { limit: 101 };
66
+ const selector = Object.entries(service.spec.selector ?? {}).map(([key, value]) => `${key}=${value}`).join(",");
67
+ const external = service.spec.type === "ExternalName";
68
+ const ownerApis = external ? [] : [
69
+ require_extensions.Renderer.K8sApi.replicaSetApi,
70
+ require_extensions.Renderer.K8sApi.deploymentApi,
71
+ require_extensions.Renderer.K8sApi.statefulSetApi,
72
+ require_extensions.Renderer.K8sApi.daemonSetApi,
73
+ require_extensions.Renderer.K8sApi.jobApi,
74
+ require_extensions.Renderer.K8sApi.cronJobApi
75
+ ];
76
+ const [pods, slices, ingresses, ...owners] = await Promise.all([
77
+ selector && !external ? list("Pod", () => require_extensions.Renderer.K8sApi.podsApi.list(options, {
78
+ ...query,
79
+ labelSelector: selector
80
+ })) : Promise.resolve([]),
81
+ service.spec.type === "ExternalName" ? Promise.resolve([]) : list("EndpointSlice", () => require_extensions.Renderer.K8sApi.endpointSliceApi.list(options, {
82
+ ...query,
83
+ labelSelector: `kubernetes.io/service-name=${service.getName()}`
84
+ })),
85
+ list("Ingress", () => require_extensions.Renderer.K8sApi.ingressApi.list(options, query)),
86
+ ...ownerApis.map((api) => list("Controller", () => api.list(options, query)))
87
+ ]);
88
+ return {
89
+ pods,
90
+ slices,
91
+ ingresses,
92
+ owners: owners.flatMap((items) => items ?? []),
93
+ gaps
94
+ };
95
+ }
96
+ function ResourceCard({ node, incoming, onOpen, onHighlight }) {
97
+ return /* @__PURE__ */ require_jsx_runtime.jsxs("article", {
98
+ className: "card",
99
+ "data-node-id": node.id,
100
+ onMouseEnter: () => onHighlight(node.id),
101
+ onMouseLeave: () => onHighlight(),
102
+ onFocus: () => onHighlight(node.id),
103
+ onBlur: () => onHighlight(),
104
+ children: [
105
+ /* @__PURE__ */ require_jsx_runtime.jsx("div", {
106
+ className: "relation-label",
107
+ children: incoming.join(" · ") || "Focus"
108
+ }),
109
+ /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
110
+ className: "card-kind",
111
+ children: [
112
+ /* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
113
+ material: icons[node.kind] ?? "account_tree",
114
+ size: 16
115
+ }),
116
+ node.kind,
117
+ node.inferred && " · reference"
118
+ ]
119
+ }),
120
+ node.selfLink ? /* @__PURE__ */ require_jsx_runtime.jsx("button", {
121
+ type: "button",
122
+ className: "resource-name",
123
+ onClick: (event) => {
124
+ event.preventDefault();
125
+ event.stopPropagation();
126
+ onOpen(node.selfLink);
127
+ },
128
+ title: `Open ${node.kind} ${node.name}`,
129
+ children: node.name
130
+ }) : /* @__PURE__ */ require_jsx_runtime.jsx("strong", { children: node.name }),
131
+ /* @__PURE__ */ require_jsx_runtime.jsxs("p", {
132
+ className: node.tone,
133
+ children: [
134
+ /* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
135
+ material: node.tone === "warning" ? "warning" : node.tone === "healthy" ? "check_circle" : "info",
136
+ size: 14
137
+ }),
138
+ " ",
139
+ node.detail
140
+ ]
141
+ })
142
+ ]
143
+ });
144
+ }
145
+ function roundedConnector(points) {
146
+ let path = `M ${points[0][0]} ${points[0][1]}`;
147
+ for (let index = 1; index < points.length - 1; index++) {
148
+ const [x, y] = points[index];
149
+ const [px, py] = points[index - 1];
150
+ const [nx, ny] = points[index + 1];
151
+ const before = Math.hypot(x - px, y - py);
152
+ const after = Math.hypot(nx - x, ny - y);
153
+ if (!before || !after) continue;
154
+ const radius = Math.min(6, before / 2, after / 2);
155
+ path += ` L ${x + (px - x) * radius / before} ${y + (py - y) * radius / before}`;
156
+ path += ` Q ${x} ${y} ${x + (nx - x) * radius / after} ${y + (ny - y) * radius / after}`;
157
+ }
158
+ const last = points[points.length - 1];
159
+ return `${path} L ${last[0]} ${last[1]}`;
160
+ }
161
+ function RelationshipGraph({ graph, onOpen, ingressAvailable, hideUpstreams = false }) {
162
+ const visibleRoles = roles.filter((role) => !hideUpstreams || role.id !== "upstream");
163
+ const element = require__global_external_react.useRef(null);
164
+ const [paths, setPaths] = require__global_external_react.useState([]);
165
+ const [highlight, setHighlight] = require__global_external_react.useState();
166
+ require__global_external_react.useLayoutEffect(() => {
167
+ const container = element.current;
168
+ if (!container) return;
169
+ const measure = () => {
170
+ const origin = container.getBoundingClientRect();
171
+ const cards = new Map(Array.from(container.querySelectorAll("[data-node-id]")).map((card) => [card.dataset.nodeId, card.getBoundingClientRect()]));
172
+ setPaths(graph.edges.flatMap((edge, index) => {
173
+ const from = cards.get(edge.from);
174
+ const to = cards.get(edge.to);
175
+ if (!from || !to) return [];
176
+ const sy = from.top + from.height / 2 - origin.top;
177
+ const ty = to.top + to.height / 2 - origin.top;
178
+ const sameColumn = Math.abs(from.left - to.left) < 2;
179
+ const sx = (sameColumn ? from.left : from.right) - origin.left;
180
+ const tx = to.left - origin.left;
181
+ const laneOffset = (index % 3 - 1) * 8;
182
+ const gutter = sameColumn ? sx - 28 + laneOffset : (sx + tx) / 2 + laneOffset;
183
+ const crossesColumn = Array.from(cards.values()).some((card) => card.left > from.left + 2 && card.right < to.left - 2);
184
+ let path = !sameColumn && sy === ty ? `M ${sx} ${sy} H ${tx}` : roundedConnector([
185
+ [sx, sy],
186
+ [gutter, sy],
187
+ [gutter, ty],
188
+ [tx, ty]
189
+ ]);
190
+ if (crossesColumn) {
191
+ const bottom = Math.max(...Array.from(cards.values(), (card) => card.bottom)) - origin.top + 24 + index % 3 * 8;
192
+ const sourceGutter = sx + 32 + laneOffset;
193
+ const targetGutter = tx - 32 + laneOffset;
194
+ path = roundedConnector([
195
+ [sx, sy],
196
+ [sourceGutter, sy],
197
+ [sourceGutter, bottom],
198
+ [targetGutter, bottom],
199
+ [targetGutter, ty],
200
+ [tx, ty]
201
+ ]);
202
+ }
203
+ return [{
204
+ ...edge,
205
+ path
206
+ }];
207
+ }));
208
+ };
209
+ measure();
210
+ const observer = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(measure);
211
+ observer?.observe(container);
212
+ for (const card of container.querySelectorAll("[data-node-id]")) observer?.observe(card);
213
+ window.addEventListener("resize", measure);
214
+ return () => {
215
+ observer?.disconnect();
216
+ window.removeEventListener("resize", measure);
217
+ };
218
+ }, [graph, hideUpstreams]);
219
+ return /* @__PURE__ */ require_jsx_runtime.jsx("div", {
220
+ className: "graph-scroll",
221
+ children: /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
222
+ ref: element,
223
+ className: `graph${hideUpstreams ? " hide-upstreams" : ""}`,
224
+ style: { gridTemplateRows: `auto repeat(${Math.max(1, ...visibleRoles.map((role) => graph.nodes.filter((node) => node.role === role.id).length))}, auto)` },
225
+ "aria-label": "Service resource relationship graph",
226
+ children: [/* @__PURE__ */ require_jsx_runtime.jsxs("svg", {
227
+ className: "resource-connections",
228
+ width: "100%",
229
+ height: "100%",
230
+ "aria-hidden": "true",
231
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx("defs", { children: /* @__PURE__ */ require_jsx_runtime.jsx("marker", {
232
+ id: "relationship-arrow",
233
+ markerWidth: "5",
234
+ markerHeight: "5",
235
+ refX: "4",
236
+ refY: "2.5",
237
+ orient: "auto",
238
+ children: /* @__PURE__ */ require_jsx_runtime.jsx("path", {
239
+ d: "M0 0L5 2.5L0 5",
240
+ fill: "currentColor"
241
+ })
242
+ }) }), paths.map((edge) => /* @__PURE__ */ require_jsx_runtime.jsx("path", {
243
+ "data-edge-from": edge.from,
244
+ "data-edge-to": edge.to,
245
+ className: highlight ? edge.from === highlight || edge.to === highlight ? "connection highlighted" : "connection dimmed" : "connection",
246
+ d: edge.path,
247
+ markerEnd: "url(#relationship-arrow)",
248
+ children: /* @__PURE__ */ require_jsx_runtime.jsxs("title", { children: [
249
+ graph.nodes.find((node) => node.id === edge.from)?.name,
250
+ " → ",
251
+ edge.label,
252
+ " →",
253
+ " ",
254
+ graph.nodes.find((node) => node.id === edge.to)?.name
255
+ ] })
256
+ }, `${edge.from}/${edge.to}/${edge.label}`))]
257
+ }), visibleRoles.map((role) => /* @__PURE__ */ require_jsx_runtime.jsxs("section", {
258
+ className: "column",
259
+ "aria-label": role.name,
260
+ children: [
261
+ /* @__PURE__ */ require_jsx_runtime.jsx("h3", { children: role.name }),
262
+ graph.nodes.filter((node) => node.role === role.id).map((node) => /* @__PURE__ */ require_jsx_runtime.jsx(ResourceCard, {
263
+ node,
264
+ incoming: [...new Set(graph.edges.filter((edge) => edge.to === node.id).map((edge) => edge.label))],
265
+ onOpen,
266
+ onHighlight: setHighlight
267
+ }, node.id)),
268
+ !graph.nodes.some((node) => node.role === role.id) && /* @__PURE__ */ require_jsx_runtime.jsx("div", {
269
+ className: "card neutral",
270
+ children: role.id === "upstream" ? ingressAvailable ? "No matching Ingress references observed" : "Ingress evidence unavailable" : "No relationships observed in available evidence"
271
+ })
272
+ ]
273
+ }, role.id))]
274
+ })
275
+ });
276
+ }
277
+ var ResourceRelationshipsPage = require__global_external_mobx_react.observer(({ enabled = true }) => {
278
+ if (!enabled) return /* @__PURE__ */ require_jsx_runtime.jsx("div", {
279
+ className: "power-relationships",
280
+ children: /* @__PURE__ */ require_jsx_runtime.jsx("p", { children: "Resource relationships are disabled in Power settings" })
281
+ });
282
+ return /* @__PURE__ */ require_jsx_runtime.jsx(EnabledResourceRelationshipsPage, {}, require_extensions.Renderer.Catalog.activeCluster.get()?.metadata.uid ?? "cluster");
283
+ });
284
+ function EnabledResourceRelationshipsPage() {
285
+ const mounted = require__global_external_react.useRef(true);
286
+ const clusterId = require__global_external_react.useRef(require_extensions.Renderer.Catalog.activeCluster.get()?.metadata.uid);
287
+ require__global_external_react.useEffect(() => {
288
+ mounted.current = true;
289
+ return () => {
290
+ mounted.current = false;
291
+ };
292
+ }, []);
293
+ const [namespaces, setNamespaces] = require__global_external_react.useState([]);
294
+ const [namespace, setNamespace] = require__global_external_react.useState("");
295
+ const [services, setServices] = require__global_external_react.useState([]);
296
+ const [serviceName, setServiceName] = require__global_external_react.useState("");
297
+ const [focusedPod, setFocusedPod] = require__global_external_react.useState("");
298
+ const [hideUpstreams, setHideUpstreams] = require__global_external_react.useState(false);
299
+ const [inventory, setInventory] = require__global_external_react.useState();
300
+ const [refresh, setRefresh] = require__global_external_react.useState(0);
301
+ const [scopeRefresh, setScopeRefresh] = require__global_external_react.useState(0);
302
+ const [namespaceMessage, setNamespaceMessage] = require__global_external_react.useState("");
303
+ const [scopeLoading, setScopeLoading] = require__global_external_react.useState(true);
304
+ const [loading, setLoading] = require__global_external_react.useState(false);
305
+ const [scopeMessage, setScopeMessage] = require__global_external_react.useState("");
306
+ const [openError, setOpenError] = require__global_external_react.useState("");
307
+ require__global_external_react.useEffect(() => {
308
+ const controller = new AbortController();
309
+ setScopeLoading(true);
310
+ setNamespaceMessage("");
311
+ require_extensions.Renderer.K8sApi.namespacesApi.list({ reqInit: { signal: controller.signal } }, { limit: 101 }).then((items) => {
312
+ if (controller.signal.aborted) return;
313
+ const names = (items ?? []).slice(0, LIMIT).map((item) => item.getName()).sort();
314
+ setNamespaces(names);
315
+ setNamespace((current) => names.includes(current) ? current : names[0] ?? "");
316
+ if ((items?.length ?? 0) > LIMIT) setNamespaceMessage(`Namespace selector is capped at ${LIMIT} entries`);
317
+ else if (!names.length) setScopeMessage("No visible namespaces returned by the API");
318
+ }).catch(() => {
319
+ if (!controller.signal.aborted) setScopeMessage("Namespace inventory unavailable; check access to namespace listing");
320
+ }).finally(() => {
321
+ if (!controller.signal.aborted) setScopeLoading(false);
322
+ });
323
+ return () => controller.abort();
324
+ }, [scopeRefresh]);
325
+ require__global_external_react.useEffect(() => {
326
+ if (!namespace) return;
327
+ const controller = new AbortController();
328
+ setLoading(false);
329
+ setServices([]);
330
+ setServiceName("");
331
+ setInventory(void 0);
332
+ setScopeLoading(true);
333
+ setScopeMessage("");
334
+ require_extensions.Renderer.K8sApi.serviceApi.list({
335
+ namespace,
336
+ reqInit: { signal: controller.signal }
337
+ }, { limit: 101 }).then((items) => {
338
+ if (controller.signal.aborted) return;
339
+ const visible = (items ?? []).slice(0, LIMIT).sort((a, b) => a.getName().localeCompare(b.getName()));
340
+ setServices(visible);
341
+ setServiceName(visible[0]?.getName() ?? "");
342
+ if ((items?.length ?? 0) > LIMIT) setScopeMessage(`Service selector is capped at ${LIMIT} entries; additional Services are not shown`);
343
+ else if (!visible.length) setScopeMessage("No visible Services in this namespace");
344
+ }).catch(() => {
345
+ if (!controller.signal.aborted) setScopeMessage("Service inventory unavailable; check namespace access");
346
+ }).finally(() => {
347
+ if (!controller.signal.aborted) setScopeLoading(false);
348
+ });
349
+ return () => controller.abort();
350
+ }, [namespace, scopeRefresh]);
351
+ const service = services.find((item) => item.getName() === serviceName);
352
+ require__global_external_react.useEffect(() => {
353
+ if (!service || !namespace || service.metadata.namespace !== namespace) return;
354
+ const controller = new AbortController();
355
+ setLoading(true);
356
+ setInventory(void 0);
357
+ setFocusedPod("");
358
+ setOpenError("");
359
+ loadRelationshipInventory(namespace, service, controller.signal).then((data) => {
360
+ if (!controller.signal.aborted) setInventory({
361
+ namespace,
362
+ service: service.getName(),
363
+ data
364
+ });
365
+ }).finally(() => {
366
+ if (!controller.signal.aborted) setLoading(false);
367
+ });
368
+ return () => controller.abort();
369
+ }, [
370
+ namespace,
371
+ service,
372
+ refresh
373
+ ]);
374
+ const current = inventory?.namespace === namespace && inventory.service === serviceName ? inventory : void 0;
375
+ const graph = current && service ? require_resource_relationships.buildResourceRelationships(service, current.data, focusedPod || void 0) : void 0;
376
+ const openResource = (path) => {
377
+ const currentCluster = () => mounted.current && require_extensions.Renderer.Catalog.activeCluster.get()?.metadata.uid === clusterId.current;
378
+ setOpenError("");
379
+ Promise.resolve().then(() => {
380
+ if (currentCluster()) return require_extensions.Renderer.Navigation.showDetails(path);
381
+ }).catch(() => {
382
+ if (currentCluster()) setOpenError("Resource details could not be opened; the object may be unavailable");
383
+ });
384
+ };
385
+ return /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
386
+ className: "power-relationships",
387
+ children: [
388
+ /* @__PURE__ */ require_jsx_runtime.jsx("style", { children: require_resource_relationships_css.resourceRelationshipsStyles }),
389
+ /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
390
+ className: "page-heading",
391
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx("h2", { children: "Resource relationships" }), /* @__PURE__ */ require_jsx_runtime.jsxs("label", {
392
+ className: "graph-option",
393
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx("input", {
394
+ type: "checkbox",
395
+ checked: hideUpstreams,
396
+ onChange: (event) => setHideUpstreams(event.target.checked)
397
+ }), "Hide Upstream"]
398
+ })]
399
+ }),
400
+ /* @__PURE__ */ require_jsx_runtime.jsx("p", {
401
+ className: "intro",
402
+ children: "Follow one Service from upstream Ingress references to selected pods, published EndpointSlices and controller ownership. Connections show Kubernetes API evidence; they do not prove network reachability."
403
+ }),
404
+ /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
405
+ className: "controls",
406
+ children: [
407
+ /* @__PURE__ */ require_jsx_runtime.jsxs("label", {
408
+ className: "control",
409
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx("span", { children: "Namespace" }), /* @__PURE__ */ require_jsx_runtime.jsx(Select, {
410
+ "aria-label": "Relationship namespace",
411
+ value: namespace,
412
+ options: namespaces.map((name) => ({
413
+ value: name,
414
+ label: name
415
+ })),
416
+ onChange: (option) => setNamespace(option?.value ?? ""),
417
+ isDisabled: !namespaces.length
418
+ })]
419
+ }),
420
+ /* @__PURE__ */ require_jsx_runtime.jsxs("label", {
421
+ className: "control",
422
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx("span", { children: "Service" }), /* @__PURE__ */ require_jsx_runtime.jsx(Select, {
423
+ "aria-label": "Relationship Service",
424
+ value: serviceName,
425
+ options: services.map((item) => ({
426
+ value: item.getName(),
427
+ label: item.getName()
428
+ })),
429
+ onChange: (option) => setServiceName(option?.value ?? ""),
430
+ isDisabled: !services.length || scopeLoading
431
+ })]
432
+ }),
433
+ /* @__PURE__ */ require_jsx_runtime.jsxs("label", {
434
+ className: "control",
435
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx("span", { children: "Pod focus" }), /* @__PURE__ */ require_jsx_runtime.jsx(Select, {
436
+ "aria-label": "Relationship pod focus",
437
+ value: focusedPod,
438
+ options: [{
439
+ value: "",
440
+ label: "All selected pods (up to 8)"
441
+ }, ...(graph?.podOptions ?? []).map((name) => ({
442
+ value: name,
443
+ label: name
444
+ }))],
445
+ onChange: (option) => setFocusedPod(option?.value ?? ""),
446
+ isDisabled: !graph?.podOptions.length
447
+ })]
448
+ }),
449
+ /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
450
+ type: "button",
451
+ className: "refresh",
452
+ onClick: () => service ? setRefresh((value) => value + 1) : setScopeRefresh((value) => value + 1),
453
+ disabled: loading || scopeLoading,
454
+ title: "Refresh scoped API evidence",
455
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
456
+ material: "refresh",
457
+ size: 18
458
+ }), "Refresh"]
459
+ })
460
+ ]
461
+ }),
462
+ namespaceMessage && /* @__PURE__ */ require_jsx_runtime.jsx("div", {
463
+ className: "notice",
464
+ role: "status",
465
+ children: namespaceMessage
466
+ }),
467
+ scopeMessage && /* @__PURE__ */ require_jsx_runtime.jsx("div", {
468
+ className: "notice",
469
+ role: "status",
470
+ children: scopeMessage
471
+ }),
472
+ openError && /* @__PURE__ */ require_jsx_runtime.jsx("div", {
473
+ className: "notice warning",
474
+ role: "alert",
475
+ children: openError
476
+ }),
477
+ (scopeLoading || loading) && /* @__PURE__ */ require_jsx_runtime.jsx("div", {
478
+ className: "empty",
479
+ role: "status",
480
+ children: "Loading scoped relationship evidence…"
481
+ }),
482
+ !scopeLoading && !loading && !service && /* @__PURE__ */ require_jsx_runtime.jsx("div", {
483
+ className: "empty",
484
+ children: "Select a namespace with a visible Service to inspect relationships"
485
+ }),
486
+ graph && !loading && /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [
487
+ /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
488
+ className: "notice relationship-summary",
489
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx("strong", { children: graph.diagnostics.summary }), /* @__PURE__ */ require_jsx_runtime.jsx("div", {
490
+ className: "ports",
491
+ children: graph.diagnostics.ports.map((port) => /* @__PURE__ */ require_jsx_runtime.jsxs("span", { children: [
492
+ port.name,
493
+ " · ",
494
+ port.protocol,
495
+ " → ",
496
+ port.target,
497
+ " · published:",
498
+ " ",
499
+ port.endpointPorts.join(", ") || "unknown",
500
+ port.unresolved ? ` · ${port.unresolved} unresolved named targets` : ""
501
+ ] }, port.name))
502
+ })]
503
+ }),
504
+ graph.gaps.length > 0 && /* @__PURE__ */ require_jsx_runtime.jsxs("details", {
505
+ open: true,
506
+ className: "notice",
507
+ children: [/* @__PURE__ */ require_jsx_runtime.jsxs("summary", { children: [
508
+ "Partial coverage (",
509
+ graph.gaps.length,
510
+ ")"
511
+ ] }), /* @__PURE__ */ require_jsx_runtime.jsx("ul", { children: graph.gaps.map((gap) => /* @__PURE__ */ require_jsx_runtime.jsx("li", { children: gap }, gap)) })]
512
+ }),
513
+ /* @__PURE__ */ require_jsx_runtime.jsx(RelationshipGraph, {
514
+ graph,
515
+ onOpen: openResource,
516
+ ingressAvailable: current?.data.ingresses !== void 0,
517
+ hideUpstreams
518
+ }),
519
+ graph.omitted > 0 && /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
520
+ className: "notice",
521
+ children: [graph.omitted, " additional visible resources omitted to keep the graph readable. Focus a pod to inspect its ownership and placement."]
522
+ }),
523
+ /* @__PURE__ */ require_jsx_runtime.jsxs("details", { children: [/* @__PURE__ */ require_jsx_runtime.jsxs("summary", { children: [
524
+ "Accessible connection list (",
525
+ graph.edges.length,
526
+ ")"
527
+ ] }), /* @__PURE__ */ require_jsx_runtime.jsx("ul", {
528
+ className: "edge-list",
529
+ children: graph.edges.map((edge) => /* @__PURE__ */ require_jsx_runtime.jsxs("li", { children: [
530
+ graph.nodes.find((node) => node.id === edge.from)?.name,
531
+ " → ",
532
+ edge.label,
533
+ " →",
534
+ " ",
535
+ graph.nodes.find((node) => node.id === edge.to)?.name
536
+ ] }, `${edge.from}/${edge.to}/${edge.label}`))
537
+ })] }),
538
+ /* @__PURE__ */ require_jsx_runtime.jsxs("p", {
539
+ className: "scope",
540
+ children: [
541
+ /* @__PURE__ */ require_jsx_runtime.jsx("span", { children: "Snapshot from namespace-scoped read-only API requests." }),
542
+ /* @__PURE__ */ require_jsx_runtime.jsx("span", { children: "Owner and node references can point to objects that were not queried." }),
543
+ /* @__PURE__ */ require_jsx_runtime.jsx("span", { children: "Endpoint counts can include separate address families." }),
544
+ /* @__PURE__ */ require_jsx_runtime.jsx("span", { children: "No Secret or ConfigMap data is read." })
545
+ ]
546
+ })
547
+ ] })
548
+ ]
549
+ });
550
+ }
551
+ //#endregion
552
+ exports.RelationshipGraph = RelationshipGraph;
553
+ exports.ResourceRelationshipsPage = ResourceRelationshipsPage;
554
+ exports.loadRelationshipInventory = loadRelationshipInventory;
555
+
556
+ //# sourceMappingURL=resource-relationships.js.map