@64mb/freelens-power 2.1.0 → 2.2.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 (106) hide show
  1. package/README.md +4 -4
  2. package/out/main/common/power-logs.js.map +1 -1
  3. package/out/main/common/resource-search.js.map +1 -1
  4. package/out/main/power-logs-child.js +3 -3
  5. package/out/main/power-logs-child.js.map +1 -1
  6. package/out/main/power-logs-database.js +165 -7
  7. package/out/main/power-logs-database.js.map +1 -1
  8. package/out/main/power-logs-ipc.js +12 -3
  9. package/out/main/power-logs-ipc.js.map +1 -1
  10. package/out/main/power-logs-worker.js +29 -7
  11. package/out/main/power-logs-worker.js.map +1 -1
  12. package/out/main/resource-search-database.js +3 -2
  13. package/out/main/resource-search-database.js.map +1 -1
  14. package/out/main/resource-search.js +2 -1
  15. package/out/main/resource-search.js.map +1 -1
  16. package/out/renderer/_virtual/_global-external_react.js +2 -1
  17. package/out/renderer/ai-context-mutations.js +14 -0
  18. package/out/renderer/ai-context-mutations.js.map +1 -0
  19. package/out/renderer/ai-copy-answer.js +18 -15
  20. package/out/renderer/ai-copy-answer.js.map +1 -1
  21. package/out/renderer/ai-diagnostic-handoff.js +2 -2
  22. package/out/renderer/ai-diagnostic-handoff.js.map +1 -1
  23. package/out/renderer/ai-extras.js +41 -38
  24. package/out/renderer/ai-extras.js.map +1 -1
  25. package/out/renderer/ai-message.js +50 -39
  26. package/out/renderer/ai-message.js.map +1 -1
  27. package/out/renderer/ai-provider-icon.js +39 -32
  28. package/out/renderer/ai-provider-icon.js.map +1 -1
  29. package/out/renderer/ai-resize.js +40 -37
  30. package/out/renderer/ai-resize.js.map +1 -1
  31. package/out/renderer/ai-shortcuts.js +20 -17
  32. package/out/renderer/ai-shortcuts.js.map +1 -1
  33. package/out/renderer/ai-terminal-context.js +42 -48
  34. package/out/renderer/ai-terminal-context.js.map +1 -1
  35. package/out/renderer/ai.js +137 -111
  36. package/out/renderer/ai.js.map +1 -1
  37. package/out/renderer/cluster-icon-colors.js +10 -7
  38. package/out/renderer/cluster-icon-colors.js.map +1 -1
  39. package/out/renderer/cluster-icons.js +47 -40
  40. package/out/renderer/cluster-icons.js.map +1 -1
  41. package/out/renderer/details/service-diagnostics-details.js +25 -23
  42. package/out/renderer/details/service-diagnostics-details.js.map +1 -1
  43. package/out/renderer/diagnostic-explain-button.js +7 -4
  44. package/out/renderer/diagnostic-explain-button.js.map +1 -1
  45. package/out/renderer/diagnostics-screen-finding.js +51 -0
  46. package/out/renderer/diagnostics-screen-finding.js.map +1 -0
  47. package/out/renderer/diagnostics.css.js +1 -1
  48. package/out/renderer/diagnostics.css.js.map +1 -1
  49. package/out/renderer/diagnostics.js +146 -107
  50. package/out/renderer/diagnostics.js.map +1 -1
  51. package/out/renderer/incident-copy-button.js +24 -21
  52. package/out/renderer/incident-copy-button.js.map +1 -1
  53. package/out/renderer/index.js +2 -0
  54. package/out/renderer/index.js.map +1 -1
  55. package/out/renderer/monitoring.js +19 -22
  56. package/out/renderer/monitoring.js.map +1 -1
  57. package/out/renderer/native-tooltip.js +22 -0
  58. package/out/renderer/native-tooltip.js.map +1 -0
  59. package/out/renderer/pod-details-enhancements.js +106 -0
  60. package/out/renderer/pod-details-enhancements.js.map +1 -0
  61. package/out/renderer/power-log-json-cell.js +44 -38
  62. package/out/renderer/power-log-json-cell.js.map +1 -1
  63. package/out/renderer/power-log-json.js +17 -10
  64. package/out/renderer/power-log-json.js.map +1 -1
  65. package/out/renderer/power-log-sql-editor.js +31 -2
  66. package/out/renderer/power-log-sql-editor.js.map +1 -1
  67. package/out/renderer/power-logs-client.js +5 -0
  68. package/out/renderer/power-logs-client.js.map +1 -1
  69. package/out/renderer/power-logs-entrypoints.js +10 -36
  70. package/out/renderer/power-logs-entrypoints.js.map +1 -1
  71. package/out/renderer/power-logs.css.js +9 -2
  72. package/out/renderer/power-logs.css.js.map +1 -1
  73. package/out/renderer/power-logs.js +376 -214
  74. package/out/renderer/power-logs.js.map +1 -1
  75. package/out/renderer/preferences/dashboard-preferences.js +42 -35
  76. package/out/renderer/preferences/dashboard-preferences.js.map +1 -1
  77. package/out/renderer/preferences/logs-preferences.js +1 -1
  78. package/out/renderer/preferences/logs-preferences.js.map +1 -1
  79. package/out/renderer/preferences/monitoring-preferences.js +7 -4
  80. package/out/renderer/preferences/monitoring-preferences.js.map +1 -1
  81. package/out/renderer/preferences/preferences.js +2 -2
  82. package/out/renderer/preferences/preferences.js.map +1 -1
  83. package/out/renderer/preferences/settings-icon-action.js +12 -11
  84. package/out/renderer/preferences/settings-icon-action.js.map +1 -1
  85. package/out/renderer/relationship-map-finding.js +53 -0
  86. package/out/renderer/relationship-map-finding.js.map +1 -0
  87. package/out/renderer/resource-relationships.css.js +6 -0
  88. package/out/renderer/resource-relationships.css.js.map +1 -1
  89. package/out/renderer/resource-relationships.js +94 -47
  90. package/out/renderer/resource-relationships.js.map +1 -1
  91. package/out/renderer/resource-search-client.js.map +1 -1
  92. package/out/renderer/resource-search.js +88 -35
  93. package/out/renderer/resource-search.js.map +1 -1
  94. package/out/renderer/src/common/gateway-relationships.js +8 -7
  95. package/out/renderer/src/common/gateway-relationships.js.map +1 -1
  96. package/out/renderer/src/common/power-log-json-filter-sql.js +12 -0
  97. package/out/renderer/src/common/power-log-json-filter-sql.js.map +1 -1
  98. package/out/renderer/src/common/power-log-json-preset.js +2 -2
  99. package/out/renderer/src/common/power-log-json-preset.js.map +1 -1
  100. package/out/renderer/src/common/power-logs.js.map +1 -1
  101. package/out/renderer/src/common/resource-relationships.js +10 -8
  102. package/out/renderer/src/common/resource-relationships.js.map +1 -1
  103. package/out/renderer/src/common/resource-search.js.map +1 -1
  104. package/out/renderer/welcome.js +16 -1
  105. package/out/renderer/welcome.js.map +1 -1
  106. package/package.json +1 -1
