generated at
scrapbox-keicho

使い方
install
js
import('/api/code/takker/scrapbox-keicho/script.js') .then(({setup}) => setup());
options
function setup(props: {botIcon?:string; keyBinding?:Partial<{key:string; ctrlKey:boolean; shiftKey:boolean; altKey:boolean;}>; disableKeyBinding?:boolean;}): void
botIcon : keichoの返信の先頭に入る文字列
keyBinding : 送信用ショートカットキー
disableKeyBinding : 送信用ショートカットキーを無効にするかどうか
PopupMenuのボタンを押して開始する
選択範囲は無視する選択範囲を送信する
選択範囲が空白のときは何もしない
PopupMenuもどきがカーソルの下に表示される。この中にあるボタンを押してkeichoとやり取りする
送信方法
Send ボタンを押すか、 <C-Enter> を押す
フィードバックボタンを押すと、そのボタンに書かれた内容が送信される
🙂と🙁は入力されるだけで送信はされない
終了方法
Exit ボタンを押す
もう一度始めるときはscrapbox-keicho#6102995f1280f000001c096dをやる

仕様
送信されるテキストの範囲
[/nishio/nisbot.icon] が先頭にある行の次に最初に現れる、インデントなしの行から
アイコンは設定から変えられるようにする
カーソル行まで
記法やインデントは外して送信される
とりあえず [] を外すだけにしておく
talk IDの保存
URLで保存する

2021-08-02
23:47:56 GitHub Gistにコードを保存しておいた
周辺ライブラリのgit管理が済んでいないので、repoを作るのは先送りした
2021-07-30
11:22:20 NGボタンを入れた

やりたいこととか
記法を外して送信する
done選択範囲をkeichoに聞く
PopupMenuから聞き出しチャットを開始するときは、選択範囲中の文字列を投げられるようにしてもいいかもしれない
ていうかそっちのほうが自然なUIな気がする
PopupMenuは選択範囲に対して何らかの作用を適用するもの
選択しているのに、選択範囲以外に対してなにかされるのは直感に反する
複数行の入力は相槌モード (keicho)を使うべき?
↓の効果もあるが、複数行の一括入力だと引用時にその複数行をまとめて引用してしまうという欠点を防ぐ効果もある
途中から相槌モードを始めることができることは確認した
最初にbotの問いかけからはじめさせたい
自分から問いかけるのは、予め何について話したいかまで言語化出来ていないと難しい
と思ったが、相槌モード (keicho)を使えばよさそう
相槌モード (keicho)終了後に「あなたはこの会話で何が起きて欲しいですか?」と勝手に聞いてきてくれる
目的特化モードを実装したい
UIを色々考える必要があるな

