@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.
- package/README.md +3 -1
- package/out/main/common/dashboard-picker.js +4 -4
- package/out/main/common/dashboard-picker.js.map +1 -1
- package/out/main/common/dashboard-readiness.js +17 -47
- package/out/main/common/dashboard-readiness.js.map +1 -1
- package/out/main/common/dashboard-selector.js +40 -6
- package/out/main/common/dashboard-selector.js.map +1 -1
- package/out/main/common/store/monitoring-preferences-store.js +5 -0
- package/out/main/common/store/monitoring-preferences-store.js.map +1 -1
- package/out/main/dashboard-picker.js +1 -4
- package/out/main/dashboard-picker.js.map +1 -1
- package/out/main/dashboard-preload.js +110 -0
- package/out/main/dashboard-preload.js.map +1 -0
- package/out/main/dashboard-views.js +207 -62
- package/out/main/dashboard-views.js.map +1 -1
- package/out/main/dock-icon.js +25 -10
- package/out/main/dock-icon.js.map +1 -1
- package/out/main/index.js +2 -0
- package/out/main/index.js.map +1 -1
- package/out/main/splash-accent.js +84 -0
- package/out/main/splash-accent.js.map +1 -0
- package/out/renderer/ai-diagnostic-handoff.js +52 -0
- package/out/renderer/ai-diagnostic-handoff.js.map +1 -0
- package/out/renderer/ai-extras.js +40 -4
- package/out/renderer/ai-extras.js.map +1 -1
- package/out/renderer/ai.js +48 -56
- package/out/renderer/ai.js.map +1 -1
- package/out/renderer/appearance.js +6 -2
- package/out/renderer/appearance.js.map +1 -1
- package/out/renderer/dashboard.js +26 -6
- package/out/renderer/dashboard.js.map +1 -1
- package/out/renderer/details/hpa-diagnostics-details.js +46 -0
- package/out/renderer/details/hpa-diagnostics-details.js.map +1 -0
- package/out/renderer/details/namespace-health-details.js +91 -0
- package/out/renderer/details/namespace-health-details.js.map +1 -0
- package/out/renderer/details/resource-health-details.js +131 -0
- package/out/renderer/details/resource-health-details.js.map +1 -0
- package/out/renderer/details/service-diagnostics-details.js +163 -0
- package/out/renderer/details/service-diagnostics-details.js.map +1 -0
- package/out/renderer/diagnostic-explain-button.js +16 -0
- package/out/renderer/diagnostic-explain-button.js.map +1 -0
- package/out/renderer/diagnostics-state.js +51 -0
- package/out/renderer/diagnostics-state.js.map +1 -0
- package/out/renderer/diagnostics.css.js +9 -0
- package/out/renderer/diagnostics.css.js.map +1 -1
- package/out/renderer/diagnostics.js +369 -73
- package/out/renderer/diagnostics.js.map +1 -1
- package/out/renderer/incident-copy-button.js +50 -0
- package/out/renderer/incident-copy-button.js.map +1 -0
- package/out/renderer/index.js +91 -19
- package/out/renderer/index.js.map +1 -1
- package/out/renderer/monitoring-history-collection.js +1 -1
- package/out/renderer/preferences/dashboard-preferences.js +16 -16
- package/out/renderer/preferences/dashboard-preferences.js.map +1 -1
- package/out/renderer/preferences/monitoring-preferences.js +10 -0
- package/out/renderer/preferences/monitoring-preferences.js.map +1 -1
- package/out/renderer/preferences/preferences.js +88 -0
- package/out/renderer/preferences/preferences.js.map +1 -1
- package/out/renderer/preferences/settings-modal.js.map +1 -1
- package/out/renderer/resource-relationships.css.js +7 -0
- package/out/renderer/resource-relationships.css.js.map +1 -0
- package/out/renderer/resource-relationships.js +556 -0
- package/out/renderer/resource-relationships.js.map +1 -0
- package/out/renderer/resource-search-distance.js +24 -0
- package/out/renderer/resource-search-distance.js.map +1 -0
- package/out/renderer/resource-search-fields.js +55 -0
- package/out/renderer/resource-search-fields.js.map +1 -0
- package/out/renderer/resource-search-model.js +193 -0
- package/out/renderer/resource-search-model.js.map +1 -0
- package/out/renderer/resource-search.js +365 -0
- package/out/renderer/resource-search.js.map +1 -0
- package/out/renderer/src/common/dashboard-picker.js.map +1 -1
- package/out/renderer/src/common/dashboard-selector.js.map +1 -1
- package/out/renderer/src/common/hpa-diagnostics.js +24 -0
- package/out/renderer/src/common/hpa-diagnostics.js.map +1 -0
- package/out/renderer/src/common/incident-summary.js +73 -0
- package/out/renderer/src/common/incident-summary.js.map +1 -0
- package/out/renderer/src/common/resource-health.js +92 -0
- package/out/renderer/src/common/resource-health.js.map +1 -0
- package/out/renderer/src/common/resource-relationships.js +155 -0
- package/out/renderer/src/common/resource-relationships.js.map +1 -0
- package/out/renderer/src/common/service-diagnostics.js +50 -0
- package/out/renderer/src/common/service-diagnostics.js.map +1 -0
- package/out/renderer/src/common/store/monitoring-preferences-store.js +5 -0
- package/out/renderer/src/common/store/monitoring-preferences-store.js.map +1 -1
- package/out/renderer/src/common/timeline-groups.js +36 -0
- package/out/renderer/src/common/timeline-groups.js.map +1 -0
- 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:
|
|
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
|