takker99/ScrapBubble
使い方
注意
公式でも指摘されていますが、UserScriptは上級者向け機能です。
以下、JavaScriptを知らない方でも導入できると思われる程度まで解説はしてありますが、ScrapboxのUserScriptの原理や、JavaScriptの文法は知っているものとして、説明は一切しておりません。 そのつもりで読んでいただければと思います
2022-05-27 13:18:21 これまだいるのかな?
どのprojectのページに貼り付けても動作しますが、UserScriptの改竄を防ぐため、自分のみが編集できるprojectのページに貼り付けることを強く勧めます 以下、[/my-project/ScrapBubble]のmod.jsに貼り付けたとします
3. 以下のコードを自分のページのscript.jsに貼り付ける code:js
import { mount } from "../../my-project/ScrapBubble/mod.js";
mount();
project名(my-project)、ページタイトル(ScrapBubble)、コードブロック名(mod.js)は各自の設定で置き換えてください
補足(わかっている人むけ)
コード生成の設定は、URL parameterをいじると変えられます
例
code:template
ScrapBubble-min
code:mod.js
@CODE@
コード生成でやっていることは、transpileとbundle、minifyだけです
mount()にoptionを渡して挙動を変更できます
code:script.js
import { mount } from "../../my-project/ScrapBubble/mod.js";
mount({
});
変更点
開発方法を完全に変えた
git管理
TSXで書く
テスト書く
実装
web componentsは使わない
?followRename=trueを使う
/icons/done.iconCardをCardBubbleに内蔵する
TextBubbleの実装と統一させる
実装したいこと
リンク同一判定をカスタムできるようにする
例:/villagepump/yyyy/MM/ddをyyyy-MM-ddにマッチさせる
そんなことできるのか?
KaTeXのDOMとtex commandとの1対1対応を作らなければならない
コピーボタンをつける
任意の$ xに$ y^2を代入して……のような文章があったら、大量のコピーボタンが現れることになる
うざったい
複雑にしすぎてない?
書き込めるようにする?
空リンクではないが、重複除去処理した結果なにも表示するものがないリンクを特別なスタイルにする?
CSS classは付与しといてもよさそう
links2hopsを使えば、より読み込み速度を上げられそう
先回りしてカードを出せる
空リンクかどうかも、ページ内のすべてのリンクに対して一度に判定できる
これでも空リンクをすぐ判定できないケース
全てのprojectで空リンクなリンク
そのリンクのページデータを全てのprojectから取得しないと判定できない
基本空リンク(赤リンク)にし、繋がりを検知し次第青リンクに切り替える
空リンクを判定できないケースが少ないから、これで問題ない
2023-01-21 08:02:13 すでに実装済み
どこで実装したかは忘れた
card bubblesの並び替え
現状:更新日時の古い順
順番を何も考えずにrenderingしたらこうなった
流石に使いにくい
algorithmの構築に時間がかかりそう。ひとまずは更新日時順にしておく
project間は、1. whiteList順 2. lastAccessedの早い順に並べる
2022-10-29 19:44:58 なぜかwhitelistの逆順に表示されてしまう…
useBubble()の責任領域を変える
こいつがやることは
各階層でhoverされているリンクの情報の提供
前階層までにhoverしたリンクのリストの提供
重複を取り除くのに必要な情報
bubbleのhover・click event handlersの提供
これが難しい。bubble()内で新しく子要素用のbubble()を作成する必要が生じ、循環参照に陥る
これどういう現象だ?なんでこんなことしなきゃいけなくなったのか忘れてしまったtakker.icon
なんとか実装した
表示・非表示情報
これも更に難しい
bubbleしても、表示するかどうかは決められない
データがあるかどうか、bubble対象のprojectか確認してからbubble()を実行すれば解決する?
2022-10-09 06:05:50 新しい方法を思いついた
attachHover()
リンクホバーから実際にbubbleするかどうか判定するまでの処理を抽象化したもの
onStartでリンクホバー時の操作(prefetchなど)、intervalでホバー判定する時間の長さ、onEndでホバー成功時の操作(bubble実行など)を設定する
イベント処理をカプセル化しているので、端末に応じてホバー操作を代替操作に置き換えたりもできる
code:attachHover.ts
export const attachHover = <E extends Element = HTMLElement, T = void>(
element: E,
{ onStart, onEnd, onCancel, interval }: AttachHoverInit,
): void => {
let canceled = false;
const handleEnter = async (e) => {
const promise = onStart?.(e);
const handleCancel = (e) => {
canceled = true;
promise?.then?.((p) => onCancel?.(e, p)) ?? onCancel?.(e);
};
element.addEventListener("pointerleave", handleCancel);
await sleep(interval);
if (!canceled) onEnd(e, await promise);
element.removeEventListener("pointerleave", handleCancel);
canceled = false;
};
element.addEventListener("pointerenter", handleEnter);
return () => {
element.removeEventListener("pointerenter", handleEnter);
canceled = true;
};
};
export interface AttachHoverInit<E extends Element, T> {
onStart?: () => Promise<T>;
onEnd: (e: PointerEvent<E>, startResult: T) => void;
onCancel?: (e: PointerEvent<E>, startResult: T) => void;
interval: number;
}
ちなみに、bubbleのデータ作成には二つのアプローチがある
初期に使っていた方法
すべてのbubbleデータの読み込みをやらなければならない
配列で一気に取得する必要がある
空リンク判定処理も密結合してしまう
表示するページタイトルだけをbubbleデータの配列に含める
cacheを使い始めた頃から使っている方法
データ読み込み処理を分離できる
各bubbleのcomponentで読み込めるようになる
配列で一度に取得する必要がなくなる。
各componentで好きなタイミングで読み込めばいい
ページの存在判定を分離しなければならない
同じ函数で処理したいのに、分散してしまう
データに齟齬が生じる
のみ。ページデータの読み込みや加工には関与しない
位置情報は、ピクセルではなく比率で保持したほうがいいかも
画面回転などで位置が崩れにくい
code:ts
export interface Bubble {
project: string;
title: string;
/** Bubbleの表示位置計算で使う座標
*
* 左から順に、top left bottom right
*/
/** 親階層のbubblesのページタイトル */
parentTitles: string[];
/** 現在の階層の下に新しいbubbleを出す
*
* すでにbubbleされていた場合、それ以降のbubbleを含めて消してから新しいのを出す
* @param project bubbleするページのメインプロジェクト
* @param title bubbleするページのタイトル
* @param position bubbleするカードの表示位置
*/
bubble: (project: string, title: string, position: Position) => void;
/** 現在の階層より下のbubblesをすべて消す */
hide: () => void;
}
export const useBubble: () => Bubble[];
mobileではリンクの側にbubbleボタンを置く
mobileではhoverをうまく扱えなくて不便
hover判定させるには長押しする必要がある
しかも、長押しすると右クリックメニュー相当のダイアログが出てきてしまう
いちいち消すのがうっとおしい
代わりにボタンを置く
delayを短くするだけでも実現できそう
bodyにmountすればいい?
18:00:04 これ今もそうなのだろうか?
CSSを外して確認したい
networkから取得したデータの状態がエラーの場合は、cacheから返すようにする
fetch.tsにfallbackToCacheオプションを生やす
page titleにproject nameを含める
背景
project Aのリンクからproject Bのページをbubbleしたとき、違うprojectであることがわからなくなった
解決策
page titleにproject nameを含める
card bubbleのheaderの部分にもproject nameを入れようかなtakker.icon
同じthemeのprojectのcardsが並ぶと、違うprojectであることがわかりにくい
bubbleするまでに必要な時間を長くするのもありかも
現状は650ミリ秒にしている
これって調節できなかったっけ?
propertyを生やした気がしたのだが
生やしていなかったら生やそう
あ、やっぱり生えてた
mount({delay: 1000})で1秒にできる
project badgeをposition: fixedにする
読み込み状況を表示する
回線の遅い環境で使っていると、scriptがバグっているのか単に読み込みが遅いのかの違いがわからない
黒糖をhoverしたら、黒糖入りホットミルクのページがページリストに加わる
黒糖入りホットミルクが空ページなら、黒糖入りホットミルクの逆リンクが表示される
ページの中身を各componentではなく中央に全て管理させる
ページ内容とbubbleの表示順序、重複除去処理が本質的に関わり合っていて、下手に分離させるとむしろ不都合が生じる
初期の設計と同じく、useBubbleですべてのデータを配信したほうがよさそうだ
prefetchをpropsで渡す必要があるか?
いまはこうなっている
code:ts
/** ページデータを先読みする
*
* white listにない外部プロジェクトリンクは、そのページだけを読み込む
*/
const prefetch = useCallback((project: string, title: string) => {
prefetch_(
title,
projects.has(project) ? projects : new Set(project), watchList,
);
わざわざこの程度のwrapperを作って渡す必要がないような……
いや、これのおかげで下部componentが直接watchListに依存しないようになっているのか
もしこのwrapperを定義しないと、prefetchでしか使わないwatchListをpropsバケツリレーすることになる これは無理
fetchのエラーはcatchしたとしてもコンソールに流れてしまう
cacheしているが、一定時間経過したら再びfetchするようにしている
ページが新規作成されているかもしれないから
これは改善予定
バグ
[/ 斜体]でパースエラーしている
✅️スクロールロックを復活させる
子要素があるかどうかは、データの読み込み状況から判断する必要があり、難しい
useBubbles()だけでは決めきれない
もしかしたらいらないかも?
何らかの条件がそろうとscrapboxをいっさいクリックできなくなる
再読込するしかなくなる
event handlingで何か失敗している?
状態をどこかに表示して、条件を調べよう
.status-barの色が見にくいかも
[/A]でbubbleした[/B/page1]中にある[/B/page2]をbubbleして[/A/page2]が表示されたとき、[/A/pages]にproject badgeが表示されない
大本のprojectと一致するかどうかでproject badgeの表示非表示を切り替えているので、挙動は正しい
しかしこの場合はproject badgeを表示してほしい
bubble元のURLと違うから
あー、大本のprojectではなく、bubble元のURLと一致するかどうかで表示を切り替えたほうがいいか。
すべてページにタイトルを表示すればいいのでは
どのページだけ表示するかという判断をしなくて済む
Pull 14のcommitでも発生した
smartphoneでよく発生するようだ
リンクを長押ししても何も表示されない
以前のbubblesが消されないまま残ってしまうこともある
どこをクリックしても消えない
これは認識していなかった
なんだろう?一瞬表示判定されてしまうとか?
bubblingのevent handlingを変えたい
現状
card bubblesだけ個別にevent listenerを渡している
どうしたいか
全てのcomponentのeventを大本の<App />で捕捉する
現状だと、card bubblesの個数分event listenerを作る必要がある
これ単にlambda函数をその場で渡しているから、常にre-renderが発生してしまうんだよな そもそもcard bubblesだけ特別扱いなのがおかしい
そんなことする理由がない
えっまじ!?takker.icon
おかしい。ちゃんと無視しているはずなのだが……
明日これ聞いておくか
bookmarkletからなら動いた
なんだろう?takker.icon
たまたまかな?
katexエラー
version upで直るかな?
scrapbox ver. assets-20220818-060939 で使われているKaTeXのversionが0.12.0
てことは設定ミスだろうか?
2023-01-21 08:04:08 今も発生しているかは未確認
一度表示されたCardsが消える
Chrome for Android
間を置かずにbubbleし直しても同じ現象が発生したので、cacheの再読込は原因ではなさそう
もしくは、なぜか何度も再読込が発生してる?
メモリに最新の状態が反映されていない可能性もある
逆リンク計算アルゴリズムを切り出して、単体テストしたほうがよさそう
2023-01-18 19:51:58 まだ発生している……
テスト書かないとだめだな
UserCSSの設定でよろしくないことが起きている
Cannot read properties of undefined (reading 'replaceAll')
bubble.tsで発生
何らかの条件に一致したbubbleが表示された時、preactのかなり深いところでnullエラーを踏む
20:08:43 500 Internal Server errorなどの失敗したresponseをcacheから読み出す時、問答無用でthrowしてしまっているのが原因
code:mod.tsx