scrapboxで認知行動療法を行えるようにしたい
nisbotその「scrapbox」は、どんな「scrapbox」ですか?
(どんなscrapboxかと言われてもねえ……)
nisbotその「scrapbox」について、他に何かありますか?
(どんなscrapboxかと言われてもねえ……)
自分がいつも使っているeditorがscrapboxなので、scrapboxでなるべく全部完結できるようにすると楽で助かる
nisbotその「scrapbox」は、どのあたりにありますか?
物理的には
自分のlaptopとsmartphoneから使える。まあその中にあるということ
時間で考えると、ほぼいつでもどこでもすぐに使える場所にある
nisbot
> 自分がいつも使っているeditorがscrapboxなので、scrapboxでなるべく全部完結できるようにすると楽で助かる
この「scrapbox」は、どこにありますか?
(「どのあたり」と「どの」の違いがわからない……)
自分の目の前にいつもある
nisbot
> 自分がいつも使っているeditorがscrapboxなので、scrapboxでなるべく全部完結できるようにすると楽で助かる
あなたはこの「scrapbox」をどう知るのですか?
(この方向の問はちょっとよくない)
nisbotあなたが価値を感じているのは何ですか?
(唐突すぎない?)
nisbot
> > 自分がいつも使っているeditorがscrapboxなので、scrapboxでなるべく全部完結できるようにすると楽で助かる
あなたはこの「scrapbox」をどう知るのですか?
この「scrapbox」は、何のようですか?
(多分これで、心の声だけなら改行するだけになった。)
(心の声がkeichoに反映されないのが難点)
(一行ごとに「まず聞いて」で入力すればなんとかなるかも)
(もしくは、keichoの方で改行に対応してもらうことでも対処できる)
👎scrapbox
nisbot
> > 自分がいつも使っているeditorがscrapboxなので、scrapboxでなるべく全部完結できるようにすると楽で助かる
あなたはこの「scrapbox」をどう知るのですか?
この「editor」は、どんな「editor」ですか?
👎editor
nisbot
> > 自分がいつも使っているeditorがscrapboxなので、scrapboxでなるべく全部完結できるようにすると楽で助かる
あなたはこの「scrapbox」をどう知るのですか?
この「全部完結できるよう」は、どんな「全部完結できるよう」ですか?
👎全部完結できるよう
nisbot
> > 自分がいつも使っているeditorがscrapboxなので、scrapboxでなるべく全部完結できるようにすると楽で助かる
あなたはこの「scrapbox」をどう知るのですか?
この「全部完結できるようにする」は、どんな「全部完結できるようにする」ですか?
👎全部完結できるようにする
nisbot
> > 自分がいつも使っているeditorがscrapboxなので、scrapboxでなるべく全部完結できるようにすると楽で助かる
あなたはこの「scrapbox」をどう知るのですか?
この「全部完結できる」は、どんな「全部完結できる」ですか?
👎全部完結できる
nisbot
> nisbotその「scrapbox」は、どんな「scrapbox」ですか?
(どんなscrapboxかと言われてもねえ……)
この「/nishio/nisbot.icon」は、どんな「/nishio/nisbot.icon」ですか?
👎/nishio/nisbot.icon
nisbot
> 物理的には
自分のlaptopとsmartphoneから使える。まあその中にあるということ
時間で考えると、ほぼいつでもどこでもすぐに使える場所にある
この「laptop」は、どんな「laptop」ですか?
👎laptop
nisbot
> 自分の目の前にいつもある
この「目」は、どんな「目」ですか?
自分の目。この目に写っている限り、そのscrapboxの中身を閲覧することができる
聴覚だけで情報を取得するのはかなり困難
nisbotその「目」について、他に何かありますか?
👎目
nisbotその「自分」は、どんな「自分」ですか?
この受け答えをしている人物
nisbotその「受け答え」は、どんな「受け答え」ですか?
scrapbox-keichoを使った受け答え
nisbotその「受け答え」について、他に何かありますか?
ない
nisbot
> scrapbox-keichoを使った受け答え
この「受け答え」は、どこから来るのですか?
keichoのserverから質問が来る。回答は自分の書き出したテキストから来ている
nisbotその「自分」について、他に何かありますか?
特にない
nisbot
> scrapbox-keichoを使った受け答え
この「scrapbox」に関して、何が起こるとよいですか?
(かなり質問の範囲が広いな)
👍scrapbox-keicho
nisbotそれには「scrapbox」に何が起こる必要がありますか?
keichoと通信して結果をscrapboxに反映するUserScriptを作る必要がある。それがscrapbox-keicho
nisbot「scrapbox」に関して、それができますか?
できる。というかもう作った
nisbot
> keichoと通信して結果をscrapboxに反映するUserScriptを作る必要がある。それがscrapbox-keicho
この「scrapbox-keicho」は、どんな「scrapbox-keicho」ですか?
scrapbox上で簡単にkeichoに話しかけられるUserScript
文字選択する必要がないので楽に使える
nisbotその「scrapbox」は、どこから来るのですか?
👎scrapbox
nisbot
> keichoと通信して結果をscrapboxに反映するUserScriptを作る必要がある。それがscrapbox-keicho
この「scrapbox-keicho」について、他に何かありますか?
これはscrapboxとkeicho間の通信を試したもの
その後使いやすいUIにしたUserScriptを作ろうとKeichoとのチャットするUserScriptでいろいろ考えていた
一ヶ月くらい後にnishioさんが試してくれたのをきっかけに開発を開始し、scrapbox-keichoとして実装できた
いまそのテストをしている
nisbotその「scrapbox-keicho」は、どこにありますか?
(こういう物理的な実体のないものの場所を聞かれても困るんだよなあ……)
(いや、物理的な実体がないからこそ、その場所を考えよということか)
(物理的なものであれば場所を問えるのは当然)
(一見場所を問えそうにないもの、そもそも問うことすら思いつかないことに対して問うことで、今まで見えていなかった情報が見えてくる可能性がある)
(……深読みし過ぎかなあ)
データはインターネット上にある
実行環境はscrapbox.ioなので、scrapbox上にあるとも言える
nisbot
これはscrapboxとkeicho間の通信を試したもの
その後使いやすいUIにしたUserScriptを作ろうとKeichoとのチャットするUserScriptでいろいろ考えていた
一ヶ月くらい後にnishioさんが試してくれたのをきっかけに開発を開始し、scrapbox-keichoとして実装できた
いまそのテストをしている

