Commit 62715bb0 authored by Domi's avatar Domi

fix: chatdocs addon (better support for Bard)

parent 66213a6e
......@@ -12,7 +12,13 @@ import IconPlayCircle from "../icons/IconPlayCircle.vue"
import IconPause from "../icons/IconPause.vue"
import IconProgressActivity from "../icons/IconProgressActivity.vue"
import { sitesConfig } from "./helper"
import { query, dispatchInput, click, waitFor } from "@/utils/dom"
import {
query,
dispatchInput,
click,
waitFor,
getInputValue,
} from "@/utils/dom"
import { chatDocPrompt } from "@/utils/prompt"
import { useI18n } from "@/utils/i18n"
......@@ -295,6 +301,8 @@ const autoSend = async () => {
sendTask.key = key
sendTask.status = "running"
const selector = config.selector
const isWorking = () =>
!currentMessage.value.done &&
sendTask.status == "running" &&
......@@ -306,15 +314,28 @@ const autoSend = async () => {
console.log(">>", message)
await new Promise((r) => setTimeout(r, 100))
const input = query(config.selector.input) as HTMLInputElement
const input = query(selector.input) as HTMLElement
if (!input) {
throw Error("couldn't find input element for " + config.selector.input)
throw Error("couldn't find input element for " + selector.input)
}
await dispatchInput(input, message)
await new Promise((r) => setTimeout(r, 200))
await click(config.selector.send)
await new Promise((r) => setTimeout(r, 600))
await waitFor(config.selector.wait, 1000 * 30)
await click(selector.send)
await waitFor(
async (i) => {
const input = query(selector.input) as HTMLElement
const value = input ? getInputValue(input) : ""
const sented = value.length < 100
if (!sented && i % 3 == 0) {
await click(selector.send)
}
return sented
},
{ interval: 1000 * 2 }
)
await new Promise((r) => setTimeout(r, 200))
await waitFor(() => query(selector.wait) != null, {})
await new Promise((r) => setTimeout(r, 200))
await nextMessage()
......@@ -491,7 +512,7 @@ const resetSent = () => {
v-if="sendTask.error"
:class="[
'text-rose-600 bg-rose-200/10 border border-rose-600 px-3',
'mb-4 rounded py-1',
'mb-4 rounded py-1 break-words',
]"
>
{{ sendTask.error }}
......
......@@ -27,9 +27,9 @@ export const sitesConfig = [
path: /chat/,
maxInputLength: 4096,
selector: {
input: "input-area rich-textarea p",
input: "input-area rich-textarea div",
send: "input-area div[class*=send] button[class*=send]",
wait: "input-area div[class*=send] button[class*=send]:not([hidden])",
wait: "input-area div[class*=send] button[class*=send]",
},
},
{
......@@ -54,7 +54,7 @@ export const sitesConfig = [
},
},
{
host: chrome.runtime.id + '-',
host: chrome.runtime.id + "-",
path: /^\/dev.html/,
maxInputLength: 8000,
maxInputToken: 4096,
......
......@@ -3,6 +3,7 @@ import {
querySome,
copyStyleSheets,
getDomNonce,
getTrustedHTML,
replaceHtmlNonce,
removePrerenderRules,
} from "@/utils/dom"
......@@ -117,13 +118,7 @@ export async function copilotReopen({ url, width, height }: ReopenOptions) {
function writeHtml(pipWindow: Window, html: string) {
const nonce = getDomNonce(document)
let escaped = replaceHtmlNonce(html, nonce)
if (window.trustedTypes) {
const escapeHTMLPolicy = window.trustedTypes.createPolicy("escapePolicy", {
createHTML: (string: string) => string,
})
escaped = escapeHTMLPolicy.createHTML(escaped)
}
escaped = getTrustedHTML(escaped)
pipWindow.document.open()
pipWindow.document.write(escaped)
......
......@@ -10,7 +10,7 @@ const manifest = {
// short_name: "__MSG_short_name__",
// no more than 132 characters
description: "__MSG_description__",
version: "1.2.0",
version: "1.2.1",
action: {
default_icon: {
16: "logo.png",
......
......@@ -157,38 +157,61 @@ export function removePrerenderRules(doc: Document) {
}
}
export async function dispatchInput(
input: HTMLInputElement | HTMLTextAreaElement,
value: string
) {
export function getTrustedHTML(html: string) {
if ("window" in globalThis) {
const policy = window.trustedTypes.createPolicy("trustedPolicy", {
createHTML: (str: string) => str,
})
return policy.createHTML(html)
}
return html
}
export async function dispatchInput(input: HTMLElement, value: string) {
if (["INPUT", "TEXTAREA"].includes(input.nodeName)) {
input.value = value
;(input as HTMLInputElement).value = value
} else if (input.contentEditable) {
input.innerText = value
const html = value.replace(/\n/g, "<br />")
input.innerHTML = html
}
input.dispatchEvent(new Event("input", { bubbles: true }))
input.dispatchEvent(new Event("change", { bubbles: true }))
}
export function getInputValue(input: HTMLElement) {
if (["INPUT", "TEXTAREA"].includes(input.nodeName)) {
return (input as HTMLInputElement).value
} else if (input.contentEditable) {
const value = input.innerText
return value
}
return ""
}
export async function click(target: string) {
const el = query(target) as HTMLElement
el.click()
}
export async function waitFor(target: string, timeout?: number) {
export async function waitFor(
test: (i: number) => PromiseLike<boolean> | boolean,
options: { timeout?: number; interval?: number }
) {
let success = false
let count = 0
const t0 = Date.now()
const timeout = options.timeout || 1000 * 30
const interval = options.interval || 600
while (!success) {
const view = query(target)
success = !!view
const result = await test(count)
success = !!result
count++
await new Promise((r) => setTimeout(r, 200))
await new Promise((r) => setTimeout(r, interval))
if (timeout && Date.now() - t0 > timeout) {
throw Error(`click() timeout, target: ${target} wait: ${waitFor}`)
throw Error(`waitFor timeout, test: ${test}`)
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment