@64mb/freelens-power 1.0.1 → 1.0.3
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 +4 -4
- package/THIRD_PARTY_LICENSES +41 -0
- package/out/main/ai.js +9 -6
- package/out/main/ai.js.map +1 -1
- package/out/main/common/ai-extras.js +17 -0
- package/out/main/common/ai-extras.js.map +1 -1
- package/out/main/common/dashboard-readiness.js +88 -0
- package/out/main/common/dashboard-readiness.js.map +1 -0
- package/out/main/common/dashboard-selector.js +9 -1
- package/out/main/common/dashboard-selector.js.map +1 -1
- package/out/main/common/dashboard-views.js +2 -0
- package/out/main/common/dashboard-views.js.map +1 -1
- package/out/main/dashboard-network.js +104 -0
- package/out/main/dashboard-network.js.map +1 -0
- package/out/main/dashboard-views.js +98 -21
- package/out/main/dashboard-views.js.map +1 -1
- package/out/renderer/ai-extras.js +41 -12
- package/out/renderer/ai-extras.js.map +1 -1
- package/out/renderer/ai-message.js +1 -0
- package/out/renderer/ai-message.js.map +1 -1
- package/out/renderer/ai-provider-icon.js +44 -0
- package/out/renderer/ai-provider-icon.js.map +1 -0
- package/out/renderer/ai-shortcuts.js +1 -1
- package/out/renderer/ai-shortcuts.js.map +1 -1
- package/out/renderer/ai.js +53 -10
- package/out/renderer/ai.js.map +1 -1
- package/out/renderer/appearance.js +2 -1
- package/out/renderer/appearance.js.map +1 -1
- package/out/renderer/assets/claude.js +6 -0
- package/out/renderer/assets/claude.js.map +1 -0
- package/out/renderer/dashboard.js +39 -4
- package/out/renderer/dashboard.js.map +1 -1
- package/out/renderer/index.js +2 -0
- package/out/renderer/index.js.map +1 -1
- package/out/renderer/menus/secure-node-shell-menu.js +1 -1
- package/out/renderer/menus/secure-node-shell-menu.js.map +1 -1
- package/out/renderer/monitoring.js +672 -91
- package/out/renderer/monitoring.js.map +1 -1
- package/out/renderer/pod-monitoring.js +282 -0
- package/out/renderer/pod-monitoring.js.map +1 -0
- package/out/renderer/preferences/ai-preferences.js +2 -2
- package/out/renderer/preferences/ai-preferences.js.map +1 -1
- package/out/renderer/preferences/dashboard-preferences.js +1 -8
- package/out/renderer/preferences/dashboard-preferences.js.map +1 -1
- package/out/renderer/preferences/preferences.js +1 -1
- package/out/renderer/preferences/preferences.js.map +1 -1
- package/out/renderer/preferences/settings-icon-action.js +3 -1
- package/out/renderer/preferences/settings-icon-action.js.map +1 -1
- package/out/renderer/src/common/ai-extras.js +17 -0
- package/out/renderer/src/common/ai-extras.js.map +1 -1
- package/out/renderer/src/common/dashboard-selector.js.map +1 -1
- package/out/renderer/src/common/dashboard-views.js +2 -0
- package/out/renderer/src/common/dashboard-views.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-extras.js","names":[],"sources":["../../src/renderer/ai-extras.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { useState } from \"react\";\nimport { type AiResourceReference,
|
|
1
|
+
{"version":3,"file":"ai-extras.js","names":[],"sources":["../../src/renderer/ai-extras.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { useState } from \"react\";\nimport { type AiResourceReference, referencedAiResources, validAiResourceReference } from \"../common/ai-extras\";\nimport { AiIcon } from \"./ai-icon\";\n\nimport type { AiChatRequest } from \"../common/ai\";\n\nexport function AiContextPreview({ context }: { context: AiChatRequest[\"context\"] }) {\n return (\n <details className=\"power-ai-context-preview\" style={{ padding: \"8px 16px\", fontSize: 11 }}>\n <summary title=\"Show the screen and resource metadata attached to your next message\">Attached context</summary>\n <pre style={{ whiteSpace: \"pre-wrap\", overflowWrap: \"anywhere\", maxHeight: 160, overflowY: \"auto\" }}>\n {JSON.stringify(context, null, 2)}\n </pre>\n <p>Screen and resource metadata only. Resource reads happen after sending.</p>\n </details>\n );\n}\n\nexport async function openAiResourceReference(clusterId: string, reference: AiResourceReference): Promise<void> {\n if (!validAiResourceReference(reference) || Renderer.Catalog.activeCluster.get()?.metadata.uid !== clusterId)\n throw new Error(\"Select the original cluster to open this resource\");\n const api = Renderer.K8sApi.apiManager.getApiByKind(reference.kind, reference.apiVersion);\n if (!api || api.isNamespaced !== Boolean(reference.namespace)) throw new Error(\"Resource details are unavailable\");\n const resource = await api.get({ name: reference.name, namespace: reference.namespace });\n if (!resource || Renderer.Catalog.activeCluster.get()?.metadata.uid !== clusterId)\n throw new Error(\"Resource details are unavailable\");\n await Renderer.Navigation.showDetails(api.getUrl({ name: reference.name, namespace: reference.namespace }));\n}\n\nexport function AiResourceReferences({\n clusterId,\n references,\n text,\n onOpened,\n}: {\n clusterId: string;\n references: unknown;\n text: string;\n onOpened?: () => void;\n}) {\n const [error, setError] = useState(\"\");\n const items = referencedAiResources(text, references);\n if (!items.length) return null;\n return (\n <div className=\"power-ai-resource-references\" style={{ marginTop: 12 }}>\n <style>{`.power-ai-resource-references button.power-ai-resource-reference:hover { color: var(--primary); border-color: var(--primary); background: color-mix(in srgb, var(--primary) 10%, var(--contentColor)); } .power-ai-resource-references button.power-ai-resource-reference:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }`}</style>\n <span style={{ fontSize: 11, color: \"var(--textColorSecondary)\" }}>Related resources</span>\n <div style={{ display: \"flex\", flexWrap: \"wrap\", gap: 6, marginTop: 6 }}>\n {items.map((reference) => (\n <button\n type=\"button\"\n key={JSON.stringify(reference)}\n className=\"power-ai-resource-reference\"\n style={{\n display: \"inline-flex\",\n alignItems: \"center\",\n gap: 6,\n maxWidth: \"100%\",\n minWidth: 0,\n padding: \"4px 8px\",\n fontSize: 12,\n lineHeight: 1.4,\n border: \"1px solid var(--borderFaintColor)\",\n borderRadius: 6,\n }}\n title={`Open ${reference.kind}/${reference.name} details · ${reference.namespace ?? \"cluster scoped\"}`}\n onClick={() => {\n setError(\"\");\n void openAiResourceReference(clusterId, reference)\n .then(onOpened)\n .catch(() => setError(\"Unable to open this resource. Check the selected cluster and your access.\"));\n }}\n >\n <span style={{ minWidth: 0, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>\n {reference.kind}/{reference.name}\n {reference.namespace ? ` · ${reference.namespace}` : \"\"}\n </span>\n <AiIcon material=\"arrow_forward\" size={14} />\n </button>\n ))}\n </div>\n {error && <p role=\"alert\">{error}</p>}\n </div>\n );\n}\n"],"mappings":";;;;;;AAOA,SAAgB,iBAAiB,EAAE,WAAkD;CACnF,OACE,oCAAA,KAAC,WAAD;EAAS,WAAU;EAA2B,OAAO;GAAE,SAAS;GAAY,UAAU;EAAG;YAAzF;GACE,oCAAA,IAAC,WAAD;IAAS,OAAM;cAAsE;GAAyB,CAAA;GAC9G,oCAAA,IAAC,OAAD;IAAK,OAAO;KAAE,YAAY;KAAY,cAAc;KAAY,WAAW;KAAK,WAAW;IAAO;cAC/F,KAAK,UAAU,SAAS,MAAM,CAAC;GAC7B,CAAA;GACL,oCAAA,IAAC,KAAD,EAAA,UAAG,0EAA0E,CAAA;EACtE;;AAEb;AAEA,eAAsB,wBAAwB,WAAmB,WAA+C;CAC9G,IAAI,CAAC,kBAAA,yBAAyB,SAAS,KAAK,mBAAA,SAAS,QAAQ,cAAc,IAAI,CAAC,EAAE,SAAS,QAAQ,WACjG,MAAM,IAAI,MAAM,mDAAmD;CACrE,MAAM,MAAM,mBAAA,SAAS,OAAO,WAAW,aAAa,UAAU,MAAM,UAAU,UAAU;CACxF,IAAI,CAAC,OAAO,IAAI,iBAAiB,QAAQ,UAAU,SAAS,GAAG,MAAM,IAAI,MAAM,kCAAkC;CAEjH,IAAI,CAAC,MADkB,IAAI,IAAI;EAAE,MAAM,UAAU;EAAM,WAAW,UAAU;CAAU,CAAC,KACtE,mBAAA,SAAS,QAAQ,cAAc,IAAI,CAAC,EAAE,SAAS,QAAQ,WACtE,MAAM,IAAI,MAAM,kCAAkC;CACpD,MAAM,mBAAA,SAAS,WAAW,YAAY,IAAI,OAAO;EAAE,MAAM,UAAU;EAAM,WAAW,UAAU;CAAU,CAAC,CAAC;AAC5G;AAEA,SAAgB,qBAAqB,EACnC,WACA,YACA,MACA,YAMC;CACD,MAAM,CAAC,OAAO,YAAY,+BAAA,SAAS,EAAE;CACrC,MAAM,QAAQ,kBAAA,sBAAsB,MAAM,UAAU;CACpD,IAAI,CAAC,MAAM,QAAQ,OAAO;CAC1B,OACE,oCAAA,KAAC,OAAD;EAAK,WAAU;EAA+B,OAAO,EAAE,WAAW,GAAG;YAArE;GACE,oCAAA,IAAC,SAAD,EAAA,UAAQ,sVAA6V,CAAA;GACrW,oCAAA,IAAC,QAAD;IAAM,OAAO;KAAE,UAAU;KAAI,OAAO;IAA4B;cAAG;GAAuB,CAAA;GAC1F,oCAAA,IAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAQ,UAAU;KAAQ,KAAK;KAAG,WAAW;IAAE;cACnE,MAAM,KAAK,cACV,oCAAA,KAAC,UAAD;KACE,MAAK;KAEL,WAAU;KACV,OAAO;MACL,SAAS;MACT,YAAY;MACZ,KAAK;MACL,UAAU;MACV,UAAU;MACV,SAAS;MACT,UAAU;MACV,YAAY;MACZ,QAAQ;MACR,cAAc;KAChB;KACA,OAAO,QAAQ,UAAU,KAAK,GAAG,UAAU,KAAK,aAAa,UAAU,aAAa;KACpF,eAAe;MACb,SAAS,EAAE;MACX,wBAA6B,WAAW,SAAS,CAAC,CAC/C,KAAK,QAAQ,CAAC,CACd,YAAY,SAAS,2EAA2E,CAAC;KACtG;eAtBF,CAwBE,oCAAA,KAAC,QAAD;MAAM,OAAO;OAAE,UAAU;OAAG,UAAU;OAAU,cAAc;OAAY,YAAY;MAAS;gBAA/F;OACG,UAAU;OAAK;OAAE,UAAU;OAC3B,UAAU,YAAY,MAAM,UAAU,cAAc;MACjD;SACN,oCAAA,IAAC,gBAAA,QAAD;MAAQ,UAAS;MAAgB,MAAM;KAAK,CAAA,CACtC;OA3BD,KAAK,UAAU,SAAS,CA2BvB,CACT;GACE,CAAA;GACJ,SAAS,oCAAA,IAAC,KAAD;IAAG,MAAK;cAAS;GAAS,CAAA;EACjC;;AAET"}
|
|
@@ -209,6 +209,7 @@ function CodeBlock({ text, language }) {
|
|
|
209
209
|
children: [/* @__PURE__ */ require_jsx_runtime.jsx("span", { children: language || "code" }), /* @__PURE__ */ require_jsx_runtime.jsxs("div", { children: [/* @__PURE__ */ require_jsx_runtime.jsxs("button", {
|
|
210
210
|
type: "button",
|
|
211
211
|
"aria-label": "Copy code",
|
|
212
|
+
title: "Copy code to clipboard",
|
|
212
213
|
onClick: () => {
|
|
213
214
|
try {
|
|
214
215
|
electron.clipboard.writeText(text);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-message.js","names":[],"sources":["../../src/renderer/ai-message.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { clipboard, shell } from \"electron\";\nimport MarkdownIt, { type Token } from \"markdown-it\";\nimport { autorun } from \"mobx\";\nimport { createElement, Fragment, type ReactNode, useEffect, useMemo, useState } from \"react\";\nimport { AiIcon } from \"./ai-icon\";\n\nconst markdown = new MarkdownIt({ html: false, linkify: true, breaks: true });\nconst markdownTags = new Set([\n \"p\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"h5\",\n \"h6\",\n \"ul\",\n \"ol\",\n \"li\",\n \"blockquote\",\n \"table\",\n \"thead\",\n \"tbody\",\n \"tr\",\n \"th\",\n \"td\",\n \"strong\",\n \"em\",\n \"s\",\n \"a\",\n]);\n\nfunction externalLink(value: string | number | null): string | undefined {\n if (typeof value !== \"string\" || !value) return;\n try {\n const url = new URL(value);\n if ([\"https:\", \"http:\", \"mailto:\"].includes(url.protocol) && !url.username && !url.password) return url.href;\n } catch {}\n return;\n}\n\nfunction renderTokens(tokens: Token[]): ReactNode[] {\n type Container = { token?: Token; key: number; children: ReactNode[] };\n const stack: Container[] = [{ key: 0, children: [] }];\n const append = (node: ReactNode) => stack[stack.length - 1].children.push(node);\n tokens.forEach((token, index) => {\n if (token.nesting === 1) {\n stack.push({ token, key: index, children: [] });\n return;\n }\n if (token.nesting === -1) {\n if (stack.length === 1) return;\n const container = stack.pop()!;\n const opening = container.token!;\n const props: Record<string, unknown> = { key: container.key };\n let tag = markdownTags.has(opening.tag) && !opening.hidden ? opening.tag : Fragment;\n if (tag === \"a\") {\n const href = externalLink(opening.attrGet(\"href\"));\n if (!href) tag = Fragment;\n else {\n props.href = href;\n props.title = opening.attrGet(\"title\") ?? undefined;\n props.rel = \"noopener noreferrer\";\n props.target = \"_blank\";\n const open = (event: React.MouseEvent<HTMLAnchorElement>) => {\n event.preventDefault();\n if (event.type === \"auxclick\" && event.button !== 1) return;\n void shell.openExternal(href).catch(() => {});\n };\n props.onClick = open;\n props.onAuxClick = open;\n }\n }\n if (tag === \"ol\") {\n const start = Number(opening.attrGet(\"start\"));\n if (Number.isSafeInteger(start) && start > 0) props.start = start;\n }\n if (tag === \"th\" || tag === \"td\") {\n const alignment = String(opening.attrGet(\"style\") ?? \"\").match(/^text-align:(left|right|center)$/)?.[1];\n if (alignment) props.style = { textAlign: alignment };\n }\n append(createElement(tag, props, ...container.children));\n return;\n }\n switch (token.type) {\n case \"inline\":\n append(<Fragment key={index}>{renderTokens(token.children ?? [])}</Fragment>);\n break;\n case \"code_inline\":\n append(<code key={index}>{token.content}</code>);\n break;\n case \"fence\":\n case \"code_block\":\n append(<CodeBlock key={index} language={token.info.trim().split(/\\s+/)[0]} text={token.content} />);\n break;\n case \"hardbreak\":\n case \"softbreak\":\n append(<br key={index} />);\n break;\n case \"hr\":\n append(<hr key={index} />);\n break;\n default:\n append(<Fragment key={index}>{token.content}</Fragment>);\n }\n });\n return stack[0].children;\n}\n\nexport function terminalInsertText(text: string): string | undefined {\n const command = text.replace(/^[\\r\\n]+|[\\r\\n]+$/g, \"\");\n // surrounding line endings are formatting, embedded control characters must never reach the terminal\n if (!command.trim() || /[\\u0000-\\u001f\\u007f-\\u009f\\u2028\\u2029]/u.test(command)) return undefined;\n return command;\n}\n\nfunction visibleTerminalId(): string | undefined {\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 if (!visible) continue;\n const id = element.dataset.testid?.slice(prefix.length);\n if (!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\nfunction useActiveTerminalId(): string | undefined {\n const [terminalId, setTerminalId] = useState<string>();\n useEffect(() => {\n let stop: (() => void) | undefined;\n const update = () => {\n stop?.();\n stop = autorun(() => setTerminalId(visibleTerminalId()));\n };\n update();\n const observer = new MutationObserver(update);\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: [\"class\", \"style\", \"hidden\", \"aria-hidden\", \"data-testid\"],\n });\n return () => {\n observer.disconnect();\n stop?.();\n };\n }, []);\n return terminalId;\n}\n\nfunction CodeBlock({ text, language }: { text: string; language: string }) {\n const [feedback, setFeedback] = useState(\"\");\n const terminalId = useActiveTerminalId();\n const [inserting, setInserting] = useState(false);\n const shell = [\"\", \"bash\", \"sh\", \"shell\", \"zsh\", \"console\", \"terminal\"].includes(language.toLowerCase());\n const command = terminalInsertText(text);\n const insert = async () => {\n const activeTerminal = visibleTerminalId();\n if (!shell || !command || !activeTerminal || inserting) return;\n setInserting(true);\n try {\n await Renderer.Component.terminalStore.sendCommand(command, { enter: false, tabId: activeTerminal });\n setFeedback(\"Inserted into terminal. Review the command before running it.\");\n } catch {\n setFeedback(\"Unable to insert into terminal.\");\n } finally {\n setInserting(false);\n }\n };\n return (\n <div className=\"power-ai-code\">\n <div className=\"power-ai-code-toolbar\">\n <span>{language || \"code\"}</span>\n <div>\n <button\n type=\"button\"\n aria-label=\"Copy code\"\n onClick={() => {\n try {\n clipboard.writeText(text);\n setFeedback(\"Copied\");\n } catch {\n setFeedback(\"Unable to copy code.\");\n }\n }}\n >\n <AiIcon material=\"content_copy\" size={14} /> Copy\n </button>\n {shell && terminalId && (\n <button\n type=\"button\"\n aria-label=\"Insert into terminal\"\n disabled={!command || !terminalId || inserting}\n title={\n !command\n ? \"Only a single line without control characters can be inserted\"\n : !terminalId\n ? \"Focus an open terminal tab to insert\"\n : \"Insert without running\"\n }\n onClick={() => void insert()}\n >\n <AiIcon material=\"terminal\" size={14} /> Insert\n </button>\n )}\n </div>\n </div>\n <pre>\n <code>{text}</code>\n </pre>\n {shell && !command && (\n <div className=\"power-ai-code-hint\">Terminal insertion requires a single line without control characters.</div>\n )}\n <div role=\"status\" className=\"power-ai-code-feedback\">\n {feedback}\n </div>\n </div>\n );\n}\n\nexport function AiMessageContent({ text }: { text: string }) {\n const content = useMemo(() => renderTokens(markdown.parse(text, {})), [text]);\n return <div className=\"power-ai-markdown\">{content}</div>;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ai-message.js","names":[],"sources":["../../src/renderer/ai-message.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { clipboard, shell } from \"electron\";\nimport MarkdownIt, { type Token } from \"markdown-it\";\nimport { autorun } from \"mobx\";\nimport { createElement, Fragment, type ReactNode, useEffect, useMemo, useState } from \"react\";\nimport { AiIcon } from \"./ai-icon\";\n\nconst markdown = new MarkdownIt({ html: false, linkify: true, breaks: true });\nconst markdownTags = new Set([\n \"p\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"h5\",\n \"h6\",\n \"ul\",\n \"ol\",\n \"li\",\n \"blockquote\",\n \"table\",\n \"thead\",\n \"tbody\",\n \"tr\",\n \"th\",\n \"td\",\n \"strong\",\n \"em\",\n \"s\",\n \"a\",\n]);\n\nfunction externalLink(value: string | number | null): string | undefined {\n if (typeof value !== \"string\" || !value) return;\n try {\n const url = new URL(value);\n if ([\"https:\", \"http:\", \"mailto:\"].includes(url.protocol) && !url.username && !url.password) return url.href;\n } catch {}\n return;\n}\n\nfunction renderTokens(tokens: Token[]): ReactNode[] {\n type Container = { token?: Token; key: number; children: ReactNode[] };\n const stack: Container[] = [{ key: 0, children: [] }];\n const append = (node: ReactNode) => stack[stack.length - 1].children.push(node);\n tokens.forEach((token, index) => {\n if (token.nesting === 1) {\n stack.push({ token, key: index, children: [] });\n return;\n }\n if (token.nesting === -1) {\n if (stack.length === 1) return;\n const container = stack.pop()!;\n const opening = container.token!;\n const props: Record<string, unknown> = { key: container.key };\n let tag = markdownTags.has(opening.tag) && !opening.hidden ? opening.tag : Fragment;\n if (tag === \"a\") {\n const href = externalLink(opening.attrGet(\"href\"));\n if (!href) tag = Fragment;\n else {\n props.href = href;\n props.title = opening.attrGet(\"title\") ?? undefined;\n props.rel = \"noopener noreferrer\";\n props.target = \"_blank\";\n const open = (event: React.MouseEvent<HTMLAnchorElement>) => {\n event.preventDefault();\n if (event.type === \"auxclick\" && event.button !== 1) return;\n void shell.openExternal(href).catch(() => {});\n };\n props.onClick = open;\n props.onAuxClick = open;\n }\n }\n if (tag === \"ol\") {\n const start = Number(opening.attrGet(\"start\"));\n if (Number.isSafeInteger(start) && start > 0) props.start = start;\n }\n if (tag === \"th\" || tag === \"td\") {\n const alignment = String(opening.attrGet(\"style\") ?? \"\").match(/^text-align:(left|right|center)$/)?.[1];\n if (alignment) props.style = { textAlign: alignment };\n }\n append(createElement(tag, props, ...container.children));\n return;\n }\n switch (token.type) {\n case \"inline\":\n append(<Fragment key={index}>{renderTokens(token.children ?? [])}</Fragment>);\n break;\n case \"code_inline\":\n append(<code key={index}>{token.content}</code>);\n break;\n case \"fence\":\n case \"code_block\":\n append(<CodeBlock key={index} language={token.info.trim().split(/\\s+/)[0]} text={token.content} />);\n break;\n case \"hardbreak\":\n case \"softbreak\":\n append(<br key={index} />);\n break;\n case \"hr\":\n append(<hr key={index} />);\n break;\n default:\n append(<Fragment key={index}>{token.content}</Fragment>);\n }\n });\n return stack[0].children;\n}\n\nexport function terminalInsertText(text: string): string | undefined {\n const command = text.replace(/^[\\r\\n]+|[\\r\\n]+$/g, \"\");\n // surrounding line endings are formatting, embedded control characters must never reach the terminal\n if (!command.trim() || /[\\u0000-\\u001f\\u007f-\\u009f\\u2028\\u2029]/u.test(command)) return undefined;\n return command;\n}\n\nfunction visibleTerminalId(): string | undefined {\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 if (!visible) continue;\n const id = element.dataset.testid?.slice(prefix.length);\n if (!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\nfunction useActiveTerminalId(): string | undefined {\n const [terminalId, setTerminalId] = useState<string>();\n useEffect(() => {\n let stop: (() => void) | undefined;\n const update = () => {\n stop?.();\n stop = autorun(() => setTerminalId(visibleTerminalId()));\n };\n update();\n const observer = new MutationObserver(update);\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: [\"class\", \"style\", \"hidden\", \"aria-hidden\", \"data-testid\"],\n });\n return () => {\n observer.disconnect();\n stop?.();\n };\n }, []);\n return terminalId;\n}\n\nfunction CodeBlock({ text, language }: { text: string; language: string }) {\n const [feedback, setFeedback] = useState(\"\");\n const terminalId = useActiveTerminalId();\n const [inserting, setInserting] = useState(false);\n const shell = [\"\", \"bash\", \"sh\", \"shell\", \"zsh\", \"console\", \"terminal\"].includes(language.toLowerCase());\n const command = terminalInsertText(text);\n const insert = async () => {\n const activeTerminal = visibleTerminalId();\n if (!shell || !command || !activeTerminal || inserting) return;\n setInserting(true);\n try {\n await Renderer.Component.terminalStore.sendCommand(command, { enter: false, tabId: activeTerminal });\n setFeedback(\"Inserted into terminal. Review the command before running it.\");\n } catch {\n setFeedback(\"Unable to insert into terminal.\");\n } finally {\n setInserting(false);\n }\n };\n return (\n <div className=\"power-ai-code\">\n <div className=\"power-ai-code-toolbar\">\n <span>{language || \"code\"}</span>\n <div>\n <button\n type=\"button\"\n aria-label=\"Copy code\"\n title=\"Copy code to clipboard\"\n onClick={() => {\n try {\n clipboard.writeText(text);\n setFeedback(\"Copied\");\n } catch {\n setFeedback(\"Unable to copy code.\");\n }\n }}\n >\n <AiIcon material=\"content_copy\" size={14} /> Copy\n </button>\n {shell && terminalId && (\n <button\n type=\"button\"\n aria-label=\"Insert into terminal\"\n disabled={!command || !terminalId || inserting}\n title={\n !command\n ? \"Only a single line without control characters can be inserted\"\n : !terminalId\n ? \"Focus an open terminal tab to insert\"\n : \"Insert without running\"\n }\n onClick={() => void insert()}\n >\n <AiIcon material=\"terminal\" size={14} /> Insert\n </button>\n )}\n </div>\n </div>\n <pre>\n <code>{text}</code>\n </pre>\n {shell && !command && (\n <div className=\"power-ai-code-hint\">Terminal insertion requires a single line without control characters.</div>\n )}\n <div role=\"status\" className=\"power-ai-code-feedback\">\n {feedback}\n </div>\n </div>\n );\n}\n\nexport function AiMessageContent({ text }: { text: string }) {\n const content = useMemo(() => renderTokens(markdown.parse(text, {})), [text]);\n return <div className=\"power-ai-markdown\">{content}</div>;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const require_jsx_runtime = require("./_virtual/_global-external_react/jsx-runtime.js");
|
|
2
|
+
const require_claude = require("./assets/claude.js");
|
|
3
|
+
//#region src/renderer/ai-provider-icon.tsx
|
|
4
|
+
function AiProviderIcon({ provider, size = 16 }) {
|
|
5
|
+
if (provider === "codex") return /* @__PURE__ */ require_jsx_runtime.jsxs("svg", {
|
|
6
|
+
className: "power-ai-provider-icon",
|
|
7
|
+
"data-provider": "codex",
|
|
8
|
+
width: size,
|
|
9
|
+
height: size,
|
|
10
|
+
viewBox: "0 0 24 24",
|
|
11
|
+
"aria-hidden": "true",
|
|
12
|
+
fill: "currentColor",
|
|
13
|
+
style: {
|
|
14
|
+
color: "var(--textColorPrimary)",
|
|
15
|
+
flexShrink: 0,
|
|
16
|
+
verticalAlign: "middle"
|
|
17
|
+
},
|
|
18
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsx("title", { children: "Codex" }), /* @__PURE__ */ require_jsx_runtime.jsx("path", {
|
|
19
|
+
fillRule: "evenodd",
|
|
20
|
+
d: "M9.205 8.658v-2.26c0-.19.072-.333.238-.428l4.543-2.616c.619-.357 1.356-.523 2.117-.523 2.854 0 4.662 2.212 4.662 4.566 0 .167 0 .357-.024.547l-4.71-2.759a.797.797 0 00-.856 0l-5.97 3.473zm10.609 8.8V12.06c0-.333-.143-.57-.429-.737l-5.97-3.473 1.95-1.118a.433.433 0 01.476 0l4.543 2.617c1.309.76 2.189 2.378 2.189 3.948 0 1.808-1.07 3.473-2.76 4.163zM7.802 12.703l-1.95-1.142c-.167-.095-.239-.238-.239-.428V5.899c0-2.545 1.95-4.472 4.591-4.472 1 0 1.927.333 2.712.928L8.23 5.067c-.285.166-.428.404-.428.737v6.898zM12 15.128l-2.795-1.57v-3.33L12 8.658l2.795 1.57v3.33L12 15.128zm1.796 7.23c-1 0-1.927-.332-2.712-.927l4.686-2.712c.285-.166.428-.404.428-.737v-6.898l1.974 1.142c.167.095.238.238.238.428v5.233c0 2.545-1.974 4.472-4.614 4.472zm-5.637-5.303l-4.544-2.617c-1.308-.761-2.188-2.378-2.188-3.948A4.482 4.482 0 014.21 6.327v5.423c0 .333.143.571.428.738l5.947 3.449-1.95 1.118a.432.432 0 01-.476 0zm-.262 3.9c-2.688 0-4.662-2.021-4.662-4.519 0-.19.024-.38.047-.57l4.686 2.71c.286.167.571.167.856 0l5.97-3.448v2.26c0 .19-.07.333-.237.428l-4.543 2.616c-.619.357-1.356.523-2.117.523zm5.899 2.83a5.947 5.947 0 005.827-4.756C22.287 18.339 24 15.84 24 13.296c0-1.665-.713-3.282-1.998-4.448.119-.5.19-.999.19-1.498 0-3.401-2.759-5.947-5.946-5.947-.642 0-1.26.095-1.88.31A5.962 5.962 0 0010.205 0a5.947 5.947 0 00-5.827 4.757C1.713 5.447 0 7.945 0 10.49c0 1.666.713 3.283 1.998 4.448-.119.5-.19 1-.19 1.499 0 3.401 2.759 5.946 5.946 5.946.642 0 1.26-.095 1.88-.309a5.96 5.96 0 004.162 1.713z"
|
|
21
|
+
})]
|
|
22
|
+
});
|
|
23
|
+
return /* @__PURE__ */ require_jsx_runtime.jsx("img", {
|
|
24
|
+
className: "power-ai-provider-icon",
|
|
25
|
+
"data-provider": provider,
|
|
26
|
+
src: require_claude.default,
|
|
27
|
+
width: size,
|
|
28
|
+
height: size,
|
|
29
|
+
title: provider === "claude" ? "Claude" : "Codex",
|
|
30
|
+
alt: "",
|
|
31
|
+
"aria-hidden": "true",
|
|
32
|
+
draggable: false,
|
|
33
|
+
style: {
|
|
34
|
+
display: "inline-block",
|
|
35
|
+
flexShrink: 0,
|
|
36
|
+
objectFit: "contain",
|
|
37
|
+
verticalAlign: "middle"
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
exports.AiProviderIcon = AiProviderIcon;
|
|
43
|
+
|
|
44
|
+
//# sourceMappingURL=ai-provider-icon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-provider-icon.js","names":[],"sources":["../../src/renderer/ai-provider-icon.tsx"],"sourcesContent":["import claudeIcon from \"./assets/claude.svg?inline\";\n\nexport function AiProviderIcon({ provider, size = 16 }: { provider: \"codex\" | \"claude\"; size?: number }) {\n if (provider === \"codex\")\n return (\n <svg\n className=\"power-ai-provider-icon\"\n data-provider=\"codex\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n fill=\"currentColor\"\n style={{ color: \"var(--textColorPrimary)\", flexShrink: 0, verticalAlign: \"middle\" }}\n >\n <title>Codex</title>\n <path\n fillRule=\"evenodd\"\n d=\"M9.205 8.658v-2.26c0-.19.072-.333.238-.428l4.543-2.616c.619-.357 1.356-.523 2.117-.523 2.854 0 4.662 2.212 4.662 4.566 0 .167 0 .357-.024.547l-4.71-2.759a.797.797 0 00-.856 0l-5.97 3.473zm10.609 8.8V12.06c0-.333-.143-.57-.429-.737l-5.97-3.473 1.95-1.118a.433.433 0 01.476 0l4.543 2.617c1.309.76 2.189 2.378 2.189 3.948 0 1.808-1.07 3.473-2.76 4.163zM7.802 12.703l-1.95-1.142c-.167-.095-.239-.238-.239-.428V5.899c0-2.545 1.95-4.472 4.591-4.472 1 0 1.927.333 2.712.928L8.23 5.067c-.285.166-.428.404-.428.737v6.898zM12 15.128l-2.795-1.57v-3.33L12 8.658l2.795 1.57v3.33L12 15.128zm1.796 7.23c-1 0-1.927-.332-2.712-.927l4.686-2.712c.285-.166.428-.404.428-.737v-6.898l1.974 1.142c.167.095.238.238.238.428v5.233c0 2.545-1.974 4.472-4.614 4.472zm-5.637-5.303l-4.544-2.617c-1.308-.761-2.188-2.378-2.188-3.948A4.482 4.482 0 014.21 6.327v5.423c0 .333.143.571.428.738l5.947 3.449-1.95 1.118a.432.432 0 01-.476 0zm-.262 3.9c-2.688 0-4.662-2.021-4.662-4.519 0-.19.024-.38.047-.57l4.686 2.71c.286.167.571.167.856 0l5.97-3.448v2.26c0 .19-.07.333-.237.428l-4.543 2.616c-.619.357-1.356.523-2.117.523zm5.899 2.83a5.947 5.947 0 005.827-4.756C22.287 18.339 24 15.84 24 13.296c0-1.665-.713-3.282-1.998-4.448.119-.5.19-.999.19-1.498 0-3.401-2.759-5.947-5.946-5.947-.642 0-1.26.095-1.88.31A5.962 5.962 0 0010.205 0a5.947 5.947 0 00-5.827 4.757C1.713 5.447 0 7.945 0 10.49c0 1.666.713 3.283 1.998 4.448-.119.5-.19 1-.19 1.499 0 3.401 2.759 5.946 5.946 5.946.642 0 1.26-.095 1.88-.309a5.96 5.96 0 004.162 1.713z\"\n />\n </svg>\n );\n return (\n <img\n className=\"power-ai-provider-icon\"\n data-provider={provider}\n src={claudeIcon}\n width={size}\n height={size}\n title={provider === \"claude\" ? \"Claude\" : \"Codex\"}\n alt=\"\"\n aria-hidden=\"true\"\n draggable={false}\n style={{ display: \"inline-block\", flexShrink: 0, objectFit: \"contain\", verticalAlign: \"middle\" }}\n />\n );\n}\n"],"mappings":";;;AAEA,SAAgB,eAAe,EAAE,UAAU,OAAO,MAAuD;CACvG,IAAI,aAAa,SACf,OACE,oCAAA,KAAC,OAAD;EACE,WAAU;EACV,iBAAc;EACd,OAAO;EACP,QAAQ;EACR,SAAQ;EACR,eAAY;EACZ,MAAK;EACL,OAAO;GAAE,OAAO;GAA2B,YAAY;GAAG,eAAe;EAAS;YARpF,CAUE,oCAAA,IAAC,SAAD,EAAA,UAAO,QAAY,CAAA,GACnB,oCAAA,IAAC,QAAD;GACE,UAAS;GACT,GAAE;EACH,CAAA,CACE;;CAET,OACE,oCAAA,IAAC,OAAD;EACE,WAAU;EACV,iBAAe;EACf,KAAK,eAAA;EACL,OAAO;EACP,QAAQ;EACR,OAAO,aAAa,WAAW,WAAW;EAC1C,KAAI;EACJ,eAAY;EACZ,WAAW;EACX,OAAO;GAAE,SAAS;GAAgB,YAAY;GAAG,WAAW;GAAW,eAAe;EAAS;CAChG,CAAA;AAEL"}
|
|
@@ -22,7 +22,7 @@ var AiStarterPrompts = require__global_external_mobx_react.observer(({ onSelect,
|
|
|
22
22
|
minHeight: 32
|
|
23
23
|
},
|
|
24
24
|
disabled,
|
|
25
|
-
title:
|
|
25
|
+
title: `Insert /${shortcut.command}: ${shortcut.label}`,
|
|
26
26
|
onClick: () => onSelect(shortcut.prompt),
|
|
27
27
|
children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, { material: {
|
|
28
28
|
health: "favorite_border",
|
|
@@ -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={
|
|
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"}
|
package/out/renderer/ai.js
CHANGED
|
@@ -13,6 +13,7 @@ const require_ai_icon = require("./ai-icon.js");
|
|
|
13
13
|
const require_ai_copy_answer = require("./ai-copy-answer.js");
|
|
14
14
|
const require_ai_extras$1 = require("./ai-extras.js");
|
|
15
15
|
const require_ai_message = require("./ai-message.js");
|
|
16
|
+
const require_ai_provider_icon = require("./ai-provider-icon.js");
|
|
16
17
|
const require_ai_resize = require("./ai-resize.js");
|
|
17
18
|
const require_ai_shortcuts$1 = require("./ai-shortcuts.js");
|
|
18
19
|
let electron = require("electron");
|
|
@@ -89,6 +90,7 @@ var styles = `
|
|
|
89
90
|
@keyframes power-ai-enter { from { transform: translateX(100%); } to { transform: translateX(0); } }
|
|
90
91
|
:root .power-ai-launcher { position: fixed; bottom: 16px; left: auto; right: 16px; z-index: 100; display: inline-flex; align-items: center; gap: 6px; min-width: 0; min-height: 0; height: 34px; border: 1px solid var(--primary, #5896ff); border-radius: 24px; padding: 0 13px; background: var(--contentColor, #242424); color: var(--textColorAccent); box-shadow: 0 3px 12px #0003; font: inherit; font-size: 12px; font-weight: 500; line-height: 1; cursor: pointer; transition: opacity 180ms ease, background-color 140ms ease, box-shadow 140ms ease, transform 180ms ease; }
|
|
91
92
|
.power-ai-overview-actions { position: relative; z-index: 5; height: 0; min-height: 0; flex: 0 0 0; pointer-events: none; }
|
|
93
|
+
.power-ai-overview-actions + [data-testid="cluster-overview-page"] [data-monitoring-summaries] { --power-monitoring-summary-top: 56px; }
|
|
92
94
|
.power-ai-overview-actions button { transition: background-color 160ms ease, border-color 160ms ease, color 160ms ease, box-shadow 160ms ease; position: absolute; top: 12px; right: 12px; pointer-events: auto; box-shadow: 0 2px 8px #0002; display: inline-flex; flex: 0 0 auto; width: auto; align-items: center; gap: 6px; min-height: 32px; padding: 6px 12px; font: inherit; font-size: 12px; border: 1px solid var(--borderFaintColor); border-radius: 10px; color: var(--textColorPrimary); background: var(--contentColor); cursor: pointer; }
|
|
93
95
|
.power-ai-overview-actions button:hover { border-color: var(--primary); color: var(--textColorAccent); background: var(--contentColor); }
|
|
94
96
|
.power-ai-overview-actions button:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
|
|
@@ -112,6 +114,10 @@ var styles = `
|
|
|
112
114
|
:root .power-ai-panel button.power-ai-copy-answer { width: 24px; height: 24px; padding: 0; margin-left: auto; flex-shrink: 0; justify-content: center; border: 0; background: transparent; color: var(--textColorSecondary); }
|
|
113
115
|
:root .power-ai-panel button.power-ai-copy-answer:not(:disabled):not(.disabled):hover { color: var(--primary); background: color-mix(in srgb, var(--primary) 10%, transparent); }
|
|
114
116
|
:root .power-ai-panel button.power-ai-copy-answer:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
|
|
117
|
+
.power-ai-cancellation { display: flex; align-items: center; gap: 6px; }
|
|
118
|
+
:root .power-ai-panel button.power-ai-dismiss { width: 24px; height: 24px; padding: 0; justify-content: center; border: 0; border-radius: 6px; background: transparent; color: var(--textColorSecondary); }
|
|
119
|
+
:root .power-ai-panel button.power-ai-dismiss:hover { color: var(--primary); background: color-mix(in srgb, var(--primary) 10%, transparent); }
|
|
120
|
+
:root .power-ai-panel button.power-ai-dismiss:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
|
|
115
121
|
.power-ai-panel button:disabled { opacity: 0.5; cursor: default; }
|
|
116
122
|
.power-ai-panel textarea { display: block; width: 100%; box-sizing: border-box; min-width: 0; min-height: 64px; max-height: 180px; field-sizing: content; padding: 12px 16px 6px; border: 0; border-radius: 12px; background: transparent; color: var(--textColorAccent); font: inherit; font-size: var(--power-ai-font-size, 14px); line-height: 1.5; resize: none; }
|
|
117
123
|
.power-ai-panel textarea::placeholder { color: var(--textColorSecondary); opacity: 1; }
|
|
@@ -503,6 +509,7 @@ var AiChat = require__global_external_mobx_react.observer(({ isClusterDocument }
|
|
|
503
509
|
children: [
|
|
504
510
|
/* @__PURE__ */ require_jsx_runtime.jsxs("span", {
|
|
505
511
|
className: "power-ai-brand",
|
|
512
|
+
title: "AI assistant",
|
|
506
513
|
children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
|
|
507
514
|
material: "auto_awesome",
|
|
508
515
|
size: 18
|
|
@@ -516,6 +523,7 @@ var AiChat = require__global_external_mobx_react.observer(({ isClusterDocument }
|
|
|
516
523
|
type: "button",
|
|
517
524
|
disabled: busy,
|
|
518
525
|
"aria-label": "New chat",
|
|
526
|
+
title: "Clear this conversation and start a new chat",
|
|
519
527
|
onClick: () => {
|
|
520
528
|
preferences.setMessages(key, []);
|
|
521
529
|
setStopped((keys) => keys.filter((entry) => entry !== key));
|
|
@@ -552,6 +560,7 @@ var AiChat = require__global_external_mobx_react.observer(({ isClusterDocument }
|
|
|
552
560
|
},
|
|
553
561
|
className: "power-ai-close",
|
|
554
562
|
"aria-label": "Close AI assistant",
|
|
563
|
+
title: "Close AI assistant",
|
|
555
564
|
children: /* @__PURE__ */ require_jsx_runtime.jsx("span", {
|
|
556
565
|
"aria-hidden": "true",
|
|
557
566
|
children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
|
|
@@ -597,17 +606,22 @@ var AiChat = require__global_external_mobx_react.observer(({ isClusterDocument }
|
|
|
597
606
|
className: `power-ai-message power-ai-message-${message.role}`,
|
|
598
607
|
children: [
|
|
599
608
|
/* @__PURE__ */ require_jsx_runtime.jsxs("strong", { children: [
|
|
600
|
-
/* @__PURE__ */ require_jsx_runtime.jsx(
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
609
|
+
message.role === "user" ? /* @__PURE__ */ require_jsx_runtime.jsx("span", {
|
|
610
|
+
title: "You",
|
|
611
|
+
children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
|
|
612
|
+
material: "person_outline",
|
|
613
|
+
size: 16
|
|
614
|
+
})
|
|
615
|
+
}) : /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_provider_icon.AiProviderIcon, { provider: message.provider === "claude" ? "claude" : "codex" }),
|
|
604
616
|
message.role === "user" ? "You" : message.provider === "claude" ? "Claude" : "Codex",
|
|
605
617
|
message.role === "assistant" && /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_copy_answer.AiCopyAnswer, { text: message.text })
|
|
606
618
|
] }),
|
|
607
619
|
/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_message.AiMessageContent, { text: message.text }),
|
|
608
620
|
message.role === "assistant" && context.clusterId && /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_extras$1.AiResourceReferences, {
|
|
609
621
|
clusterId: context.clusterId,
|
|
610
|
-
references: message.references
|
|
622
|
+
references: message.references,
|
|
623
|
+
text: message.text,
|
|
624
|
+
onOpened: () => setOpen(false)
|
|
611
625
|
})
|
|
612
626
|
]
|
|
613
627
|
}, index)),
|
|
@@ -619,6 +633,7 @@ var AiChat = require__global_external_mobx_react.observer(({ isClusterDocument }
|
|
|
619
633
|
margin: "8px 16px"
|
|
620
634
|
},
|
|
621
635
|
children: readActivity.map((read, index) => /* @__PURE__ */ require_jsx_runtime.jsxs("span", {
|
|
636
|
+
title: `${read.method} · ${read.status === "reading" ? "reading" : read.status === "ok" ? "read" : "unavailable"}`,
|
|
622
637
|
"data-method": read.method,
|
|
623
638
|
"data-status": read.status,
|
|
624
639
|
style: { marginRight: 10 },
|
|
@@ -649,6 +664,7 @@ var AiChat = require__global_external_mobx_react.observer(({ isClusterDocument }
|
|
|
649
664
|
})]
|
|
650
665
|
}),
|
|
651
666
|
!busy && stopped.includes(key) && /* @__PURE__ */ require_jsx_runtime.jsxs("p", {
|
|
667
|
+
className: "power-ai-cancellation",
|
|
652
668
|
role: "status",
|
|
653
669
|
children: [
|
|
654
670
|
"Response stopped",
|
|
@@ -656,8 +672,13 @@ var AiChat = require__global_external_mobx_react.observer(({ isClusterDocument }
|
|
|
656
672
|
/* @__PURE__ */ require_jsx_runtime.jsx("button", {
|
|
657
673
|
type: "button",
|
|
658
674
|
"aria-label": "Dismiss cancellation",
|
|
675
|
+
className: "power-ai-dismiss",
|
|
676
|
+
title: "Dismiss",
|
|
659
677
|
onClick: () => setStopped((keys) => keys.filter((entry) => entry !== key)),
|
|
660
|
-
children:
|
|
678
|
+
children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
|
|
679
|
+
material: "close",
|
|
680
|
+
size: 14
|
|
681
|
+
})
|
|
661
682
|
})
|
|
662
683
|
]
|
|
663
684
|
}),
|
|
@@ -683,6 +704,7 @@ var AiChat = require__global_external_mobx_react.observer(({ isClusterDocument }
|
|
|
683
704
|
role: "option",
|
|
684
705
|
id: `power-ai-command-${index}`,
|
|
685
706
|
"aria-selected": index === suggestionIndex % suggestions.length,
|
|
707
|
+
title: `Insert /${shortcut.command}: ${shortcut.label}`,
|
|
686
708
|
onMouseDown: (event) => event.preventDefault(),
|
|
687
709
|
onClick: () => selectSuggestion(index),
|
|
688
710
|
children: [/* @__PURE__ */ require_jsx_runtime.jsxs("span", { children: ["/", shortcut.command] }), /* @__PURE__ */ require_jsx_runtime.jsx("span", { children: shortcut.label })]
|
|
@@ -729,7 +751,6 @@ var AiChat = require__global_external_mobx_react.observer(({ isClusterDocument }
|
|
|
729
751
|
children: [/* @__PURE__ */ require_jsx_runtime.jsx("span", { children: "Enter to send · Shift+Enter for newline" }), busy ? /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
|
|
730
752
|
type: "button",
|
|
731
753
|
"aria-label": "Stop response",
|
|
732
|
-
title: "Stop response",
|
|
733
754
|
disabled: stopped.includes(key),
|
|
734
755
|
onClick: stopResponse,
|
|
735
756
|
children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
|
|
@@ -740,7 +761,6 @@ var AiChat = require__global_external_mobx_react.observer(({ isClusterDocument }
|
|
|
740
761
|
type: "submit",
|
|
741
762
|
disabled: !draft.trim(),
|
|
742
763
|
"aria-label": "Send",
|
|
743
|
-
title: "Send",
|
|
744
764
|
children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
|
|
745
765
|
material: "send",
|
|
746
766
|
size: 16
|
|
@@ -760,9 +780,21 @@ function startAiChat(isClusterDocument = isAiClusterDocument) {
|
|
|
760
780
|
let mounted = false;
|
|
761
781
|
let overviewActions;
|
|
762
782
|
let overviewIcon;
|
|
783
|
+
let overviewLabel;
|
|
784
|
+
let overviewResize;
|
|
785
|
+
const updateOverviewLabel = () => {
|
|
786
|
+
const overview = overviewActions?.nextElementSibling;
|
|
787
|
+
const compact = Boolean(overview?.querySelector("[data-freelens-power-monitoring]") && overview.clientWidth > 960);
|
|
788
|
+
const label = compact ? "Ask AI" : "Ask about cluster";
|
|
789
|
+
if (overviewLabel && overviewLabel.textContent !== label) overviewLabel.textContent = label;
|
|
790
|
+
overviewActions?.querySelector("button")?.setAttribute("aria-label", compact ? "Ask AI about cluster" : label);
|
|
791
|
+
};
|
|
763
792
|
const removeOverview = () => {
|
|
793
|
+
overviewResize?.disconnect();
|
|
794
|
+
overviewResize = void 0;
|
|
764
795
|
if (overviewIcon) require__global_external_react_dom.default.unmountComponentAtNode(overviewIcon);
|
|
765
796
|
overviewIcon = void 0;
|
|
797
|
+
overviewLabel = void 0;
|
|
766
798
|
overviewActions?.remove();
|
|
767
799
|
overviewActions = void 0;
|
|
768
800
|
};
|
|
@@ -773,18 +805,29 @@ function startAiChat(isClusterDocument = isAiClusterDocument) {
|
|
|
773
805
|
removeOverview();
|
|
774
806
|
return;
|
|
775
807
|
}
|
|
776
|
-
if (overviewActions?.isConnected && overviewActions.nextElementSibling === overview)
|
|
808
|
+
if (overviewActions?.isConnected && overviewActions.nextElementSibling === overview) {
|
|
809
|
+
updateOverviewLabel();
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
777
812
|
removeOverview();
|
|
778
813
|
overviewActions = document.createElement("div");
|
|
779
814
|
overviewActions.className = "power-ai-overview-actions";
|
|
780
815
|
const overviewButton = document.createElement("button");
|
|
781
816
|
overviewButton.type = "button";
|
|
817
|
+
overviewButton.setAttribute("aria-label", "Ask about cluster");
|
|
818
|
+
overviewButton.title = "Open the AI assistant to ask about this cluster";
|
|
782
819
|
overviewIcon = document.createElement("span");
|
|
783
820
|
overviewIcon.setAttribute("aria-hidden", "true");
|
|
784
|
-
|
|
821
|
+
overviewLabel = document.createElement("span");
|
|
822
|
+
overviewButton.append(overviewIcon, overviewLabel);
|
|
785
823
|
overviewButton.onclick = () => window.dispatchEvent(new Event("power-ai-open"));
|
|
786
824
|
overviewActions.append(overviewButton);
|
|
787
825
|
overview.before(overviewActions);
|
|
826
|
+
updateOverviewLabel();
|
|
827
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
828
|
+
overviewResize = new ResizeObserver(updateOverviewLabel);
|
|
829
|
+
overviewResize.observe(overview);
|
|
830
|
+
}
|
|
788
831
|
require__global_external_react_dom.default.render(/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
|
|
789
832
|
material: "auto_awesome",
|
|
790
833
|
size: 16
|