この「scrapbox-keicho」は、どこから来るのですか?
👎scrapbox-keicho
nisbot
これはscrapboxとkeicho間の通信を試したもの
その後使いやすいUIにしたUserScriptを作ろうとKeichoとのチャットするUserScriptでいろいろ考えていた
一ヶ月くらい後にnishioさんが試してくれたのをきっかけに開発を開始し、scrapbox-keichoとして実装できた
いまそのテストをしている

この「keicho」は、どんな「keicho」ですか?
/nishio/聞き出しチャットシステムによると、言語化出来ていないことに気づきを与えて言語化する支援を行うシステム
nisbotその「keicho」について、他に何かありますか?
もやもやしたときやぼーっとしているとき、やることがわからないときに、keichoに尋ねればなにか見えてくるんじゃないかと思っている
(しょうもない期待ですね)
認知行動療法の一環としても使えると思うtakker
カウンセラーの代わりとしてkeichoを使う
nisbotその「keicho」は、どこから来るのですか?
(同じ質問をさっきされた気がするのだが)
nishioさんが運営しているserver上から返答が返ってくる
nisbot
> > もやもやしたときやぼーっとしているとき、やることがわからないときに、keichoに尋ねればなにか見えてくるんじゃないかと思っている
> 認知行動療法の一環としても使えると思うtakker
>  カウンセラーの代わりとしてkeichoを使う
この「keicho」は、どこにありますか?
👎keicho
nisbotその「/nishio/nishio.icon」は、どんな「/nishio/nishio.icon」ですか?
👎/nishio/nishio.icon
nisbotその「server」は、どんな「server」ですか?
👎server
nisbot
>  これはscrapboxとkeicho間の通信を試したもの
> その後使いやすいUIにしたUserScriptを作ろうとKeichoとのチャットするUserScriptでいろいろ考えていた
> 一ヶ月くらい後にnishioさんが試してくれたのをきっかけに開発を開始し、scrapbox-keichoとして実装できた
> いまそのテストをしている
この「UserScript」は、どんな「UserScript」ですか?
👎UserScript
nisbot
> > もやもやしたときやぼーっとしているとき、やることがわからないときに、keichoに尋ねればなにか見えてくるんじゃないかと思っている
> 認知行動療法の一環としても使えると思うtakker
>  カウンセラーの代わりとしてkeichoを使う
この「認知行動療法」は、どんな「認知行動療法」ですか?
(そういえば最初の質問が認知行動療法をscrapboxで使いたいという話だったのを忘れてた)
(脱線しすぎたな)