@@ -2,6 +2,7 @@ const require__global_external_mobx_react = require("./_virtual/_global-external
2
2
  const require_ai_shortcuts_store = require("./src/common/store/ai-shortcuts-store.js");
3
3
  const require_jsx_runtime = require("./_virtual/_global-external_react/jsx-runtime.js");
4
4
  const require_ai_icon = require("./ai-icon.js");
5
+ const require_native_tooltip = require("./native-tooltip.js");
5
6
  //#region src/renderer/ai-shortcuts.tsx
6
7
  var AiStarterPrompts = require__global_external_mobx_react.observer(({ onSelect, disabled = false }) => {
7
8
  return /* @__PURE__ */ require_jsx_runtime.jsx("div", {
@@ -11,24 +12,26 @@ var AiStarterPrompts = require__global_external_mobx_react.observer(({ onSelect,
11
12
  alignItems: "flex-start",
12
13
  gap: 10
13
14
  },
14
- children: require_ai_shortcuts_store.AiShortcutsStore.getInstanceOrCreate().shortcuts.map((shortcut) => /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
15
- type: "button",
16
- className: "Button",
17
- style: {
18
- display: "inline-flex",
19
- alignItems: "center",
20
- gap: 10,
21
- padding: "6px 10px",
22
- minHeight: 32
23
- },
24
- disabled,
15
+ children: require_ai_shortcuts_store.AiShortcutsStore.getInstanceOrCreate().shortcuts.map((shortcut) => /* @__PURE__ */ require_jsx_runtime.jsx(require_native_tooltip.NativeTooltip, {
25
16
  title: `Insert /${shortcut.command}: ${shortcut.label}`,
26
- onClick: () => onSelect(shortcut.prompt),
27
- children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, { material: {
28
- health: "favorite_border",
29
- pods: "view_module",
30
- logs: "subject"
31
- }[shortcut.command] ?? "chat_bubble_outline" }), shortcut.label]
17
+ children: /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
18
+ type: "button",
19
+ className: "Button",
20
+ style: {
21
+ display: "inline-flex",
22
+ alignItems: "center",
23
+ gap: 10,
24
+ padding: "6px 10px",
25
+ minHeight: 32
26
+ },
27
+ disabled,
28
+ onClick: () => onSelect(shortcut.prompt),
29
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, { material: {
30
+ health: "favorite_border",
31
+ pods: "view_module",
32
+ logs: "subject"
33
+ }[shortcut.command] ?? "chat_bubble_outline" }), shortcut.label]
34
+ })
32
35
  }, shortcut.id))
33
36
  });
34
37
  });
@@ -1 +1 @@
1
- {"version":3,"file":"ai-shortcuts.js","names":[],"sources":["../../src/renderer/ai-shortcuts.tsx"],"sourcesContent":["import { observer } from \"mobx-react\";\nimport { AiShortcutsStore } from \"../common/store/ai-shortcuts-store\";\nimport { AiIcon } from \"./ai-icon\";\n\nexport const AiStarterPrompts = observer(\n ({ onSelect, disabled = false }: { onSelect: (prompt: string) => void; disabled?: boolean }) => {\n const preferences = AiShortcutsStore.getInstanceOrCreate<AiShortcutsStore>();\n return (\n <div style={{ display: \"flex\", flexDirection: \"column\", alignItems: \"flex-start\", gap: 10 }}>\n {preferences.shortcuts.map((shortcut) => (\n <button\n type=\"button\"\n className=\"Button\"\n style={{ display: \"inline-flex\", alignItems: \"center\", gap: 10, padding: \"6px 10px\", minHeight: 32 }}\n key={shortcut.id}\n disabled={disabled}\n title={`Insert /${shortcut.command}: ${shortcut.label}`}\n onClick={() => onSelect(shortcut.prompt)}\n >\n <AiIcon\n material={\n ({ health: \"favorite_border\", pods: \"view_module\", logs: \"subject\" } as Record<string, string>)[\n shortcut.command\n ] ?? \"chat_bubble_outline\"\n }\n />\n {shortcut.label}\n </button>\n ))}\n </div>\n );\n },\n);\n"],"mappings":";;;;;AAIA,IAAa,mBAAmB,oCAAA,UAC7B,EAAE,UAAU,WAAW,YAAwE;CAE9F,OACE,oCAAA,IAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAQ,eAAe;GAAU,YAAY;GAAc,KAAK;EAAG;YAFxE,2BAAA,iBAAiB,oBAGhC,CAAA,CAAY,UAAU,KAAK,aAC1B,oCAAA,KAAC,UAAD;GACE,MAAK;GACL,WAAU;GACV,OAAO;IAAE,SAAS;IAAe,YAAY;IAAU,KAAK;IAAI,SAAS;IAAY,WAAW;GAAG;GAEzF;GACV,OAAO,WAAW,SAAS,QAAQ,IAAI,SAAS;GAChD,eAAe,SAAS,SAAS,MAAM;aAPzC,CASE,oCAAA,IAAC,gBAAA,QAAD,EACE,UACG;IAAE,QAAQ;IAAmB,MAAM;IAAe,MAAM;GAAU,EACjE,SAAS,YACN,sBAER,CAAA,GACA,SAAS,KACJ;KAbD,SAAS,EAaR,CACT;CACE,CAAA;AAET,CACF"}
1
+ {"version":3,"file":"ai-shortcuts.js","names":[],"sources":["../../src/renderer/ai-shortcuts.tsx"],"sourcesContent":["import { observer } from \"mobx-react\";\nimport { AiShortcutsStore } from \"../common/store/ai-shortcuts-store\";\nimport { AiIcon } from \"./ai-icon\";\nimport { NativeTooltip } from \"./native-tooltip\";\n\nexport const AiStarterPrompts = observer(\n ({ onSelect, disabled = false }: { onSelect: (prompt: string) => void; disabled?: boolean }) => {\n const preferences = AiShortcutsStore.getInstanceOrCreate<AiShortcutsStore>();\n return (\n <div style={{ display: \"flex\", flexDirection: \"column\", alignItems: \"flex-start\", gap: 10 }}>\n {preferences.shortcuts.map((shortcut) => (\n <NativeTooltip key={shortcut.id} title={`Insert /${shortcut.command}: ${shortcut.label}`}>\n <button\n type=\"button\"\n className=\"Button\"\n style={{ display: \"inline-flex\", alignItems: \"center\", gap: 10, padding: \"6px 10px\", minHeight: 32 }}\n disabled={disabled}\n onClick={() => onSelect(shortcut.prompt)}\n >\n <AiIcon\n material={\n ({ health: \"favorite_border\", pods: \"view_module\", logs: \"subject\" } as Record<string, string>)[\n shortcut.command\n ] ?? \"chat_bubble_outline\"\n }\n />\n {shortcut.label}\n </button>\n </NativeTooltip>\n ))}\n </div>\n );\n },\n);\n"],"mappings":";;;;;;AAKA,IAAa,mBAAmB,oCAAA,UAC7B,EAAE,UAAU,WAAW,YAAwE;CAE9F,OACE,oCAAA,IAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAQ,eAAe;GAAU,YAAY;GAAc,KAAK;EAAG;YAFxE,2BAAA,iBAAiB,oBAGhC,CAAA,CAAY,UAAU,KAAK,aAC1B,oCAAA,IAAC,uBAAA,eAAD;GAAiC,OAAO,WAAW,SAAS,QAAQ,IAAI,SAAS;aAC/E,oCAAA,KAAC,UAAD;IACE,MAAK;IACL,WAAU;IACV,OAAO;KAAE,SAAS;KAAe,YAAY;KAAU,KAAK;KAAI,SAAS;KAAY,WAAW;IAAG;IACzF;IACV,eAAe,SAAS,SAAS,MAAM;cALzC,CAOE,oCAAA,IAAC,gBAAA,QAAD,EACE,UACG;KAAE,QAAQ;KAAmB,MAAM;KAAe,MAAM;IAAU,EACjE,SAAS,YACN,sBAER,CAAA,GACA,SAAS,KACJ;;EACK,GAjBK,SAAS,EAiBd,CAChB;CACE,CAAA;AAET,CACF"}
@@ -2,10 +2,10 @@ const require_extensions = require("./_virtual/_global-external_@freelensapp/ext
2
2
  const require__global_external_react = require("./_virtual/_global-external_react.js");
3
3
  const require_jsx_runtime = require("./_virtual/_global-external_react/jsx-runtime.js");
4
4
  const require_ai_icon = require("./ai-icon.js");
5
+ const require_native_tooltip = require("./native-tooltip.js");
5
6
  const require_ai_terminal_context = require("./src/common/ai-terminal-context.js");
6
7
  const require_date_time = require("./src/common/date-time.js");
7
8
  //#region src/renderer/ai-terminal-context.tsx
8
- var nextTerminalTooltip = 0;
9
9
  function activeAiTerminal(clusterId) {
10
10
  if (!clusterId || require_extensions.Renderer.Catalog.activeCluster.get()?.metadata.uid !== clusterId) return;
11
11
  for (const element of document.querySelectorAll(`[data-testid^="dock-tab-content-for-"]`)) {
@@ -52,7 +52,6 @@ function captureAiTerminal(clusterId, expectedId) {
52
52
  }
53
53
  }
54
54
  function AiTerminalContext({ clusterId, attachment, onChange, disabled }) {
55
- const [tooltipId] = require__global_external_react.useState(() => `power-terminal-tooltip-${++nextTerminalTooltip}`);
56
55
  const [terminalId, setTerminalId] = require__global_external_react.useState(() => activeAiTerminal(clusterId));
57
56
  const [preview, setPreview] = require__global_external_react.useState();
58
57
  const [unavailable, setUnavailable] = require__global_external_react.useState(false);
@@ -110,58 +109,53 @@ function AiTerminalContext({ clusterId, attachment, onChange, disabled }) {
110
109
  clusterId,
111
110
  onChange
112
111
  ]);
113
- const tooltip = attached ? "Detach terminal output from this question" : terminalId ? "Attach terminal output to this question" : "Open a connected terminal in this cluster";
114
112
  return /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
115
113
  className: "power-ai-terminal-context",
116
114
  style: { fontSize: 11 },
117
115
  children: [
118
- /* @__PURE__ */ require_jsx_runtime.jsx("style", { children: `.power-ai-terminal-trigger{position:absolute;top:8px;right:8px;display:inline-flex}.power-ai-terminal-tooltip{position:absolute;right:0;bottom:calc(100% + 8px);z-index:20;width:max-content;max-width:240px;padding:6px 8px;border:1px solid var(--borderFaintColor);border-radius:4px;background:var(--mainBackground);color:var(--textColorPrimary);font-size:12px;line-height:1.4;visibility:hidden;opacity:0;pointer-events:none}.power-ai-terminal-trigger:hover .power-ai-terminal-tooltip,.power-ai-terminal-trigger:focus-within .power-ai-terminal-tooltip{visibility:visible;opacity:1}` }),
119
- /* @__PURE__ */ require_jsx_runtime.jsxs("span", {
116
+ /* @__PURE__ */ require_jsx_runtime.jsx("style", { children: `.power-ai-terminal-trigger{position:absolute;top:8px;right:8px;display:inline-flex}` }),
117
+ /* @__PURE__ */ require_jsx_runtime.jsx("span", {
120
118
  className: "power-ai-terminal-trigger",
121
- children: [/* @__PURE__ */ require_jsx_runtime.jsx("button", {
122
- type: "button",
123
- "aria-describedby": tooltipId,
124
- "aria-label": "Attach terminal output",
125
- "aria-pressed": attached,
126
- style: {
127
- display: "inline-flex",
128
- alignItems: "center",
129
- justifyContent: "center",
130
- width: 26,
131
- height: 26,
132
- minHeight: 26,
133
- padding: 0,
134
- color: "var(--textColorSecondary)",
135
- borderColor: "var(--borderFaintColor)",
136
- background: attached ? "color-mix(in srgb, var(--textColorPrimary) 8%, transparent)" : "transparent"
137
- },
138
- disabled: disabled || !terminalId,
139
- title: tooltip,
140
- onClick: () => {
141
- if (attached) {
142
- onChange(void 0);
143
- setPreview(void 0);
144
- return;
145
- }
146
- const snapshot = captureAiTerminal(clusterId, terminalId);
147
- setUnavailable(!snapshot);
148
- if (!snapshot || !clusterId || !terminalId) return;
149
- setPreview(snapshot);
150
- onChange({
151
- clusterId,
152
- terminalId
153
- });
154
- },
155
- children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
156
- material: "terminal",
157
- size: 16
119
+ children: /* @__PURE__ */ require_jsx_runtime.jsx(require_native_tooltip.NativeTooltip, {
120
+ title: attached ? "Detach terminal output from this question" : terminalId ? "Attach terminal output to this question" : "Open a connected terminal in this cluster",
121
+ children: /* @__PURE__ */ require_jsx_runtime.jsx("button", {
122
+ type: "button",
123
+ "aria-label": "Attach terminal output",
124
+ "aria-pressed": attached,
125
+ style: {
126
+ display: "inline-flex",
127
+ alignItems: "center",
128
+ justifyContent: "center",
129
+ width: 26,
130
+ height: 26,
131
+ minHeight: 26,
132
+ padding: 0,
133
+ color: "var(--textColorSecondary)",
134
+ borderColor: "var(--borderFaintColor)",
135
+ background: attached ? "color-mix(in srgb, var(--textColorPrimary) 8%, transparent)" : "transparent"
136
+ },
137
+ disabled: disabled || !terminalId,
138
+ onClick: () => {
139
+ if (attached) {
140
+ onChange(void 0);
141
+ setPreview(void 0);
142
+ return;
143
+ }
144
+ const snapshot = captureAiTerminal(clusterId, terminalId);
145
+ setUnavailable(!snapshot);
146
+ if (!snapshot || !clusterId || !terminalId) return;
147
+ setPreview(snapshot);
148
+ onChange({
149
+ clusterId,
150
+ terminalId
151
+ });
152
+ },
153
+ children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
154
+ material: "terminal",
155
+ size: 16
156
+ })
158
157
  })
159
- }), /* @__PURE__ */ require_jsx_runtime.jsx("span", {
160
- role: "tooltip",
161
- id: tooltipId,
162
- className: "power-ai-terminal-tooltip",
163
- children: tooltip
164
- })]
158
+ })
165
159
  }),
166
160
  attached && preview && /* @__PURE__ */ require_jsx_runtime.jsxs("details", {
167
161
  style: {
@@ -1 +1 @@
1
- {"version":3,"file":"ai-terminal-context.js","names":[],"sources":["../../src/renderer/ai-terminal-context.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { useEffect, useState } from \"react\";\nimport { type AiTerminalSnapshot, sanitizeAiTerminalSnapshot } from \"../common/ai-terminal-context\";\nimport { formatDateTime } from \"../common/date-time\";\nimport { AiIcon } from \"./ai-icon\";\n\nlet nextTerminalTooltip = 0;\n\ntype TerminalBuffer = {\n length: number;\n baseY: number;\n cursorY: number;\n getLine(index: number): { isWrapped?: boolean; translateToString(trimRight?: boolean): string } | undefined;\n};\n\nexport function activeAiTerminal(clusterId?: string): string | undefined {\n if (!clusterId || Renderer.Catalog.activeCluster.get()?.metadata.uid !== clusterId) return;\n const prefix = \"dock-tab-content-for-\";\n for (const element of document.querySelectorAll<HTMLElement>(`[data-testid^=\"${prefix}\"]`)) {\n let visible = true;\n for (let node: HTMLElement | null = element; node; node = node.parentElement) {\n const style = getComputedStyle(node);\n if (\n node.hidden ||\n node.getAttribute(\"aria-hidden\") === \"true\" ||\n style.display === \"none\" ||\n style.visibility === \"hidden\" ||\n style.visibility === \"collapse\"\n ) {\n visible = false;\n break;\n }\n }\n const id = element.dataset.testid?.slice(prefix.length);\n if (!visible || !id || !Renderer.Component.terminalStore.isConnected(id)) continue;\n const api = Renderer.Component.terminalStore.getTerminalApi(id);\n if (api?.isReady && api.isConnected()) return id;\n }\n return;\n}\n\nexport function captureAiTerminal(clusterId?: string, expectedId?: string): AiTerminalSnapshot | undefined {\n const id = activeAiTerminal(clusterId);\n if (!id || (expectedId && id !== expectedId)) return;\n try {\n // [xterm] is a guarded host implementation detail with no public buffer accessor\n const terminal = Renderer.Component.terminalStore.getTerminal(id) as unknown as\n | {\n xterm?: { buffer?: { active?: TerminalBuffer } };\n }\n | undefined;\n const buffer = terminal?.xterm?.buffer?.active;\n if (!buffer || !Number.isSafeInteger(buffer.length) || buffer.length > 10_000 || buffer.length < 0) return;\n const end = buffer.length;\n if (!Number.isSafeInteger(end) || end < 0) return;\n const logical: string[] = [];\n let characters = 0;\n for (let index = 0; index < end; index++) {\n const line = buffer.getLine(index);\n if (!line || (index === 0 && line.isWrapped)) return;\n const text = line.translateToString(!buffer.getLine(index + 1)?.isWrapped);\n characters += text.length + 1;\n if (characters > 1_000_000) return;\n if (line.isWrapped && logical.length) logical[logical.length - 1] += text;\n else logical.push(text);\n }\n if (activeAiTerminal(clusterId) !== id) return;\n return sanitizeAiTerminalSnapshot({ text: logical.join(\"\\n\"), capturedAt: new Date().toISOString() });\n } catch {\n return;\n }\n}\n\nexport interface AiTerminalAttachment {\n clusterId: string;\n terminalId: string;\n}\n\nexport function AiTerminalContext({\n clusterId,\n attachment,\n onChange,\n disabled,\n}: {\n clusterId?: string;\n attachment?: AiTerminalAttachment;\n onChange(value: AiTerminalAttachment | undefined): void;\n disabled: boolean;\n}) {\n const [tooltipId] = useState(() => `power-terminal-tooltip-${++nextTerminalTooltip}`);\n const [terminalId, setTerminalId] = useState(() => activeAiTerminal(clusterId));\n const [preview, setPreview] = useState<AiTerminalSnapshot>();\n const [unavailable, setUnavailable] = useState(false);\n useEffect(() => {\n const update = () => setTerminalId(activeAiTerminal(clusterId));\n update();\n const timer = window.setInterval(update, 500);\n return () => window.clearInterval(timer);\n }, [clusterId]);\n const attached = Boolean(attachment && attachment.clusterId === clusterId && attachment.terminalId === terminalId);\n useEffect(() => {\n if (!attached) {\n if (attachment) onChange(undefined);\n setPreview(undefined);\n return;\n }\n let stopped = false;\n let scheduled: number | undefined;\n let polling: number | undefined;\n let subscription: { dispose(): void } | undefined;\n const stop = () => {\n stopped = true;\n window.clearTimeout(scheduled);\n window.clearInterval(polling);\n subscription?.dispose();\n subscription = undefined;\n };\n const update = () => {\n if (stopped) return;\n scheduled = undefined;\n const snapshot = captureAiTerminal(clusterId, attachment?.terminalId);\n setUnavailable(!snapshot);\n if (!snapshot) {\n setPreview(undefined);\n if (activeAiTerminal(clusterId) !== attachment?.terminalId) {\n stop();\n onChange(undefined);\n }\n return;\n }\n setPreview((previous) =>\n previous?.text === snapshot.text && previous.truncated === snapshot.truncated ? previous : snapshot,\n );\n };\n update();\n if (stopped) return stop;\n try {\n const terminal = Renderer.Component.terminalStore.getTerminal(attachment!.terminalId) as unknown as\n | {\n xterm?: { onWriteParsed?: (listener: () => void) => { dispose(): void } };\n }\n | undefined;\n subscription = terminal?.xterm?.onWriteParsed?.(() => {\n if (!stopped && scheduled === undefined) scheduled = window.setTimeout(update, 250);\n });\n } catch {}\n if (!subscription) polling = window.setInterval(update, 1000);\n return stop;\n }, [attached, attachment, clusterId, onChange]);\n const tooltip = attached\n ? \"Detach terminal output from this question\"\n : terminalId\n ? \"Attach terminal output to this question\"\n : \"Open a connected terminal in this cluster\";\n return (\n <div className=\"power-ai-terminal-context\" style={{ fontSize: 11 }}>\n <style>{`.power-ai-terminal-trigger{position:absolute;top:8px;right:8px;display:inline-flex}.power-ai-terminal-tooltip{position:absolute;right:0;bottom:calc(100% + 8px);z-index:20;width:max-content;max-width:240px;padding:6px 8px;border:1px solid var(--borderFaintColor);border-radius:4px;background:var(--mainBackground);color:var(--textColorPrimary);font-size:12px;line-height:1.4;visibility:hidden;opacity:0;pointer-events:none}.power-ai-terminal-trigger:hover .power-ai-terminal-tooltip,.power-ai-terminal-trigger:focus-within .power-ai-terminal-tooltip{visibility:visible;opacity:1}`}</style>\n <span className=\"power-ai-terminal-trigger\">\n <button\n type=\"button\"\n aria-describedby={tooltipId}\n aria-label=\"Attach terminal output\"\n aria-pressed={attached}\n style={{\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n width: 26,\n height: 26,\n minHeight: 26,\n padding: 0,\n color: \"var(--textColorSecondary)\",\n borderColor: \"var(--borderFaintColor)\",\n background: attached ? \"color-mix(in srgb, var(--textColorPrimary) 8%, transparent)\" : \"transparent\",\n }}\n disabled={disabled || !terminalId}\n title={tooltip}\n onClick={() => {\n if (attached) {\n onChange(undefined);\n setPreview(undefined);\n return;\n }\n const snapshot = captureAiTerminal(clusterId, terminalId);\n setUnavailable(!snapshot);\n if (!snapshot || !clusterId || !terminalId) return;\n setPreview(snapshot);\n onChange({ clusterId, terminalId });\n }}\n >\n <AiIcon material=\"terminal\" size={16} />\n </button>\n <span role=\"tooltip\" id={tooltipId} className=\"power-ai-terminal-tooltip\">\n {tooltip}\n </span>\n </span>\n {attached && preview && (\n <details style={{ padding: \"0 16px 4px\", margin: 0 }}>\n <summary>\n Terminal · {preview.text.split(\"\\n\").length} rows{preview.truncated ? \" · truncated\" : \"\"}\n </summary>\n <p>\n Preview captured <span style={{ whiteSpace: \"pre-wrap\" }}>{formatDateTime(preview.capturedAt)}</span> ·\n captured again when sent · up to 300 rows / 32 KiB · Common credentials masked. Review before sending.\n </p>\n <pre style={{ maxHeight: 160, overflow: \"auto\", whiteSpace: \"pre-wrap\" }}>{preview.text}</pre>\n <p>The attachment is not stored in chat history. Assistant replies may quote it.</p>\n </details>\n )}\n {unavailable && (\n <div role=\"status\" style={{ padding: \"4px 16px 8px\" }}>\n Terminal output is unavailable or exceeds the safe capture limit\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ai-terminal-context.js","names":[],"sources":["../../src/renderer/ai-terminal-context.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { useEffect, useState } from \"react\";\nimport { type AiTerminalSnapshot, sanitizeAiTerminalSnapshot } from \"../common/ai-terminal-context\";\nimport { formatDateTime } from \"../common/date-time\";\nimport { AiIcon } from \"./ai-icon\";\nimport { NativeTooltip } from \"./native-tooltip\";\n\ntype TerminalBuffer = {\n length: number;\n baseY: number;\n cursorY: number;\n getLine(index: number): { isWrapped?: boolean; translateToString(trimRight?: boolean): string } | undefined;\n};\n\nexport function activeAiTerminal(clusterId?: string): string | undefined {\n if (!clusterId || Renderer.Catalog.activeCluster.get()?.metadata.uid !== clusterId) return;\n const prefix = \"dock-tab-content-for-\";\n for (const element of document.querySelectorAll<HTMLElement>(`[data-testid^=\"${prefix}\"]`)) {\n let visible = true;\n for (let node: HTMLElement | null = element; node; node = node.parentElement) {\n const style = getComputedStyle(node);\n if (\n node.hidden ||\n node.getAttribute(\"aria-hidden\") === \"true\" ||\n style.display === \"none\" ||\n style.visibility === \"hidden\" ||\n style.visibility === \"collapse\"\n ) {\n visible = false;\n break;\n }\n }\n const id = element.dataset.testid?.slice(prefix.length);\n if (!visible || !id || !Renderer.Component.terminalStore.isConnected(id)) continue;\n const api = Renderer.Component.terminalStore.getTerminalApi(id);\n if (api?.isReady && api.isConnected()) return id;\n }\n return;\n}\n\nexport function captureAiTerminal(clusterId?: string, expectedId?: string): AiTerminalSnapshot | undefined {\n const id = activeAiTerminal(clusterId);\n if (!id || (expectedId && id !== expectedId)) return;\n try {\n // [xterm] is a guarded host implementation detail with no public buffer accessor\n const terminal = Renderer.Component.terminalStore.getTerminal(id) as unknown as\n | {\n xterm?: { buffer?: { active?: TerminalBuffer } };\n }\n | undefined;\n const buffer = terminal?.xterm?.buffer?.active;\n if (!buffer || !Number.isSafeInteger(buffer.length) || buffer.length > 10_000 || buffer.length < 0) return;\n const end = buffer.length;\n if (!Number.isSafeInteger(end) || end < 0) return;\n const logical: string[] = [];\n let characters = 0;\n for (let index = 0; index < end; index++) {\n const line = buffer.getLine(index);\n if (!line || (index === 0 && line.isWrapped)) return;\n const text = line.translateToString(!buffer.getLine(index + 1)?.isWrapped);\n characters += text.length + 1;\n if (characters > 1_000_000) return;\n if (line.isWrapped && logical.length) logical[logical.length - 1] += text;\n else logical.push(text);\n }\n if (activeAiTerminal(clusterId) !== id) return;\n return sanitizeAiTerminalSnapshot({ text: logical.join(\"\\n\"), capturedAt: new Date().toISOString() });\n } catch {\n return;\n }\n}\n\nexport interface AiTerminalAttachment {\n clusterId: string;\n terminalId: string;\n}\n\nexport function AiTerminalContext({\n clusterId,\n attachment,\n onChange,\n disabled,\n}: {\n clusterId?: string;\n attachment?: AiTerminalAttachment;\n onChange(value: AiTerminalAttachment | undefined): void;\n disabled: boolean;\n}) {\n const [terminalId, setTerminalId] = useState(() => activeAiTerminal(clusterId));\n const [preview, setPreview] = useState<AiTerminalSnapshot>();\n const [unavailable, setUnavailable] = useState(false);\n useEffect(() => {\n const update = () => setTerminalId(activeAiTerminal(clusterId));\n update();\n const timer = window.setInterval(update, 500);\n return () => window.clearInterval(timer);\n }, [clusterId]);\n const attached = Boolean(attachment && attachment.clusterId === clusterId && attachment.terminalId === terminalId);\n useEffect(() => {\n if (!attached) {\n if (attachment) onChange(undefined);\n setPreview(undefined);\n return;\n }\n let stopped = false;\n let scheduled: number | undefined;\n let polling: number | undefined;\n let subscription: { dispose(): void } | undefined;\n const stop = () => {\n stopped = true;\n window.clearTimeout(scheduled);\n window.clearInterval(polling);\n subscription?.dispose();\n subscription = undefined;\n };\n const update = () => {\n if (stopped) return;\n scheduled = undefined;\n const snapshot = captureAiTerminal(clusterId, attachment?.terminalId);\n setUnavailable(!snapshot);\n if (!snapshot) {\n setPreview(undefined);\n if (activeAiTerminal(clusterId) !== attachment?.terminalId) {\n stop();\n onChange(undefined);\n }\n return;\n }\n setPreview((previous) =>\n previous?.text === snapshot.text && previous.truncated === snapshot.truncated ? previous : snapshot,\n );\n };\n update();\n if (stopped) return stop;\n try {\n const terminal = Renderer.Component.terminalStore.getTerminal(attachment!.terminalId) as unknown as\n | {\n xterm?: { onWriteParsed?: (listener: () => void) => { dispose(): void } };\n }\n | undefined;\n subscription = terminal?.xterm?.onWriteParsed?.(() => {\n if (!stopped && scheduled === undefined) scheduled = window.setTimeout(update, 250);\n });\n } catch {}\n if (!subscription) polling = window.setInterval(update, 1000);\n return stop;\n }, [attached, attachment, clusterId, onChange]);\n const tooltip = attached\n ? \"Detach terminal output from this question\"\n : terminalId\n ? \"Attach terminal output to this question\"\n : \"Open a connected terminal in this cluster\";\n return (\n <div className=\"power-ai-terminal-context\" style={{ fontSize: 11 }}>\n <style>{`.power-ai-terminal-trigger{position:absolute;top:8px;right:8px;display:inline-flex}`}</style>\n <span className=\"power-ai-terminal-trigger\">\n <NativeTooltip title={tooltip}>\n <button\n type=\"button\"\n aria-label=\"Attach terminal output\"\n aria-pressed={attached}\n style={{\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n width: 26,\n height: 26,\n minHeight: 26,\n padding: 0,\n color: \"var(--textColorSecondary)\",\n borderColor: \"var(--borderFaintColor)\",\n background: attached ? \"color-mix(in srgb, var(--textColorPrimary) 8%, transparent)\" : \"transparent\",\n }}\n disabled={disabled || !terminalId}\n onClick={() => {\n if (attached) {\n onChange(undefined);\n setPreview(undefined);\n return;\n }\n const snapshot = captureAiTerminal(clusterId, terminalId);\n setUnavailable(!snapshot);\n if (!snapshot || !clusterId || !terminalId) return;\n setPreview(snapshot);\n onChange({ clusterId, terminalId });\n }}\n >\n <AiIcon material=\"terminal\" size={16} />\n </button>\n </NativeTooltip>\n </span>\n {attached && preview && (\n <details style={{ padding: \"0 16px 4px\", margin: 0 }}>\n <summary>\n Terminal · {preview.text.split(\"\\n\").length} rows{preview.truncated ? \" · truncated\" : \"\"}\n </summary>\n <p>\n Preview captured <span style={{ whiteSpace: \"pre-wrap\" }}>{formatDateTime(preview.capturedAt)}</span> ·\n captured again when sent · up to 300 rows / 32 KiB · Common credentials masked. Review before sending.\n </p>\n <pre style={{ maxHeight: 160, overflow: \"auto\", whiteSpace: \"pre-wrap\" }}>{preview.text}</pre>\n <p>The attachment is not stored in chat history. Assistant replies may quote it.</p>\n </details>\n )}\n {unavailable && (\n <div role=\"status\" style={{ padding: \"4px 16px 8px\" }}>\n Terminal output is unavailable or exceeds the safe capture limit\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -8,8 +8,10 @@ const require_ai_shortcuts_store = require("./src/common/store/ai-shortcuts-stor
8
8
  const require__global_external_react = require("./_virtual/_global-external_react.js");
9
9
  const require__global_external_react_dom = require("./_virtual/_global-external_react-dom.js");
10
10
  const require_ai = require("./src/common/ai.js");
11
+ const require_ai_context_mutations = require("./ai-context-mutations.js");
11
12
  const require_jsx_runtime = require("./_virtual/_global-external_react/jsx-runtime.js");
12
13
  const require_ai_icon = require("./ai-icon.js");
14
+ const require_native_tooltip = require("./native-tooltip.js");
13
15
  const require_ai_copy_answer = require("./ai-copy-answer.js");
14
16
  const require_ai_diagnostic_handoff = require("./ai-diagnostic-handoff.js");
15
17
  const require_ai_extras$1 = require("./ai-extras.js");
@@ -220,6 +222,9 @@ var AiChat = require__global_external_mobx_react.observer(({ isClusterDocument }
220
222
  composer.current?.focus();
221
223
  };
222
224
  const [context, setContext] = require__global_external_react.useState(readAiContext);
225
+ require__global_external_react.useEffect(() => {
226
+ if (open && !finding && !terminalAttachment) window.dispatchEvent(new Event("power-ai-request-screen-context"));
227
+ }, [open]);
223
228
  const [pending, setPending] = require__global_external_react.useState([]);
224
229
  const [error, setError] = require__global_external_react.useState("");
225
230
  const [stopped, setStopped] = require__global_external_react.useState([]);
@@ -284,7 +289,9 @@ var AiChat = require__global_external_mobx_react.observer(({ isClusterDocument }
284
289
  window.addEventListener("popstate", update);
285
290
  update();
286
291
  const timer = window.setInterval(update, 500);
287
- const observer = new MutationObserver(update);
292
+ const observer = new MutationObserver((mutations) => {
293
+ if (require_ai_context_mutations.hasAiContextMutations(mutations)) update();
294
+ });
288
295
  observer.observe(document.body, {
289
296
  childList: true,
290
297
  subtree: true,
@@ -301,7 +308,7 @@ var AiChat = require__global_external_mobx_react.observer(({ isClusterDocument }
301
308
  if (!next || !preferences.enabled || !isClusterDocument() || next.clusterId !== readAiContext().clusterId) return;
302
309
  update();
303
310
  setFinding(next);
304
- setDraft((previous) => previous || (preferences.conversations[next.clusterId]?.length ? "" : "Explain this finding and suggest the next read-only checks."));
311
+ setDraft((previous) => previous || (next.source === "relationships" || next.source === "diagnostics" || preferences.conversations[next.clusterId]?.length ? "" : "Explain this finding and suggest the next read-only checks."));
305
312
  setOpen(true);
306
313
  requestAnimationFrame(() => composer.current?.focus());
307
314
  };
@@ -536,30 +543,32 @@ var AiChat = require__global_external_mobx_react.observer(({ isClusterDocument }
536
543
  };
537
544
  return /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [
538
545
  /* @__PURE__ */ require_jsx_runtime.jsx("style", { children: styles }),
539
- !open && /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
540
- type: "button",
541
- className: `power-ai-launcher${launcherHidden ? " power-ai-launcher-hidden" : ""}`,
542
- "aria-hidden": launcherHidden || void 0,
543
- tabIndex: launcherHidden ? -1 : 0,
544
- style: {
545
- bottom: launcherBottom,
546
- left: resourceDetailsOpen ? launcherLeft : "auto",
547
- right: resourceDetailsOpen ? "auto" : 16
548
- },
549
- onClick: () => setOpen(true),
550
- "aria-label": "Open AI assistant",
546
+ !open && /* @__PURE__ */ require_jsx_runtime.jsx(require_native_tooltip.NativeTooltip, {
551
547
  title: "Open AI assistant",
552
- children: [
553
- /* @__PURE__ */ require_jsx_runtime.jsx("span", {
554
- "aria-hidden": "true",
555
- children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
556
- material: "chat_bubble_outline",
557
- size: 16
558
- })
559
- }),
560
- " ",
561
- "AI"
562
- ]
548
+ children: /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
549
+ type: "button",
550
+ className: `power-ai-launcher${launcherHidden ? " power-ai-launcher-hidden" : ""}`,
551
+ "aria-hidden": launcherHidden || void 0,
552
+ tabIndex: launcherHidden ? -1 : 0,
553
+ style: {
554
+ bottom: launcherBottom,
555
+ left: resourceDetailsOpen ? launcherLeft : "auto",
556
+ right: resourceDetailsOpen ? "auto" : 16
557
+ },
558
+ onClick: () => setOpen(true),
559
+ "aria-label": "Open AI assistant",
560
+ children: [
561
+ /* @__PURE__ */ require_jsx_runtime.jsx("span", {
562
+ "aria-hidden": "true",
563
+ children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
564
+ material: "chat_bubble_outline",
565
+ size: 16
566
+ })
567
+ }),
568
+ " ",
569
+ "AI"
570
+ ]
571
+ })
563
572
  }),
564
573
  open && /* @__PURE__ */ require_jsx_runtime.jsxs("aside", {
565
574
  className: "power-ai-panel",
@@ -578,67 +587,75 @@ var AiChat = require__global_external_mobx_react.observer(({ isClusterDocument }
578
587
  /* @__PURE__ */ require_jsx_runtime.jsxs("strong", {
579
588
  className: "power-ai-title",
580
589
  children: [
581
- /* @__PURE__ */ require_jsx_runtime.jsxs("span", {
582
- className: "power-ai-brand",
590
+ /* @__PURE__ */ require_jsx_runtime.jsx(require_native_tooltip.NativeTooltip, {
583
591
  title: "AI assistant",
584
- children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
585
- material: "auto_awesome",
586
- size: 18
587
- }), " AI"]
592
+ children: /* @__PURE__ */ require_jsx_runtime.jsxs("span", {
593
+ className: "power-ai-brand",
594
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
595
+ material: "auto_awesome",
596
+ size: 18
597
+ }), " AI"]
598
+ })
588
599
  }),
589
600
  " ",
590
601
  "Assistant"
591
602
  ]
592
603
  }),
593
- /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
594
- type: "button",
595
- disabled: busy,
596
- "aria-label": "New chat",
604
+ /* @__PURE__ */ require_jsx_runtime.jsx(require_native_tooltip.NativeTooltip, {
597
605
  title: "Clear this conversation and start a new chat",
598
- onClick: () => {
599
- setTerminalAttachment(void 0);
600
- setFinding(void 0);
601
- preferences.setMessages(key, []);
602
- setStopped((keys) => keys.filter((entry) => entry !== key));
603
- setActivity({
604
- key,
605
- reads: []
606
- });
607
- setError("");
608
- },
609
- children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
610
- material: "add_comment",
611
- size: 16
612
- }), " New chat"]
606
+ children: /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
607
+ type: "button",
608
+ disabled: busy,
609
+ "aria-label": "New chat",
610
+ onClick: () => {
611
+ setTerminalAttachment(void 0);
612
+ setFinding(void 0);
613
+ preferences.setMessages(key, []);
614
+ setStopped((keys) => keys.filter((entry) => entry !== key));
615
+ setActivity({
616
+ key,
617
+ reads: []
618
+ });
619
+ setError("");
620
+ },
621
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
622
+ material: "add_comment",
623
+ size: 16
624
+ }), " New chat"]
625
+ })
613
626
  }),
614
- /* @__PURE__ */ require_jsx_runtime.jsx("button", {
615
- type: "button",
616
- className: "power-ai-pin",
617
- "aria-label": "Pin AI assistant",
618
- "aria-pressed": pinned,
627
+ /* @__PURE__ */ require_jsx_runtime.jsx(require_native_tooltip.NativeTooltip, {
619
628
  title: pinned ? "Unpin to close when clicking outside" : "Pin to keep the assistant open",
620
- onClick: () => setPinned((value) => !value),
621
- children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
622
- material: "push_pin",
623
- size: 18
629
+ children: /* @__PURE__ */ require_jsx_runtime.jsx("button", {
630
+ type: "button",
631
+ className: "power-ai-pin",
632
+ "aria-label": "Pin AI assistant",
633
+ "aria-pressed": pinned,
634
+ onClick: () => setPinned((value) => !value),
635
+ children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
636
+ material: "push_pin",
637
+ size: 18
638
+ })
624
639
  })
625
640
  }),
626
- /* @__PURE__ */ require_jsx_runtime.jsx("button", {
627
- type: "button",
628
- onPointerDown: (event) => event.stopPropagation(),
629
- onMouseDown: (event) => event.stopPropagation(),
630
- onClick: (event) => {
631
- event.stopPropagation();
632
- setOpen(false);
633
- },
634
- className: "power-ai-close",
635
- "aria-label": "Close AI assistant",
641
+ /* @__PURE__ */ require_jsx_runtime.jsx(require_native_tooltip.NativeTooltip, {
636
642
  title: "Close AI assistant",
637
- children: /* @__PURE__ */ require_jsx_runtime.jsx("span", {
638
- "aria-hidden": "true",
639
- children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
640
- material: "close",
641
- size: 18
643
+ children: /* @__PURE__ */ require_jsx_runtime.jsx("button", {
644
+ type: "button",
645
+ onPointerDown: (event) => event.stopPropagation(),
646
+ onMouseDown: (event) => event.stopPropagation(),
647
+ onClick: (event) => {
648
+ event.stopPropagation();
649
+ setOpen(false);
650
+ },
651
+ className: "power-ai-close",
652
+ "aria-label": "Close AI assistant",
653
+ children: /* @__PURE__ */ require_jsx_runtime.jsx("span", {
654
+ "aria-hidden": "true",
655
+ children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
656
+ material: "close",
657
+ size: 18
658
+ })
642
659
  })
643
660
  })
644
661
  })
@@ -679,12 +696,12 @@ var AiChat = require__global_external_mobx_react.observer(({ isClusterDocument }
679
696
  className: `power-ai-message power-ai-message-${message.role}`,
680
697
  children: [
681
698
  /* @__PURE__ */ require_jsx_runtime.jsxs("strong", { children: [
682
- message.role === "user" ? /* @__PURE__ */ require_jsx_runtime.jsx("span", {
699
+ message.role === "user" ? /* @__PURE__ */ require_jsx_runtime.jsx(require_native_tooltip.NativeTooltip, {
683
700
  title: "You",
684
- children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
701
+ children: /* @__PURE__ */ require_jsx_runtime.jsx("span", { children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
685
702
  material: "person_outline",
686
703
  size: 16
687
- })
704
+ }) })
688
705
  }) : /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_provider_icon.AiProviderIcon, { provider: message.provider === "claude" ? "claude" : "codex" }),
689
706
  message.role === "user" ? "You" : message.provider === "claude" ? "Claude" : "Codex",
690
707
  message.role === "assistant" && /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_copy_answer.AiCopyAnswer, { text: message.text })
@@ -705,22 +722,24 @@ var AiChat = require__global_external_mobx_react.observer(({ isClusterDocument }
705
722
  color: "var(--textColorSecondary)",
706
723
  margin: "8px 16px"
707
724
  },
708
- children: readActivity.map((read, index) => /* @__PURE__ */ require_jsx_runtime.jsxs("span", {
725
+ children: readActivity.map((read, index) => /* @__PURE__ */ require_jsx_runtime.jsx(require_native_tooltip.NativeTooltip, {
709
726
  title: `${read.method} · ${read.status === "reading" ? "reading" : read.status === "ok" ? "read" : "unavailable"}`,
710
- "data-method": read.method,
711
- "data-status": read.status,
712
- style: { marginRight: 10 },
713
- children: [
714
- /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
715
- material: read.status === "reading" ? "sync" : read.status === "ok" ? "check_circle_outline" : "info_outline",
716
- size: 13
717
- }),
718
- " ",
719
- read.method,
720
- " ·",
721
- " ",
722
- read.status === "reading" ? "reading" : read.status === "ok" ? "read" : "unavailable"
723
- ]
727
+ children: /* @__PURE__ */ require_jsx_runtime.jsxs("span", {
728
+ "data-method": read.method,
729
+ "data-status": read.status,
730
+ style: { marginRight: 10 },
731
+ children: [
732
+ /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
733
+ material: read.status === "reading" ? "sync" : read.status === "ok" ? "check_circle_outline" : "info_outline",
734
+ size: 13
735
+ }),
736
+ " ",
737
+ read.method,
738
+ " ·",
739
+ " ",
740
+ read.status === "reading" ? "reading" : read.status === "ok" ? "read" : "unavailable"
741
+ ]
742
+ })
724
743
  }, index))
725
744
  }),
726
745
  busy && /* @__PURE__ */ require_jsx_runtime.jsxs("p", {
@@ -742,15 +761,17 @@ var AiChat = require__global_external_mobx_react.observer(({ isClusterDocument }
742
761
  children: [
743
762
  "Response stopped",
744
763
  " ",
745
- /* @__PURE__ */ require_jsx_runtime.jsx("button", {
746
- type: "button",
747
- "aria-label": "Dismiss cancellation",
748
- className: "power-ai-dismiss",
764
+ /* @__PURE__ */ require_jsx_runtime.jsx(require_native_tooltip.NativeTooltip, {
749
765
  title: "Dismiss",
750
- onClick: () => setStopped((keys) => keys.filter((entry) => entry !== key)),
751
- children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
752
- material: "close",
753
- size: 14
766
+ children: /* @__PURE__ */ require_jsx_runtime.jsx("button", {
767
+ type: "button",
768
+ "aria-label": "Dismiss cancellation",
769
+ className: "power-ai-dismiss",
770
+ onClick: () => setStopped((keys) => keys.filter((entry) => entry !== key)),
771
+ children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
772
+ material: "close",
773
+ size: 14
774
+ })
754
775
  })
755
776
  })
756
777
  ]
@@ -772,15 +793,17 @@ var AiChat = require__global_external_mobx_react.observer(({ isClusterDocument }
772
793
  role: "listbox",
773
794
  id: "power-ai-commands",
774
795
  "aria-label": "Prompt shortcuts",
775
- children: suggestions.map((shortcut, index) => /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
776
- type: "button",
777
- role: "option",
778
- id: `power-ai-command-${index}`,
779
- "aria-selected": index === suggestionIndex % suggestions.length,
796
+ children: suggestions.map((shortcut, index) => /* @__PURE__ */ require_jsx_runtime.jsx(require_native_tooltip.NativeTooltip, {
780
797
  title: `Insert /${shortcut.command}: ${shortcut.label}`,
781
- onMouseDown: (event) => event.preventDefault(),
782
- onClick: () => selectSuggestion(index),
783
- children: [/* @__PURE__ */ require_jsx_runtime.jsxs("span", { children: ["/", shortcut.command] }), /* @__PURE__ */ require_jsx_runtime.jsx("span", { children: shortcut.label })]
798
+ children: /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
799
+ type: "button",
800
+ role: "option",
801
+ id: `power-ai-command-${index}`,
802
+ "aria-selected": index === suggestionIndex % suggestions.length,
803
+ onMouseDown: (event) => event.preventDefault(),
804
+ onClick: () => selectSuggestion(index),
805
+ children: [/* @__PURE__ */ require_jsx_runtime.jsxs("span", { children: ["/", shortcut.command] }), /* @__PURE__ */ require_jsx_runtime.jsx("span", { children: shortcut.label })]
806
+ })
784
807
  }, shortcut.id))
785
808
  }),
786
809
  /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_extras$1.AiContextPreview, {
@@ -901,7 +924,7 @@ function startAiChat(isClusterDocument = isAiClusterDocument) {
901
924
  const overviewButton = document.createElement("button");
902
925
  overviewButton.type = "button";
903
926
  overviewButton.setAttribute("aria-label", "Ask about cluster");
904
- overviewButton.title = "Open the AI assistant to ask about this cluster";
927
+ overviewButton.id = "power-ai-overview-button";
905
928
  overviewIcon = document.createElement("span");
906
929
  overviewIcon.setAttribute("aria-hidden", "true");
907
930
  overviewLabel = document.createElement("span");
@@ -914,10 +937,13 @@ function startAiChat(isClusterDocument = isAiClusterDocument) {
914
937
  overviewResize = new ResizeObserver(updateOverviewLabel);
915
938
  overviewResize.observe(overview);
916
939
  }
917
- require__global_external_react_dom.default.render(/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
940
+ require__global_external_react_dom.default.render(/* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
918
941
  material: "auto_awesome",
919
942
  size: 16
920
- }), overviewIcon);
943
+ }), /* @__PURE__ */ require_jsx_runtime.jsx(require_extensions.Renderer.Component.Tooltip, {
944
+ targetId: "power-ai-overview-button",
945
+ children: "Open the AI assistant to ask about this cluster"
946
+ })] }), overviewIcon);
921
947
  };
922
948
  const observer = new MutationObserver(reconcile);
923
949
  const stop = require__global_external_mobx.autorun(() => {