chatGPTとchatするUserScript
これは何をするためにつくられたものなんだろう?基素.icon
AskChatGPTでは返答が来るまで思考がストップされる感じが嫌だったので自動入力してくれないかなと思い作ったやつですwogikaze.icon Notion AIがそこそこ便利だと思ったので案を置いておきたかった
Scrapbox上でChatするってことか〜基素.icon
使い方
導入すると青いボタンが行ごとに出るので実行しちゃってください
行の下に返答が挿入されます
多分並列処理できます
なるほどー。行ごとにコメントかけるってことかあtakker.icon
自分のもこのUIで試してみるか
既知のバグ
ボタンを押した後すぐに下の行を編集するとinsertされない
下の行に依存して挿入しているため
リンクやアイコンを含む行にボタンが表示されない
修正したコードはStreamのゴミ箱にある
必要
このリンクでbundleした以下のコードを自分のページに貼り付ける code:sc.js
insertメソッドがあれば使いたかったのですがサポートしないと書いてあったので
(ついでにwogikaze.iconの技術力的に実装できないなーと思い)こうなりましたwogikaze.icon
あれそんなこと書いてましたっけ?何も覚えていない()takker.icon
使い道あったじゃん!takker.icon
想像力が足りない
-stdの方で実装できますかね...
patch()だと、特定行の下に挿入するコードを書くのが確かにめんどいですねtakker.icon
こうなる
code:ts
await patch(
project,
title,
(lines) => lines.reduce((acc, cur) => {
acc.push(cur.text);
if (cur.id === id) acc.push(...additionalLines);
return acc;
}, [] as string[]),
);
試してないのであれですがpatchだとpatchが終了する前に行を更新したら更新分が消えませんか?wogikaze.icon
①から②の間で別の編集が入った場合、最初からやり直しになりますtakker.icon
サーバからcommit idを取得(①)→updatorで変更後の本文を生成→変更前後で差分をとってサーバに送信する(②)
そのため、途中で行を更新されても、その更新を考慮して差分を作成できます
patch(project, title, () => [title, "aaa", "bbb"])のような常に同じ本文を返すと更新が消えてしまいますが、今回のコードは変更前の本文をそのまま維持するため、更新分が消えることはないと思います
code:sc.js
function updateButtonStyle(button, lineElement, indent) {
const head = lineElement.getElementsByClassName("indent")0 ?? lineElement.getElementsByClassName("char-index")0; const offset = lineElement.getBoundingClientRect().left;
const left = indent === 0 ? offset : head.getBoundingClientRect().left;
button.style.left = calc(${Math.round(left - offset)}px - ${lineElement.getElementsByClassName("code-body").length > 0 ? 2.0 : 1.0}em);
}
async function onButtonClick(lineElement) { // async を追加
const result = await askChatGPT(lineElement.innerText.trimStart()); // await を追加
let answer;
if (!result.error) {
answer = result.choices0.message.content.trim(); } else {
answer = "test";
}
const { insert, cleanup } = await joinPageRoom(scrapbox.Project.name, scrapbox.Page.title);
const nextLineElement = lineElement.nextElementSibling;
if (nextLineElement) {
await insert(lineElement.textContent.match(/^\s*/)0.replace(/\t/g," ")+" "+answer, nextLineElement.id.slice(1)); } else {
await insert(lineElement.textContent.match(/^\s*/)0.replace(/\t/g," ")+" "+answer, "_end"); }
cleanup();
}
function renderButton() {
if (scrapbox.Layout !== "page") return;
scrapbox.Page.lines.forEach(line => {
const lineElement = document.querySelector(#L${line.id});
let button = lineElement.querySelector("a");
if (button) {
if (line.text.trim() === "") {
button.style.display = "none";
} else {
button.style.display = "inline";
updateButtonStyle(button, lineElement, lineElement.indent);
}
} else if (line.text.trim() !== "") {
button = document.createElement("a");
button.type = "button";
button.style.position = "absolute";
button.style.fontSize = "20px";
button.style.zIndex = "200";
updateButtonStyle(button, lineElement, lineElement.indent);
HTMLElement.indentってなんだろうtakker.icon
このようなpropertyは生えていないはず
確かにない、なんで動いているんだ(困惑)wogikaze.icon
/vim-jp-emojis/こわわ.icontakker.icon
code:sc.js
button.addEventListener("click", () => onButtonClick(lineElement));
const icon = document.createElement("i");
icon.className = "fas fa-caret-down";
button.append(icon);
lineElement.insertAdjacentElement("afterbegin", button);
}
});
}
const start = Date.now();
// 処理
renderButton();
scrapbox.addListener("lines:changed", renderButton);
const end = Date.now();
console.log(ButtonのRenderingにかかった時間: ${end - start}ミリ秒);
performanceのことを考え始めるときには、別の方法を使うといいかも
code:fetch.js
// ==UserScript==
// @name Fetch ChatGPT
// @version 0.1
// @description try to take over the world!
// @author You
// @grant GM_xmlhttpRequest
// ==/UserScript==
unsafeWindow.askChatGPT = async (
text,
{ temperature = 0.7, max_tokens = 500 } = {}
) => {
const headers = {
"Content-Type": "application/json",
Authorization: "Bearer " + localStorage.getItem("OPENAI_KEY"),
};
const data = JSON.stringify({
temperature,
max_tokens,
model: "gpt-3.5-turbo",
messages: role: "user", content: text },
});
return await new Promise((resolve, reject) =>
GM_xmlhttpRequest({
method: "POST",
data,
headers,
onload: ({ response }) =>
resolve({
...response,
}),
responseType: "json",
onerror: (e) => reject(e),
})
);
};