dependencies
script.js
import { createPopupMenuBar } from "../PopupMenuもどき/script.js"; import { insertText } from "../scrapbox-insert-text-2/script.js"; import { goEnd } from "../scrapbox-motion-emulation/script.js"; export function setup(props) { const { botIcon = "[/nishio/nisbot.icon]", keyBinding = {key: "Enter", ctrlKey: true}, disableKeyBinding = false, } = props ?? {}; const { render, visible, open, close } = createPopupMenuBar(); close(); scrapbox.PopupMenu.addButton({ title: () => !visible() ? "🤖" : "", onClick: (text) => { if (text.trim() === "") return; update(text); }, }); if (disableKeyBinding) return; const {key, ctrlKey, shiftKey, altKey} = keyBinding; document.getElementById('text-input').addEventListener('keydown', (e) => { if (!visible()) return; if (key !== undefined && key !== e.key) return; if (ctrlKey !== undefined && ctrlKey !== e.ctrlKey) return; if (shiftKey !== undefined && shiftKey !== e.shiftKey) return; if (altKey !== undefined && altKey !== e.altKey) return; e.preventDefault(); e.stopPropagation(); update(); });

script.js
async function update(text) { text = text ?? getAnswer(botIcon); if (text.trim() === "") { goEnd(); await insertText("\n"); return; } const pending = askKeicho(text); // 時間がかかるようであれば読み込み中表示をする const timer = setTimeout(() => { render([{text: "Asking..."}]); open(); }, 1000); const { question, buttons } = await pending; clearTimeout(timer); console.log(`[scrapbox-keicho] Ask: \n${text}`); console.log(`[scrapbox-keicho] Answer: \n${question}`); if (buttons.length > 0) console.log(`[scrapbox-keicho] Buttons`, buttons); goEnd(); await insertText(question); render([ { text: "Send", onClick: () => update(), }, { text: "🙂", onClick: () => insertText("🙂"), }, { text: "🙁", onClick: () => insertText("🙁"), }, ...buttons.map((button) => ({ text: button, onClick: async () => { goEnd(); await insertText(button); await update(button); }, })), { text: "Exit", onClick: () => close(), }, ]); open(); }

keichoに尋ねる
script.js
async function askKeicho(text) { // idを取得する const id = getTalkId(); const { id: _id, text: question, buttons } = await globalThis.askKeicho(text, { id }); // idが更新されたらURLを作る const url = id !== _id ? `https://keicho.netlify.app/#talk=${_id}\n` : ""; return { question: question.trim() === "" ? "\n" : `\n${url}${botIcon}${format(question)}\n`, buttons, }; } }

質問のformatting
script.js
function format(question) { const splitted = question.split("\n\n"); if (splitted.length > 1) { const [quote, _question] = splitted; return [ "", ...quote.trim().split("\n").map((line) => ` > ${line}`), ` ${_question}` ].join("\n"); } return question; }
talk Idを取得する
script.js
const REGEXP_URL = /https:\/\/keicho\.netlify\.app\/#talk=(\w+)/; function getTalkId() { for (const { text } of scrapbox.Page.lines) { const talkId = text.match(REGEXP_URL)?.[1]; if (talkId) return talkId; } }

送信範囲を取得する
dependencies
script.js
import { position } from "../scrapbox-cursor-position-6/script.js"; import { getLineNo, getIndentLineCount } from "../scrapbox-access-nodes@0.1.0/script.js"; function getAnswer(botIcon) { const endNo = getLineNo(position().line); const iconLineNos = scrapbox.Page.lines.flatMap(({ text }, index) => text.startsWith(botIcon) ? [index] : [] ); let startNo = 1; // アイコン行がなければ、タイトルの次の行からテキストを取得する if (iconLineNos.length > 0) { // カーソル行がどのアイコン行よりも上にあったら何もしない if (endNo <= iconLineNos[0]) return ""; startNo = iconLineNos.filter((lineNo) => lineNo < endNo).pop(); } // ぶら下がっている行は含めない startNo += getIndentLineCount(startNo); // アイコン行を除く startNo++; const lines = scrapbox.Page.lines.slice(startNo, endNo + 1).map((line) => line.text); if (lines.every((text) => /\([^)]*\)/.test(text.trim()))) { // 全ての行が心の声なら、そのまま返す return lines.join("\n"); } else { // 普通の回答が混じっていたら、そこだけ取り出す return lines.filter((text) => !/\([^)]*\)/.test(text.trim())).join("\n"); } }

#2021-08-02 15:09:12
#2021-08-01 11:03:16
#2021-07-30 10:17:16
#2021-07-30 01:24:36
#2021-07-29 21:07:17