@64mb/freelens-power 2.2.1 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/THIRD_PARTY_LICENSES +26 -0
- package/out/main/ai-models.js +2 -1
- package/out/main/ai-models.js.map +1 -1
- package/out/main/ai.js +13 -5
- package/out/main/ai.js.map +1 -1
- package/out/main/power-logs-query.js +4 -3
- package/out/main/power-logs-query.js.map +1 -1
- package/out/renderer/ai-extras.js +21 -16
- package/out/renderer/ai-extras.js.map +1 -1
- package/out/renderer/ai-icon.js +23 -0
- package/out/renderer/ai-icon.js.map +1 -1
- package/out/renderer/ai-logs-sql.js +1 -0
- package/out/renderer/ai-logs-sql.js.map +1 -1
- package/out/renderer/ai-message.js +8 -2
- package/out/renderer/ai-message.js.map +1 -1
- package/out/renderer/ai.js +91 -13
- package/out/renderer/ai.js.map +1 -1
- package/out/renderer/appearance.js +20 -2
- package/out/renderer/appearance.js.map +1 -1
- package/out/renderer/details/resource-health-details.js +2 -2
- package/out/renderer/details/resource-health-details.js.map +1 -1
- package/out/renderer/host-layout.js +15 -0
- package/out/renderer/host-layout.js.map +1 -0
- package/out/renderer/index.js +2 -0
- package/out/renderer/index.js.map +1 -1
- package/out/renderer/power-log-json-cell.js +2 -2
- package/out/renderer/power-logs.css.js +11 -2
- package/out/renderer/power-logs.css.js.map +1 -1
- package/out/renderer/power-logs.js +76 -33
- package/out/renderer/power-logs.js.map +1 -1
- package/out/renderer/preferences/ai-preferences.js +6 -3
- package/out/renderer/preferences/ai-preferences.js.map +1 -1
- package/out/renderer/preferences/monitoring-preferences.js +14 -6
- package/out/renderer/preferences/monitoring-preferences.js.map +1 -1
- package/package.json +1 -1
|
@@ -6,6 +6,7 @@ const require_ai_icon = require("./ai-icon.js");
|
|
|
6
6
|
const require_native_tooltip = require("./native-tooltip.js");
|
|
7
7
|
const require_ai_logs_sql = require("./ai-logs-sql.js");
|
|
8
8
|
const require_markdown_it = require("./node_modules/.pnpm/markdown-it@15.0.2/node_modules/markdown-it/dist/markdown-it.js");
|
|
9
|
+
const require_power_log_sql_editor = require("./power-log-sql-editor.js");
|
|
9
10
|
let electron = require("electron");
|
|
10
11
|
//#region src/renderer/ai-message.tsx
|
|
11
12
|
var markdown = new require_markdown_it.default({
|
|
@@ -192,7 +193,9 @@ function CodeBlock({ text, language }) {
|
|
|
192
193
|
].includes(language.toLowerCase());
|
|
193
194
|
const command = terminalInsertText(text);
|
|
194
195
|
const logsWorkspace = require_ai_logs_sql.usePowerLogsWorkspace();
|
|
195
|
-
const
|
|
196
|
+
const sql = ["sql", "sqlite"].includes(language.toLowerCase());
|
|
197
|
+
const sqlTokens = require__global_external_react.useMemo(() => sql ? require_power_log_sql_editor.tokenizeSql(text) : void 0, [sql, text]);
|
|
198
|
+
const sqlDraft = sql && logsWorkspace;
|
|
196
199
|
const insert = async () => {
|
|
197
200
|
const activeTerminal = visibleTerminalId();
|
|
198
201
|
if (!shell || !command || !activeTerminal || inserting) return;
|
|
@@ -266,7 +269,10 @@ function CodeBlock({ text, language }) {
|
|
|
266
269
|
})
|
|
267
270
|
] })]
|
|
268
271
|
}),
|
|
269
|
-
/* @__PURE__ */ require_jsx_runtime.jsx("pre", { children: /* @__PURE__ */ require_jsx_runtime.jsx("code", { children:
|
|
272
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("pre", { children: /* @__PURE__ */ require_jsx_runtime.jsx("code", { children: sqlTokens ? sqlTokens.map((token, index) => /* @__PURE__ */ require_jsx_runtime.jsx("span", {
|
|
273
|
+
"data-sql-token": token.kind,
|
|
274
|
+
children: token.text
|
|
275
|
+
}, index)) : text }) }),
|
|
270
276
|
shell && !command && /* @__PURE__ */ require_jsx_runtime.jsx("div", {
|
|
271
277
|
className: "power-ai-code-hint",
|
|
272
278
|
children: "Terminal insertion requires a single line without control characters."
|
|
@@ -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\";\nimport { isPowerLogsWorkspace, usePowerLogsWorkspace } from \"./ai-logs-sql\";\nimport { NativeTooltip } from \"./native-tooltip\";\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.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 const element = createElement(tag, props, ...container.children);\n const title = tag === \"a\" ? opening.attrGet(\"title\") : undefined;\n append(\n title ? (\n <NativeTooltip key={container.key} title={title}>\n {element}\n </NativeTooltip>\n ) : (\n element\n ),\n );\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 logsWorkspace = usePowerLogsWorkspace();\n const sqlDraft = [\"sql\", \"sqlite\"].includes(language.toLowerCase()) && logsWorkspace;\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 <NativeTooltip title=\"Copy code to clipboard\">\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 </NativeTooltip>\n {sqlDraft && (\n <NativeTooltip title=\"Insert into the SQL editor without running\">\n <button\n type=\"button\"\n aria-label=\"Insert into SQL\"\n disabled={!text.trim() || text.length > 65536}\n onClick={() => {\n if (!isPowerLogsWorkspace() || !text.trim() || text.length > 65536) return;\n window.dispatchEvent(new CustomEvent(\"power-logs-sql-draft\", { detail: { sql: text } }));\n window.dispatchEvent(new Event(\"power-ai-close\"));\n }}\n >\n <AiIcon material=\"post_add\" size={14} /> Insert into SQL\n </button>\n </NativeTooltip>\n )}\n {shell && terminalId && (\n <NativeTooltip\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 >\n <button\n type=\"button\"\n aria-label=\"Insert into terminal\"\n disabled={!command || !terminalId || inserting}\n onClick={() => void insert()}\n >\n <AiIcon material=\"terminal\" size={14} /> Insert\n </button>\n </NativeTooltip>\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":";;;;;;;;;;AASA,IAAM,WAAW,IAAI,oBAAA,QAAW;CAAE,MAAM;CAAO,SAAS;CAAM,QAAQ;AAAK,CAAC;AAC5E,IAAM,+BAAe,IAAI,IAAI;CAC3B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,SAAS,aAAa,OAAmD;CACvE,IAAI,OAAO,UAAU,YAAY,CAAC,OAAO;CACzC,IAAI;EACF,MAAM,MAAM,IAAI,IAAI,KAAK;EACzB,IAAI;GAAC;GAAU;GAAS;EAAS,CAAC,CAAC,SAAS,IAAI,QAAQ,KAAK,CAAC,IAAI,YAAY,CAAC,IAAI,UAAU,OAAO,IAAI;CAC1G,QAAQ,CAAC;AAEX;AAEA,SAAS,aAAa,QAA8B;CAElD,MAAM,QAAqB,CAAC;EAAE,KAAK;EAAG,UAAU,CAAC;CAAE,CAAC;CACpD,MAAM,UAAU,SAAoB,MAAM,MAAM,SAAS,EAAE,CAAC,SAAS,KAAK,IAAI;CAC9E,OAAO,SAAS,OAAO,UAAU;EAC/B,IAAI,MAAM,YAAY,GAAG;GACvB,MAAM,KAAK;IAAE;IAAO,KAAK;IAAO,UAAU,CAAC;GAAE,CAAC;GAC9C;EACF;EACA,IAAI,MAAM,YAAY,IAAI;GACxB,IAAI,MAAM,WAAW,GAAG;GACxB,MAAM,YAAY,MAAM,IAAI;GAC5B,MAAM,UAAU,UAAU;GAC1B,MAAM,QAAiC,EAAE,KAAK,UAAU,IAAI;GAC5D,IAAI,MAAM,aAAa,IAAI,QAAQ,GAAG,KAAK,CAAC,QAAQ,SAAS,QAAQ,MAAM,+BAAA;GAC3E,IAAI,QAAQ,KAAK;IACf,MAAM,OAAO,aAAa,QAAQ,QAAQ,MAAM,CAAC;IACjD,IAAI,CAAC,MAAM,MAAM,+BAAA;SACZ;KACH,MAAM,OAAO;KACb,MAAM,MAAM;KACZ,MAAM,SAAS;KACf,MAAM,QAAQ,UAA+C;MAC3D,MAAM,eAAe;MACrB,IAAI,MAAM,SAAS,cAAc,MAAM,WAAW,GAAG;MACrD,SAAK,MAAM,aAAa,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC;KAC9C;KACA,MAAM,UAAU;KAChB,MAAM,aAAa;IACrB;GACF;GACA,IAAI,QAAQ,MAAM;IAChB,MAAM,QAAQ,OAAO,QAAQ,QAAQ,OAAO,CAAC;IAC7C,IAAI,OAAO,cAAc,KAAK,KAAK,QAAQ,GAAG,MAAM,QAAQ;GAC9D;GACA,IAAI,QAAQ,QAAQ,QAAQ,MAAM;IAChC,MAAM,YAAY,OAAO,QAAQ,QAAQ,OAAO,KAAK,EAAE,CAAC,CAAC,MAAM,kCAAkC,CAAC,GAAG;IACrG,IAAI,WAAW,MAAM,QAAQ,EAAE,WAAW,UAAU;GACtD;GACA,MAAM,UAAU,+BAAA,cAAc,KAAK,OAAO,GAAG,UAAU,QAAQ;GAC/D,MAAM,QAAQ,QAAQ,MAAM,QAAQ,QAAQ,OAAO,IAAI,KAAA;GACvD,OACE,QACE,oCAAA,IAAC,uBAAA,eAAD;IAA0C;cACvC;GACY,GAFK,UAAU,GAEf,IAEf,OAEJ;GACA;EACF;EACA,QAAQ,MAAM,MAAd;GACE,KAAK;IACH,OAAO,oCAAA,IAAC,+BAAA,UAAD,EAAA,UAAuB,aAAa,MAAM,YAAY,CAAC,CAAC,EAAY,GAArD,KAAqD,CAAC;IAC5E;GACF,KAAK;IACH,OAAO,oCAAA,IAAC,QAAD,EAAA,UAAmB,MAAM,QAAc,GAA5B,KAA4B,CAAC;IAC/C;GACF,KAAK;GACL,KAAK;IACH,OAAO,oCAAA,IAAC,WAAD;KAAuB,UAAU,MAAM,KAAK,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC;KAAI,MAAM,MAAM;IAAU,GAA1E,KAA0E,CAAC;IAClG;GACF,KAAK;GACL,KAAK;IACH,OAAO,oCAAA,IAAC,MAAD,CAAiB,GAAR,KAAQ,CAAC;IACzB;GACF,KAAK;IACH,OAAO,oCAAA,IAAC,MAAD,CAAiB,GAAR,KAAQ,CAAC;IACzB;GACF,SACE,OAAO,oCAAA,IAAC,+BAAA,UAAD,EAAA,UAAuB,MAAM,QAAkB,GAAhC,KAAgC,CAAC;EAC3D;CACF,CAAC;CACD,OAAO,MAAM,EAAE,CAAC;AAClB;AAEA,SAAgB,mBAAmB,MAAkC;CACnE,MAAM,UAAU,KAAK,QAAQ,sBAAsB,EAAE;CAErD,IAAI,CAAC,QAAQ,KAAK,KAAK,4CAA4C,KAAK,OAAO,GAAG,OAAO,KAAA;CACzF,OAAO;AACT;AAEA,SAAS,oBAAwC;CAE/C,KAAK,MAAM,WAAW,SAAS,iBAA8B,wCAA4B,GAAG;EAC1F,IAAI,UAAU;EACd,KAAK,IAAI,OAA2B,SAAS,MAAM,OAAO,KAAK,eAAe;GAC5E,MAAM,QAAQ,iBAAiB,IAAI;GACnC,IACE,KAAK,UACL,KAAK,aAAa,aAAa,MAAM,UACrC,MAAM,YAAY,UAClB,MAAM,eAAe,YACrB,MAAM,eAAe,YACrB;IACA,UAAU;IACV;GACF;EACF;EACA,IAAI,CAAC,SAAS;EACd,MAAM,KAAK,QAAQ,QAAQ,QAAQ,MAAM,EAAa;EACtD,IAAI,CAAC,MAAM,CAAC,mBAAA,SAAS,UAAU,cAAc,YAAY,EAAE,GAAG;EAC9D,MAAM,MAAM,mBAAA,SAAS,UAAU,cAAc,eAAe,EAAE;EAC9D,IAAI,KAAK,WAAW,IAAI,YAAY,GAAG,OAAO;CAChD;AAEF;AAEA,SAAS,sBAA0C;CACjD,MAAM,CAAC,YAAY,iBAAiB,+BAAA,SAAiB;CACrD,+BAAA,gBAAgB;EACd,IAAI;EACJ,MAAM,eAAe;GACnB,OAAO;GACP,OAAO,8BAAA,cAAc,cAAc,kBAAkB,CAAC,CAAC;EACzD;EACA,OAAO;EACP,MAAM,WAAW,IAAI,iBAAiB,MAAM;EAC5C,SAAS,QAAQ,SAAS,MAAM;GAC9B,WAAW;GACX,SAAS;GACT,YAAY;GACZ,iBAAiB;IAAC;IAAS;IAAS;IAAU;IAAe;GAAa;EAC5E,CAAC;EACD,aAAa;GACX,SAAS,WAAW;GACpB,OAAO;EACT;CACF,GAAG,CAAC,CAAC;CACL,OAAO;AACT;AAEA,SAAS,UAAU,EAAE,MAAM,YAAgD;CACzE,MAAM,CAAC,UAAU,eAAe,+BAAA,SAAS,EAAE;CAC3C,MAAM,aAAa,oBAAoB;CACvC,MAAM,CAAC,WAAW,gBAAgB,+BAAA,SAAS,KAAK;CAChD,MAAM,QAAQ;EAAC;EAAI;EAAQ;EAAM;EAAS;EAAO;EAAW;CAAU,CAAC,CAAC,SAAS,SAAS,YAAY,CAAC;CACvG,MAAM,UAAU,mBAAmB,IAAI;CACvC,MAAM,gBAAgB,oBAAA,sBAAsB;CAC5C,MAAM,WAAW,CAAC,OAAO,QAAQ,CAAC,CAAC,SAAS,SAAS,YAAY,CAAC,KAAK;CACvE,MAAM,SAAS,YAAY;EACzB,MAAM,iBAAiB,kBAAkB;EACzC,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,kBAAkB,WAAW;EACxD,aAAa,IAAI;EACjB,IAAI;GACF,MAAM,mBAAA,SAAS,UAAU,cAAc,YAAY,SAAS;IAAE,OAAO;IAAO,OAAO;GAAe,CAAC;GACnG,YAAY,+DAA+D;EAC7E,QAAQ;GACN,YAAY,iCAAiC;EAC/C,UAAU;GACR,aAAa,KAAK;EACpB;CACF;CACA,OACE,oCAAA,KAAC,OAAD;EAAK,WAAU;YAAf;GACE,oCAAA,KAAC,OAAD;IAAK,WAAU;cAAf,CACE,oCAAA,IAAC,QAAD,EAAA,UAAO,YAAY,OAAa,CAAA,GAChC,oCAAA,KAAC,OAAD,EAAA,UAAA;KACE,oCAAA,IAAC,uBAAA,eAAD;MAAe,OAAM;gBACnB,oCAAA,KAAC,UAAD;OACE,MAAK;OACL,cAAW;OACX,eAAe;QACb,IAAI;SACF,SAAA,UAAU,UAAU,IAAI;SACxB,YAAY,QAAQ;QACtB,QAAQ;SACN,YAAY,sBAAsB;QACpC;OACF;iBAVF,CAYE,oCAAA,IAAC,gBAAA,QAAD;QAAQ,UAAS;QAAe,MAAM;OAAK,CAAA,GAAC,OACtC;;KACK,CAAA;KACd,YACC,oCAAA,IAAC,uBAAA,eAAD;MAAe,OAAM;gBACnB,oCAAA,KAAC,UAAD;OACE,MAAK;OACL,cAAW;OACX,UAAU,CAAC,KAAK,KAAK,KAAK,KAAK,SAAS;OACxC,eAAe;QACb,IAAI,CAAC,oBAAA,qBAAqB,KAAK,CAAC,KAAK,KAAK,KAAK,KAAK,SAAS,OAAO;QACpE,OAAO,cAAc,IAAI,YAAY,wBAAwB,EAAE,QAAQ,EAAE,KAAK,KAAK,EAAE,CAAC,CAAC;QACvF,OAAO,cAAc,IAAI,MAAM,gBAAgB,CAAC;OAClD;iBARF,CAUE,oCAAA,IAAC,gBAAA,QAAD;QAAQ,UAAS;QAAW,MAAM;OAAK,CAAA,GAAC,kBAClC;;KACK,CAAA;KAEhB,SAAS,cACR,oCAAA,IAAC,uBAAA,eAAD;MACE,OACE,CAAC,UACG,kEACA,CAAC,aACC,yCACA;gBAGR,oCAAA,KAAC,UAAD;OACE,MAAK;OACL,cAAW;OACX,UAAU,CAAC,WAAW,CAAC,cAAc;OACrC,eAAe,KAAK,OAAO;iBAJ7B,CAME,oCAAA,IAAC,gBAAA,QAAD;QAAQ,UAAS;QAAW,MAAM;OAAK,CAAA,GAAC,SAClC;;KACK,CAAA;IAEd,EAAA,CAAA,CACF;;GACL,oCAAA,IAAC,OAAD,EAAA,UACE,oCAAA,IAAC,QAAD,EAAA,UAAO,KAAW,CAAA,EACf,CAAA;GACJ,SAAS,CAAC,WACT,oCAAA,IAAC,OAAD;IAAK,WAAU;cAAqB;GAA0E,CAAA;GAEhH,oCAAA,IAAC,OAAD;IAAK,MAAK;IAAS,WAAU;cAC1B;GACE,CAAA;EACF;;AAET;AAEA,SAAgB,iBAAiB,EAAE,QAA0B;CAE3D,OAAO,oCAAA,IAAC,OAAD;EAAK,WAAU;YADN,+BAAA,cAAc,aAAa,SAAS,MAAM,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAChC;CAAa,CAAA;AAC1D"}
|
|
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\";\nimport { isPowerLogsWorkspace, usePowerLogsWorkspace } from \"./ai-logs-sql\";\nimport { NativeTooltip } from \"./native-tooltip\";\nimport { tokenizeSql } from \"./power-log-sql-editor\";\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.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 const element = createElement(tag, props, ...container.children);\n const title = tag === \"a\" ? opening.attrGet(\"title\") : undefined;\n append(\n title ? (\n <NativeTooltip key={container.key} title={title}>\n {element}\n </NativeTooltip>\n ) : (\n element\n ),\n );\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 logsWorkspace = usePowerLogsWorkspace();\n const sql = [\"sql\", \"sqlite\"].includes(language.toLowerCase());\n const sqlTokens = useMemo(() => (sql ? tokenizeSql(text) : undefined), [sql, text]);\n const sqlDraft = sql && logsWorkspace;\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 <NativeTooltip title=\"Copy code to clipboard\">\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 </NativeTooltip>\n {sqlDraft && (\n <NativeTooltip title=\"Insert into the SQL editor without running\">\n <button\n type=\"button\"\n aria-label=\"Insert into SQL\"\n disabled={!text.trim() || text.length > 65536}\n onClick={() => {\n if (!isPowerLogsWorkspace() || !text.trim() || text.length > 65536) return;\n window.dispatchEvent(new CustomEvent(\"power-logs-sql-draft\", { detail: { sql: text } }));\n window.dispatchEvent(new Event(\"power-ai-close\"));\n }}\n >\n <AiIcon material=\"post_add\" size={14} /> Insert into SQL\n </button>\n </NativeTooltip>\n )}\n {shell && terminalId && (\n <NativeTooltip\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 >\n <button\n type=\"button\"\n aria-label=\"Insert into terminal\"\n disabled={!command || !terminalId || inserting}\n onClick={() => void insert()}\n >\n <AiIcon material=\"terminal\" size={14} /> Insert\n </button>\n </NativeTooltip>\n )}\n </div>\n </div>\n <pre>\n <code>\n {sqlTokens\n ? sqlTokens.map((token, index) => (\n <span key={index} data-sql-token={token.kind}>\n {token.text}\n </span>\n ))\n : text}\n </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":";;;;;;;;;;;AAUA,IAAM,WAAW,IAAI,oBAAA,QAAW;CAAE,MAAM;CAAO,SAAS;CAAM,QAAQ;AAAK,CAAC;AAC5E,IAAM,+BAAe,IAAI,IAAI;CAC3B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,SAAS,aAAa,OAAmD;CACvE,IAAI,OAAO,UAAU,YAAY,CAAC,OAAO;CACzC,IAAI;EACF,MAAM,MAAM,IAAI,IAAI,KAAK;EACzB,IAAI;GAAC;GAAU;GAAS;EAAS,CAAC,CAAC,SAAS,IAAI,QAAQ,KAAK,CAAC,IAAI,YAAY,CAAC,IAAI,UAAU,OAAO,IAAI;CAC1G,QAAQ,CAAC;AAEX;AAEA,SAAS,aAAa,QAA8B;CAElD,MAAM,QAAqB,CAAC;EAAE,KAAK;EAAG,UAAU,CAAC;CAAE,CAAC;CACpD,MAAM,UAAU,SAAoB,MAAM,MAAM,SAAS,EAAE,CAAC,SAAS,KAAK,IAAI;CAC9E,OAAO,SAAS,OAAO,UAAU;EAC/B,IAAI,MAAM,YAAY,GAAG;GACvB,MAAM,KAAK;IAAE;IAAO,KAAK;IAAO,UAAU,CAAC;GAAE,CAAC;GAC9C;EACF;EACA,IAAI,MAAM,YAAY,IAAI;GACxB,IAAI,MAAM,WAAW,GAAG;GACxB,MAAM,YAAY,MAAM,IAAI;GAC5B,MAAM,UAAU,UAAU;GAC1B,MAAM,QAAiC,EAAE,KAAK,UAAU,IAAI;GAC5D,IAAI,MAAM,aAAa,IAAI,QAAQ,GAAG,KAAK,CAAC,QAAQ,SAAS,QAAQ,MAAM,+BAAA;GAC3E,IAAI,QAAQ,KAAK;IACf,MAAM,OAAO,aAAa,QAAQ,QAAQ,MAAM,CAAC;IACjD,IAAI,CAAC,MAAM,MAAM,+BAAA;SACZ;KACH,MAAM,OAAO;KACb,MAAM,MAAM;KACZ,MAAM,SAAS;KACf,MAAM,QAAQ,UAA+C;MAC3D,MAAM,eAAe;MACrB,IAAI,MAAM,SAAS,cAAc,MAAM,WAAW,GAAG;MACrD,SAAK,MAAM,aAAa,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC;KAC9C;KACA,MAAM,UAAU;KAChB,MAAM,aAAa;IACrB;GACF;GACA,IAAI,QAAQ,MAAM;IAChB,MAAM,QAAQ,OAAO,QAAQ,QAAQ,OAAO,CAAC;IAC7C,IAAI,OAAO,cAAc,KAAK,KAAK,QAAQ,GAAG,MAAM,QAAQ;GAC9D;GACA,IAAI,QAAQ,QAAQ,QAAQ,MAAM;IAChC,MAAM,YAAY,OAAO,QAAQ,QAAQ,OAAO,KAAK,EAAE,CAAC,CAAC,MAAM,kCAAkC,CAAC,GAAG;IACrG,IAAI,WAAW,MAAM,QAAQ,EAAE,WAAW,UAAU;GACtD;GACA,MAAM,UAAU,+BAAA,cAAc,KAAK,OAAO,GAAG,UAAU,QAAQ;GAC/D,MAAM,QAAQ,QAAQ,MAAM,QAAQ,QAAQ,OAAO,IAAI,KAAA;GACvD,OACE,QACE,oCAAA,IAAC,uBAAA,eAAD;IAA0C;cACvC;GACY,GAFK,UAAU,GAEf,IAEf,OAEJ;GACA;EACF;EACA,QAAQ,MAAM,MAAd;GACE,KAAK;IACH,OAAO,oCAAA,IAAC,+BAAA,UAAD,EAAA,UAAuB,aAAa,MAAM,YAAY,CAAC,CAAC,EAAY,GAArD,KAAqD,CAAC;IAC5E;GACF,KAAK;IACH,OAAO,oCAAA,IAAC,QAAD,EAAA,UAAmB,MAAM,QAAc,GAA5B,KAA4B,CAAC;IAC/C;GACF,KAAK;GACL,KAAK;IACH,OAAO,oCAAA,IAAC,WAAD;KAAuB,UAAU,MAAM,KAAK,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC;KAAI,MAAM,MAAM;IAAU,GAA1E,KAA0E,CAAC;IAClG;GACF,KAAK;GACL,KAAK;IACH,OAAO,oCAAA,IAAC,MAAD,CAAiB,GAAR,KAAQ,CAAC;IACzB;GACF,KAAK;IACH,OAAO,oCAAA,IAAC,MAAD,CAAiB,GAAR,KAAQ,CAAC;IACzB;GACF,SACE,OAAO,oCAAA,IAAC,+BAAA,UAAD,EAAA,UAAuB,MAAM,QAAkB,GAAhC,KAAgC,CAAC;EAC3D;CACF,CAAC;CACD,OAAO,MAAM,EAAE,CAAC;AAClB;AAEA,SAAgB,mBAAmB,MAAkC;CACnE,MAAM,UAAU,KAAK,QAAQ,sBAAsB,EAAE;CAErD,IAAI,CAAC,QAAQ,KAAK,KAAK,4CAA4C,KAAK,OAAO,GAAG,OAAO,KAAA;CACzF,OAAO;AACT;AAEA,SAAS,oBAAwC;CAE/C,KAAK,MAAM,WAAW,SAAS,iBAA8B,wCAA4B,GAAG;EAC1F,IAAI,UAAU;EACd,KAAK,IAAI,OAA2B,SAAS,MAAM,OAAO,KAAK,eAAe;GAC5E,MAAM,QAAQ,iBAAiB,IAAI;GACnC,IACE,KAAK,UACL,KAAK,aAAa,aAAa,MAAM,UACrC,MAAM,YAAY,UAClB,MAAM,eAAe,YACrB,MAAM,eAAe,YACrB;IACA,UAAU;IACV;GACF;EACF;EACA,IAAI,CAAC,SAAS;EACd,MAAM,KAAK,QAAQ,QAAQ,QAAQ,MAAM,EAAa;EACtD,IAAI,CAAC,MAAM,CAAC,mBAAA,SAAS,UAAU,cAAc,YAAY,EAAE,GAAG;EAC9D,MAAM,MAAM,mBAAA,SAAS,UAAU,cAAc,eAAe,EAAE;EAC9D,IAAI,KAAK,WAAW,IAAI,YAAY,GAAG,OAAO;CAChD;AAEF;AAEA,SAAS,sBAA0C;CACjD,MAAM,CAAC,YAAY,iBAAiB,+BAAA,SAAiB;CACrD,+BAAA,gBAAgB;EACd,IAAI;EACJ,MAAM,eAAe;GACnB,OAAO;GACP,OAAO,8BAAA,cAAc,cAAc,kBAAkB,CAAC,CAAC;EACzD;EACA,OAAO;EACP,MAAM,WAAW,IAAI,iBAAiB,MAAM;EAC5C,SAAS,QAAQ,SAAS,MAAM;GAC9B,WAAW;GACX,SAAS;GACT,YAAY;GACZ,iBAAiB;IAAC;IAAS;IAAS;IAAU;IAAe;GAAa;EAC5E,CAAC;EACD,aAAa;GACX,SAAS,WAAW;GACpB,OAAO;EACT;CACF,GAAG,CAAC,CAAC;CACL,OAAO;AACT;AAEA,SAAS,UAAU,EAAE,MAAM,YAAgD;CACzE,MAAM,CAAC,UAAU,eAAe,+BAAA,SAAS,EAAE;CAC3C,MAAM,aAAa,oBAAoB;CACvC,MAAM,CAAC,WAAW,gBAAgB,+BAAA,SAAS,KAAK;CAChD,MAAM,QAAQ;EAAC;EAAI;EAAQ;EAAM;EAAS;EAAO;EAAW;CAAU,CAAC,CAAC,SAAS,SAAS,YAAY,CAAC;CACvG,MAAM,UAAU,mBAAmB,IAAI;CACvC,MAAM,gBAAgB,oBAAA,sBAAsB;CAC5C,MAAM,MAAM,CAAC,OAAO,QAAQ,CAAC,CAAC,SAAS,SAAS,YAAY,CAAC;CAC7D,MAAM,YAAY,+BAAA,cAAe,MAAM,6BAAA,YAAY,IAAI,IAAI,KAAA,GAAY,CAAC,KAAK,IAAI,CAAC;CAClF,MAAM,WAAW,OAAO;CACxB,MAAM,SAAS,YAAY;EACzB,MAAM,iBAAiB,kBAAkB;EACzC,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,kBAAkB,WAAW;EACxD,aAAa,IAAI;EACjB,IAAI;GACF,MAAM,mBAAA,SAAS,UAAU,cAAc,YAAY,SAAS;IAAE,OAAO;IAAO,OAAO;GAAe,CAAC;GACnG,YAAY,+DAA+D;EAC7E,QAAQ;GACN,YAAY,iCAAiC;EAC/C,UAAU;GACR,aAAa,KAAK;EACpB;CACF;CACA,OACE,oCAAA,KAAC,OAAD;EAAK,WAAU;YAAf;GACE,oCAAA,KAAC,OAAD;IAAK,WAAU;cAAf,CACE,oCAAA,IAAC,QAAD,EAAA,UAAO,YAAY,OAAa,CAAA,GAChC,oCAAA,KAAC,OAAD,EAAA,UAAA;KACE,oCAAA,IAAC,uBAAA,eAAD;MAAe,OAAM;gBACnB,oCAAA,KAAC,UAAD;OACE,MAAK;OACL,cAAW;OACX,eAAe;QACb,IAAI;SACF,SAAA,UAAU,UAAU,IAAI;SACxB,YAAY,QAAQ;QACtB,QAAQ;SACN,YAAY,sBAAsB;QACpC;OACF;iBAVF,CAYE,oCAAA,IAAC,gBAAA,QAAD;QAAQ,UAAS;QAAe,MAAM;OAAK,CAAA,GAAC,OACtC;;KACK,CAAA;KACd,YACC,oCAAA,IAAC,uBAAA,eAAD;MAAe,OAAM;gBACnB,oCAAA,KAAC,UAAD;OACE,MAAK;OACL,cAAW;OACX,UAAU,CAAC,KAAK,KAAK,KAAK,KAAK,SAAS;OACxC,eAAe;QACb,IAAI,CAAC,oBAAA,qBAAqB,KAAK,CAAC,KAAK,KAAK,KAAK,KAAK,SAAS,OAAO;QACpE,OAAO,cAAc,IAAI,YAAY,wBAAwB,EAAE,QAAQ,EAAE,KAAK,KAAK,EAAE,CAAC,CAAC;QACvF,OAAO,cAAc,IAAI,MAAM,gBAAgB,CAAC;OAClD;iBARF,CAUE,oCAAA,IAAC,gBAAA,QAAD;QAAQ,UAAS;QAAW,MAAM;OAAK,CAAA,GAAC,kBAClC;;KACK,CAAA;KAEhB,SAAS,cACR,oCAAA,IAAC,uBAAA,eAAD;MACE,OACE,CAAC,UACG,kEACA,CAAC,aACC,yCACA;gBAGR,oCAAA,KAAC,UAAD;OACE,MAAK;OACL,cAAW;OACX,UAAU,CAAC,WAAW,CAAC,cAAc;OACrC,eAAe,KAAK,OAAO;iBAJ7B,CAME,oCAAA,IAAC,gBAAA,QAAD;QAAQ,UAAS;QAAW,MAAM;OAAK,CAAA,GAAC,SAClC;;KACK,CAAA;IAEd,EAAA,CAAA,CACF;;GACL,oCAAA,IAAC,OAAD,EAAA,UACE,oCAAA,IAAC,QAAD,EAAA,UACG,YACG,UAAU,KAAK,OAAO,UACpB,oCAAA,IAAC,QAAD;IAAkB,kBAAgB,MAAM;cACrC,MAAM;GACH,GAFK,KAEL,CACP,IACD,KACA,CAAA,EACH,CAAA;GACJ,SAAS,CAAC,WACT,oCAAA,IAAC,OAAD;IAAK,WAAU;cAAqB;GAA0E,CAAA;GAEhH,oCAAA,IAAC,OAAD;IAAK,MAAK;IAAS,WAAU;cAC1B;GACE,CAAA;EACF;;AAET;AAEA,SAAgB,iBAAiB,EAAE,QAA0B;CAE3D,OAAO,oCAAA,IAAC,OAAD;EAAK,WAAU;YADN,+BAAA,cAAc,aAAa,SAAS,MAAM,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAChC;CAAa,CAAA;AAC1D"}
|
package/out/renderer/ai.js
CHANGED
|
@@ -137,6 +137,7 @@ body:has(.AddRemoveButtons .remove-button:not([data-freelens-power-read-only]))
|
|
|
137
137
|
:root .power-ai-panel form button[type="submit"]:disabled { background-color: var(--contentColor); border-color: var(--borderFaintColor); color: var(--textColorSecondary); }
|
|
138
138
|
.power-ai-message-user { border-color: color-mix(in srgb, var(--primary) 28%, var(--borderFaintColor)); background: color-mix(in srgb, var(--primary) 7%, var(--mainBackground)); }
|
|
139
139
|
.power-ai-context { padding: 10px 20px; line-height: 1.5; border-bottom: 1px solid var(--borderFaintColor); font-size: 12px; color: var(--textColorSecondary); overflow-wrap: anywhere; }
|
|
140
|
+
.power-ai-attached-resource { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
|
|
140
141
|
.power-ai-title, .power-ai-panel button { display: inline-flex; align-items: center; gap: 6px; }
|
|
141
142
|
.power-ai-title { margin-right: auto; white-space: nowrap; color: var(--textColorAccent); }
|
|
142
143
|
.power-ai-brand { display: inline-flex; align-items: center; gap: 6px; color: var(--primary); }
|
|
@@ -145,6 +146,15 @@ body:has(.AddRemoveButtons .remove-button:not([data-freelens-power-read-only]))
|
|
|
145
146
|
.power-ai-panel header > button { flex-shrink: 0; }
|
|
146
147
|
.power-ai-panel form { position: relative; flex-shrink: 0; margin: 0 16px 16px; border: 1px solid var(--borderFaintColor); border-radius: 12px; background: var(--mainBackground); transition: border-color 160ms ease; }
|
|
147
148
|
.power-ai-panel form > .power-ai-context-preview { padding: 8px 48px 4px 16px !important; min-height: 34px; margin: 0; box-sizing: border-box; }
|
|
149
|
+
.power-ai-context-preview pre { white-space: pre-wrap; overflow-wrap: anywhere; overflow: auto; }
|
|
150
|
+
.power-ai-finding-content pre { max-height: 140px; }
|
|
151
|
+
.power-ai-context-content pre { max-height: 160px; }
|
|
152
|
+
.power-ai-context-preview .power-log-json { --json-key:#9cdcfe; --json-string:#ce9178; --json-number:#b5cea8; --json-literal:#c586c0; }
|
|
153
|
+
.theme-light .power-ai-context-preview .power-log-json { --json-key:#005a9e; --json-string:#9e2b1a; --json-number:#17642d; --json-literal:#7a288c; }
|
|
154
|
+
.power-ai-context-preview .power-log-json [data-token=key] { color:var(--json-key); }
|
|
155
|
+
.power-ai-context-preview .power-log-json [data-token=string] { color:var(--json-string); }
|
|
156
|
+
.power-ai-context-preview .power-log-json [data-token=number] { color:var(--json-number); }
|
|
157
|
+
.power-ai-context-preview .power-log-json [data-token=literal] { color:var(--json-literal); }
|
|
148
158
|
.power-ai-context-finding { display: flex; align-items: flex-start; gap: 8px; margin-bottom: 4px; min-width: 0; }
|
|
149
159
|
.power-ai-context-finding > .Icon { flex-shrink: 0; margin-top: 7px; }
|
|
150
160
|
.power-ai-context-finding > details { flex: 1; min-width: 0; }
|
|
@@ -178,12 +188,19 @@ body:has(.AddRemoveButtons .remove-button:not([data-freelens-power-read-only]))
|
|
|
178
188
|
.power-ai-markdown a { color: var(--primary); text-decoration: underline; text-underline-offset: 2px; }
|
|
179
189
|
.power-ai-markdown hr { margin: 20px 0; border: 0; border-top: 1px solid var(--borderFaintColor); }
|
|
180
190
|
.power-ai-markdown code { font-family: var(--font-monospace, monospace); font-size: max(10px, calc(var(--power-ai-font-size, 14px) - 1px)); background: color-mix(in srgb, var(--textColorAccent) 8%, transparent); padding: 1px 4px; border-radius: 4px; }
|
|
181
|
-
.power-ai-code { margin: 12px 0; border: 1px solid var(--borderFaintColor); border-radius: 8px; overflow: hidden; background: var(--mainBackground); }
|
|
191
|
+
.power-ai-code { --sql-string:#e6a28c; --sql-number:#98c78e; --sql-function:#81b9e6; --sql-identifier:var(--textColorPrimary); margin: 12px 0; border: 1px solid var(--borderFaintColor); border-radius: 8px; overflow: hidden; background: var(--mainBackground); }
|
|
182
192
|
.power-ai-code-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 6px 8px; border-bottom: 1px solid var(--borderFaintColor); color: var(--textColorSecondary); font-size: 11px; }
|
|
183
193
|
.power-ai-code-toolbar > div { display: flex; gap: 4px; }
|
|
184
194
|
.power-ai-code-toolbar button { padding: 4px 6px; font-size: 11px; }
|
|
185
|
-
.power-ai-code pre { margin: 0; padding: 12px; overflow-x: auto; white-space: pre; }
|
|
186
|
-
.power-ai-code pre code { padding: 0; background: transparent; }
|
|
195
|
+
.power-ai-code pre { color:var(--textColorPrimary); margin: 0; padding: 12px; overflow-x: auto; white-space: pre; }
|
|
196
|
+
.power-ai-code pre code { padding: 0; background: transparent; color:inherit; }
|
|
197
|
+
.theme-light .power-ai-code { --sql-string:#9e2b1a; --sql-number:#17642d; --sql-function:#005a9e; --sql-identifier:#684c9b; }
|
|
198
|
+
.power-ai-code [data-sql-token=keyword] { color:var(--primary); }
|
|
199
|
+
.power-ai-code [data-sql-token=function] { color:var(--sql-function); }
|
|
200
|
+
.power-ai-code [data-sql-token=string] { color:var(--sql-string); }
|
|
201
|
+
.power-ai-code [data-sql-token=identifier] { color:var(--sql-identifier); }
|
|
202
|
+
.power-ai-code [data-sql-token=number] { color:var(--sql-number); }
|
|
203
|
+
.power-ai-code [data-sql-token=comment] { color:var(--textColorSecondary); }
|
|
187
204
|
.power-ai-code-hint, .power-ai-code-feedback:not(:empty) { padding: 0 12px 10px; color: var(--textColorSecondary); font-size: 11px; }
|
|
188
205
|
.power-ai-autocomplete { position: absolute; bottom: calc(100% + 8px); left: 0; right: 0; max-height: 240px; overflow-y: auto; padding: 6px; border: 1px solid var(--borderColor); border-radius: 10px; background: var(--contentColor); box-shadow: 0 4px 16px #0003; }
|
|
189
206
|
.power-ai-autocomplete button { display: flex; width: 100%; justify-content: space-between; gap: 12px; border: 0; text-align: left; }
|
|
@@ -202,6 +219,28 @@ var AiChat = require__global_external_mobx_react.observer(({ isClusterDocument }
|
|
|
202
219
|
const [launcherHidden, setLauncherHidden] = require__global_external_react.useState(false);
|
|
203
220
|
const [draft, setDraft] = require__global_external_react.useState("");
|
|
204
221
|
const [finding, setFinding] = require__global_external_react.useState();
|
|
222
|
+
const [resourceAttachment, setResourceAttachment] = require__global_external_react.useState();
|
|
223
|
+
const dismissedResource = require__global_external_react.useRef();
|
|
224
|
+
const panelOpen = require__global_external_react.useRef(open);
|
|
225
|
+
panelOpen.current = open;
|
|
226
|
+
const captureScreenResource = (current = readAiContext()) => {
|
|
227
|
+
if (current.clusterId && current.resource && dismissedResource.current !== JSON.stringify({
|
|
228
|
+
clusterId: current.clusterId,
|
|
229
|
+
resource: current.resource
|
|
230
|
+
})) setResourceAttachment(current);
|
|
231
|
+
};
|
|
232
|
+
const attachScreenResource = () => {
|
|
233
|
+
dismissedResource.current = void 0;
|
|
234
|
+
captureScreenResource();
|
|
235
|
+
};
|
|
236
|
+
const removeResource = () => {
|
|
237
|
+
const current = resourceAttachment ?? readAiContext();
|
|
238
|
+
dismissedResource.current = JSON.stringify({
|
|
239
|
+
clusterId: current.clusterId,
|
|
240
|
+
resource: current.resource
|
|
241
|
+
});
|
|
242
|
+
setResourceAttachment(void 0);
|
|
243
|
+
};
|
|
205
244
|
const [terminalAttachment, setTerminalAttachment] = require__global_external_react.useState();
|
|
206
245
|
const [suggestionIndex, setSuggestionIndex] = require__global_external_react.useState(0);
|
|
207
246
|
const [dismissedDraft, setDismissedDraft] = require__global_external_react.useState();
|
|
@@ -257,6 +296,8 @@ var AiChat = require__global_external_mobx_react.observer(({ isClusterDocument }
|
|
|
257
296
|
updateDock();
|
|
258
297
|
const nextContext = readAiContext();
|
|
259
298
|
setContext(nextContext);
|
|
299
|
+
if (panelOpen.current) captureScreenResource(nextContext);
|
|
300
|
+
setResourceAttachment((current) => current?.clusterId === nextContext.clusterId ? current : void 0);
|
|
260
301
|
setFinding((current) => current && current.clusterId !== nextContext.clusterId ? void 0 : current);
|
|
261
302
|
setResourceDetailsOpen(hasAiResourceDetails());
|
|
262
303
|
setCoveredByOverlay(hasAiOverlay());
|
|
@@ -318,6 +359,7 @@ var AiChat = require__global_external_mobx_react.observer(({ isClusterDocument }
|
|
|
318
359
|
const closePanel = () => setOpen(false);
|
|
319
360
|
window.addEventListener("power-ai-close", closePanel);
|
|
320
361
|
function openOnly() {
|
|
362
|
+
attachScreenResource();
|
|
321
363
|
update();
|
|
322
364
|
setOpen(true);
|
|
323
365
|
}
|
|
@@ -376,6 +418,12 @@ var AiChat = require__global_external_mobx_react.observer(({ isClusterDocument }
|
|
|
376
418
|
}, [open, pinned]);
|
|
377
419
|
const availablePanelWidth = Math.max(1, viewportWidth - Math.max(0, launcherLeft - 16));
|
|
378
420
|
const panelWidth = require_ai_resize.clampAiPanelWidth(preferences.chatPanelWidth ?? 560, availablePanelWidth);
|
|
421
|
+
const attachedResource = resourceAttachment && resourceAttachment.clusterId === context.clusterId ? resourceAttachment.resource : void 0;
|
|
422
|
+
const previewContext = require_ai_extras.attachedAiContext({
|
|
423
|
+
clusterId: context.clusterId,
|
|
424
|
+
screen: context.screen,
|
|
425
|
+
...finding && finding.clusterId === context.clusterId && finding.resource ? { resource: finding.resource } : attachedResource ? { resource: attachedResource } : {}
|
|
426
|
+
});
|
|
379
427
|
const key = context.clusterId ?? "global";
|
|
380
428
|
const messages = preferences.conversations[key] ?? [];
|
|
381
429
|
const busy = pending.includes(key);
|
|
@@ -433,9 +481,12 @@ var AiChat = require__global_external_mobx_react.observer(({ isClusterDocument }
|
|
|
433
481
|
setError("Message and attached finding are too long. Shorten the message or remove the finding.");
|
|
434
482
|
return;
|
|
435
483
|
}
|
|
484
|
+
const currentContext = readAiContext();
|
|
485
|
+
const requestResource = activeFinding?.resource ?? (resourceAttachment && resourceAttachment.clusterId === currentContext.clusterId ? resourceAttachment.resource : void 0);
|
|
436
486
|
const requestContext = require_ai_extras.attachedAiContext({
|
|
437
|
-
|
|
438
|
-
|
|
487
|
+
clusterId: currentContext.clusterId,
|
|
488
|
+
screen: currentContext.screen,
|
|
489
|
+
...requestResource ? { resource: requestResource } : {}
|
|
439
490
|
});
|
|
440
491
|
if ((requestContext.clusterId ?? "global") !== key) {
|
|
441
492
|
setContext(requestContext);
|
|
@@ -555,7 +606,12 @@ var AiChat = require__global_external_mobx_react.observer(({ isClusterDocument }
|
|
|
555
606
|
left: resourceDetailsOpen ? launcherLeft : "auto",
|
|
556
607
|
right: resourceDetailsOpen ? "auto" : 16
|
|
557
608
|
},
|
|
558
|
-
|
|
609
|
+
onPointerDown: attachScreenResource,
|
|
610
|
+
onMouseDown: attachScreenResource,
|
|
611
|
+
onClick: () => {
|
|
612
|
+
attachScreenResource();
|
|
613
|
+
setOpen(true);
|
|
614
|
+
},
|
|
559
615
|
"aria-label": "Open AI assistant",
|
|
560
616
|
children: [
|
|
561
617
|
/* @__PURE__ */ require_jsx_runtime.jsx("span", {
|
|
@@ -573,6 +629,9 @@ var AiChat = require__global_external_mobx_react.observer(({ isClusterDocument }
|
|
|
573
629
|
open && /* @__PURE__ */ require_jsx_runtime.jsxs("aside", {
|
|
574
630
|
className: "power-ai-panel",
|
|
575
631
|
"aria-label": "AI assistant",
|
|
632
|
+
onPointerDownCapture: () => captureScreenResource(),
|
|
633
|
+
onMouseDownCapture: () => captureScreenResource(),
|
|
634
|
+
onFocusCapture: () => captureScreenResource(),
|
|
576
635
|
style: {
|
|
577
636
|
"--power-ai-font-size": `${preferences.chatFontSize ?? 14}px`,
|
|
578
637
|
width: panelWidth
|
|
@@ -610,6 +669,7 @@ var AiChat = require__global_external_mobx_react.observer(({ isClusterDocument }
|
|
|
610
669
|
onClick: () => {
|
|
611
670
|
setTerminalAttachment(void 0);
|
|
612
671
|
setFinding(void 0);
|
|
672
|
+
removeResource();
|
|
613
673
|
preferences.setMessages(key, []);
|
|
614
674
|
setStopped((keys) => keys.filter((entry) => entry !== key));
|
|
615
675
|
setActivity({
|
|
@@ -632,8 +692,8 @@ var AiChat = require__global_external_mobx_react.observer(({ isClusterDocument }
|
|
|
632
692
|
"aria-label": "Pin AI assistant",
|
|
633
693
|
"aria-pressed": pinned,
|
|
634
694
|
onClick: () => setPinned((value) => !value),
|
|
635
|
-
children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.
|
|
636
|
-
|
|
695
|
+
children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiPinIcon, {
|
|
696
|
+
pinned,
|
|
637
697
|
size: 18
|
|
638
698
|
})
|
|
639
699
|
})
|
|
@@ -666,7 +726,28 @@ var AiChat = require__global_external_mobx_react.observer(({ isClusterDocument }
|
|
|
666
726
|
context.clusterId ? "Cluster conversation" : "General conversation",
|
|
667
727
|
" · ",
|
|
668
728
|
context.screen,
|
|
669
|
-
context.resource &&
|
|
729
|
+
attachedResource && !(finding?.clusterId === context.clusterId && finding?.resource) && /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
730
|
+
className: "power-ai-attached-resource",
|
|
731
|
+
"aria-label": "Attached resource",
|
|
732
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsxs("span", { children: [
|
|
733
|
+
attachedResource.kind,
|
|
734
|
+
"/",
|
|
735
|
+
attachedResource.name,
|
|
736
|
+
attachedResource.namespace ? ` · ${attachedResource.namespace}` : ""
|
|
737
|
+
] }), /* @__PURE__ */ require_jsx_runtime.jsx(require_native_tooltip.NativeTooltip, {
|
|
738
|
+
title: "Remove resource from attached context",
|
|
739
|
+
children: /* @__PURE__ */ require_jsx_runtime.jsx("button", {
|
|
740
|
+
type: "button",
|
|
741
|
+
className: "power-ai-remove-context",
|
|
742
|
+
"aria-label": "Remove attached resource",
|
|
743
|
+
onClick: removeResource,
|
|
744
|
+
children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
|
|
745
|
+
material: "close",
|
|
746
|
+
size: 14
|
|
747
|
+
})
|
|
748
|
+
})
|
|
749
|
+
})]
|
|
750
|
+
}),
|
|
670
751
|
/* @__PURE__ */ require_jsx_runtime.jsx("br", {}),
|
|
671
752
|
"Only screen and resource metadata are attached automatically."
|
|
672
753
|
]
|
|
@@ -809,10 +890,7 @@ var AiChat = require__global_external_mobx_react.observer(({ isClusterDocument }
|
|
|
809
890
|
/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_extras$1.AiContextPreview, {
|
|
810
891
|
finding: finding?.clusterId === context.clusterId ? finding : void 0,
|
|
811
892
|
onRemoveFinding: () => setFinding(void 0),
|
|
812
|
-
context:
|
|
813
|
-
...context,
|
|
814
|
-
...finding && finding.clusterId === context.clusterId && finding.resource ? { resource: finding.resource } : {}
|
|
815
|
-
})
|
|
893
|
+
context: previewContext
|
|
816
894
|
}),
|
|
817
895
|
/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_terminal_context.AiTerminalContext, {
|
|
818
896
|
clusterId: context.clusterId,
|