Gyazoにuploadした画像URLをコピペするPage Menu@2.0.0
変更点
使い方
1. buildを押して生成されたコードをどこかのページのどこかのコードブロックに貼り付ける 以下、/sample/user/mod.jsに貼り付けたとする
3. 発行したtokenを↓のコードに入れて、自分しか参加していないprivate projectのどこかのページのどこかのコードブロックに貼り付ける
code:auth.js
export const GYAZO_ACCESS_TOKEN = "your_access_token";
以下、/sample-private/GYAZO_ACCESS_TOKEN/auth.jsに貼り付けたとする
4. 自分のページのscript.jsに以下を追記する
maxCountは無料ユーザの場合10が最大
Gyazo Proユーザーは100まで
code:js
import { addGyazoMenu } from "../../sample/user/mod.js";
import { GYAZO_ACCESS_TOKEN } from"../../sample-private/GYAZO_ACCESS_TOKEN/auth.js";
addGyazoMenu({
maxCount: 100,
accessToken: GYAZO_ACCESS_TOKEN,
});
onClickを変えると画像を選択したときの挙動を変更できる
例:ctrlキーを押すとURLコピー、それ以外はカーソル位置に挿入
code:js
import { addGyazoMenu } from "../../sample/user/mod.js";
import { GYAZO_ACCESS_TOKEN } from"../../sample-private/GYAZO_ACCESS_TOKEN/auth.js";
addGyazoMenu({
maxCount: 10,
accessToken: GYAZO_ACCESS_TOKEN,
onClick: async (image, e) => {
try {
if (e.ctrlKey) {
await navigator.clipboard.writeText(image.permalink_url);
return;
}
const cursor = document.getElementById("text-input");
if (!cursor) {
throw Error("#text-input is not ditected.");
}
cursor.focus();
cursor.value = image.permalink_url;
const event = new InputEvent("input", { bubbles: true });
cursor.dispatchEvent(event);
await scrapbox.Page.waitForSave();
} catch(error) {
alert(${error});
console.error(error);
}
},
});
code:mod.ts
/// <reference no-default-lib="true" />
/// <reference lib="esnext" />
/// <reference lib="dom" />
/// <reference lib="dom.iterable" />
import { scrapbox } from "jsr:@cosense/types@0.10/userscript";
import { getImages, type Image } from "jsr:@takker/gyazo@0.4";
import type { React } from "./react-event.ts";
export type { Image, React };
export interface AddGyazoMenuInit {
/** Gyazo Access token */
accessToken: string;
/** Max is 100
* Note that free users cannot get more than 10 images.
*
* @default {10}
*/
maxCount?: number;
onClick?: (image: Image, event: React.MouseEvent<HTMLAnchorElement>) => void;
}
interface UnsafeWindow {
GM_fetch?: (typeof globalThis.fetch);
}
const id = "Gyazo Viewer";
export const addGyazoMenu = (init: AddGyazoMenuInit):void => {
let job: Promise<void> = Promise.resolve();
const GM_fetch = (globalThis as unknown as UnsafeWindow).GM_fetch;
scrapbox.PageMenu.addMenu({
title: !GM_fetch ? "Open Gyazo" : id,
icon: "kamon kamon-gyazo",
onClick: !GM_fetch
: () => {
job = job.then(
() => loadImages({
per: init.maxCount ?? 10,
accessToken: init.accessToken,
fetch: GM_fetch,
onClick: init.onClick,
})
);
},
});
};
interface LoadImagesInit {
per: number;
accessToken: string;
fetch?: typeof fetch;
onClick?: (image: Image, event: React.MouseEvent<HTMLAnchorElement>) => void;
}
const loadImages = async (init: LoadImagesInit): Promise<void> => {
let timer: number | undefined;
try {
const promise = getImages<Response>({ page: 0, ...init });
// 読み込みに時間がかかるようであれば、読み込み中の表示を出す
timer = setTimeout(() => {
scrapbox.PageMenu(id).addItem({
title: "Loading...",
icon: "fas fa-spinner",
onClick: () => {},
})
}, 100);
const res = await promise;
if (!res.ok) throw new Error(${res.status} ${res.statusText});
const images = await res.json();
clearTimeout(timer);
scrapbox.PageMenu(id).removeAllItems();
for (const image of images) {
if (!image.image_id) continue;
scrapbox.PageMenu(id).addItem({
title: image.metadata?.title || "Untitled",
image: image.url,
onClick: (event) => init.onClick?.(image, event) ?? navigator
?.clipboard
?.writeText?.(image.permalink_url)
?.catch?.((e) => { alert(${e}); console.error(e); }),
});
}
} catch(e) {
clearTimeout(timer);
scrapbox.PageMenu(id).removeAllItems();
scrapbox.PageMenu(id).addItem({
title: "Failed to load the image list.",
icon: "fas fa-exclamation-triangle",
onClick: () => {},
});
console.error("Failed to load the image list", e);
}
};
code:react-event.ts
type NativeMouseEvent = MouseEvent;
// deno-lint-ignore no-namespace
export namespace React {
/**
* currentTarget - a reference to the element on which the event listener is registered.
*
* target - a reference to the element from which the event was originally dispatched.
* This might be a child element to the element on which the event listener is registered.
*/
export interface SyntheticEvent<
T extends Element = Element,
E extends Event = Event,
{
nativeEvent: E;
currentTarget: T & EventTarget;
target: EventTarget;
bubbles: boolean;
cancelable: boolean;
defaultPrevented: boolean;
eventPhase: number;
isTrusted: boolean;
preventDefault(): void;
isDefaultPrevented(): boolean;
stopPropagation(): void;
isPropagationStopped(): boolean;
persist(): void;
timeStamp: number;
type: string;
}
export interface AbstractView {
styleMedia: StyleMedia;
document: Document;
}
export interface UIEvent<T extends Element = Element, E extends Event = Event>
extends SyntheticEvent<T, E> {
detail: number;
view: AbstractView;
}
export interface MouseEvent<
T extends Element = Element,
E extends Event = NativeMouseEvent,
extends UIEvent<T, E> {
altKey: boolean;
button: number;
buttons: number;
clientX: number;
clientY: number;
ctrlKey: boolean;
/**
*/
getModifierState(key: string): boolean;
metaKey: boolean;
movementX: number;
movementY: number;
pageX: number;
pageY: number;
relatedTarget: EventTarget | null;
screenX: number;
screenY: number;
shiftKey: boolean;
}
}
$ await import("/api/code/takker/Gyazoにuploadした画像URLをコピペするPage_Menu@2.0.0/sample.js");
code:sample.js
import { addGyazoMenu } from "./mod.js";
import { GYAZO_ACCESS_TOKEN } from"../../takker-memex/GYAZO_ACCESS_TOKEN/auth.js";
addGyazoMenu({
maxCount: 100,
accessToken: GYAZO_ACCESS_TOKEN,
});
code:mod.js