scrapbox-edit-emulation
/icons/hr.icon
UserScriptからScrapboxの編集を行うAPI
全機能import用
code:script.js
export {
moveLeft, moveUp, moveDown, moveRight,
goHeadWithoutBlank, goEndWithoutBlank,
goHead, goEnd, goLine,
goHeadLine, goLastLine,
scrollUp, scrollDown,
scrollHalfUp, scrollHalfDown,
} from '../scrapbox-cursor-emulation/cursor.js';
export {
enterEdit, leaveEdit,
} from './focus.js';
export {
launchDrawing,
duplicatePage,
jumpRandomPage,
togglePin,
togglePresentation,
deletePage,
} from './pageMenu.js';
export {
getReadablePageURL,
getReadableLineURL,
} from './url.js';
export {
indentLines, deindentLines, upLines, downLines, moveLines,
moveLinesBefore,
indentBlocks, deindentBlocks, upBlocks, downBlocks, moveBlocks,
} from './outline.js';
export {
undo, redo,
insertTimestamp,
} from './edit.js';
内容物
text編集
code:edit.js
import {press} from '../scrapbox-keyboard-emulation-2/script.js';
import {insertText} from '../scrapbox-insert-text-2/script.js';
export function undo(count = 1) {
for (const _ of range(count)) {
press('z', {ctrlKey: true});
}
}
export function redo(count = 1) {
for (const _ of range(count)) {
press('z', {shiftKey: true, ctrlKey: true});
}
}
export function insertTimestamp(index = 1) {
for (const _ of range(count)) {
press('t', {altKey: true});
}
}
editorにfocusを当てる/外す
code:focus.js
import {scrapboxDOM} from '/api/code/takker/scrapbox-dom-accessor/script.js';
export const enterEdit = () => scrapboxDOM.textInput.focus();
export const leaveEdit = () => scrapboxDOM.textInput.blur();
code:pageMenu.js
import {scrapboxDOM} from '/api/code/takker/scrapbox-dom-accessor/script.js';
const isPresentationMode = () =>
document.getElementsByTagName('html')0.dataset.displayStyle === 'presentation'; export function launchDrawing() {
if (isPresentationMode()) return;
scrapboxDOM.pageEditButtons2.click(); }
export function duplicatePage() {
if (isPresentationMode()) return;
scrapboxDOM.pageEditButtons3.click(); }
export function jumpRandomPage() {
if (isPresentationMode()) return;
scrapboxDOM.randomJumpButton.click();
}
export function togglePin() {
if (isPresentationMode()) return;
scrapboxDOM.pageEditButtons4.click(); }
export function togglePresentation() {
if (isPresentationMode()) {
scrapboxDOM.pageEditButtons0.click(); } else {
scrapboxDOM.pageEditButtons5.click(); }
}
export function deletePage() {
if (isPresentationMode()) return;
scrapboxDOM.pageEditButtons8.click(); }
選択範囲の取得
リンクを踏む
cursorの下にあるリンクを取得
行内のリンクを全て取得
URLの取得
code:url.js
export function getReadablePageURL() {
return ${document.location.origin}${decodeURIComponent(document.location.pathname)};
}
export function getReadableLineURL({lineDOM}) {
return ${getReadablePageURL()}${lineDOM.id.replace('L','#')};
}
ページのURL
行URL
インデントの上げ下げ
code:outline.js
import {press} from '/api/code/takker/scrapbox-keyboard-emulation-2/script.js';
export function indentLines(count = 1) {
for (const _ of range(count)) {
press('ArrowRight', {ctrlKey: true});
}
}
export function deindentLines(count = 1) {
for (const _ of range(count)) {
press('ArrowLeft', {ctrlKey: true});
}
}
export function moveLines(count) {
if (count > 0) {
downLines(count);
} else {
upLines(-count);
}
}
// to行目の後ろに移動させる
export function moveLinesBefore({from: From, to}) {
const count = to - From;
if (count >= 0) {
downLines(count);
} else {
upLines(- count - 1);
}
}
export function upLines(count = 1) {
for (const _ of range(count)) {
press('ArrowUp', {ctrlKey: true});
}
}
export function downLines(count = 1) {
for (const _ of range(count)) {
press('ArrowDown', {ctrlKey: true});
}
}
export function indentBlocks(count = 1) {
for (const _ of range(count)) {
press('ArrowRight', {altKey: true});
}
}
export function deindentBlocks(count = 1) {
for (const _ of range(count)) {
press('ArrowLeft', {altKey: true});
}
}
export function moveBlocks(count) {
if (count > 0) {
downBlocks(count);
} else {
upBlocks(-count);
}
}
export function upBlocks(count = 1) {
for (const _ of range(count)) {
press('ArrowUp', {altKey: true});
}
}
export function downBlocks(count = 1) {
for (const _ of range(count)) {
press('ArrowDown', {altKey: true});
}
}
import
code:js
import {press} from '/api/code/takker/scrapbox-keyboard-emulation-2/script.js';
import {getLinkIncludingCursor} from '/api/code/takker/Scrapboxでcursor下のリンクを取得する/script.js';
import {cursor} from '/api/code/takker/scrapbox-cursor-position-2/script.js';
import {char as c} from '/api/code/takker/scrapbox-char-info/script.js';
import {line as l} from '/api/code/takker/scrapbox-line-info-2/script.js';
import {jumpToLF, jumpToChar} from '/api/code/takker/scrapbox-cursor-jumper/script.js';
import {splitWords, splitWORDs} from '/api/code/takker/ScrapVim-lite-2%2FwordSplitter/script.js';
import {scrapboxDOM} from '/api/code/takker/scrapbox-dom-accessor/script.js';
f{char}
code:js
jump_f: ({count = 1, char} = {}) {
const index = l(cursor.line).charDOMs
.slice(cursor.index) // cursor下の文字より後ろのみを取得
.find(span => span.textContent === char);
if (!index) return;
_log(move to ${index} at %o, cursor.line);
jumpToChar({id: cursor.line.id, index});
}
単語移動
後方のWordsの先頭に移動する
code:js
goWordHead: ({count = 1} = {}) => {
//後方検索
_log('splitted words: %o, cursor.line: %o, cursor.index: %o',
splitWords(line.textContent), line, index);
let match = splitWords(line.textContent)
.find(word => word.index > cursor.index);
// 単語の先頭がこれ以上なければ、次行に進む
if (!match) {
//先頭に進むのは確実なので、End+→で飛ぶ
_log('Go to the next line.');
press('End');
press('ArrowRight');
return;
}
_log('the present line: %o', line);
_log('the next word: %o', match);
jumpToChar({id: line.id, index: match.index, shiftKey: visual});
},
前方のWordsの先頭に移動する
code:js
backWordHead: ({count = 1, visual = false, cursor: {line, index}} = {}) => {
// 前方検索
_log('splitted words: %o, cursor.line: %o, cursor.index: %o',
splitWords(line.textContent), line, index);
let match = splitWords(line.textContent)
.filter(word => word.index < index)?.pop();
let pressNum = 0;
if (!match) {
// なければ前の行に入る
_log('Go to the previous line.');
const prevLine = line.previousElementSibling;
if (!prevLine) return; // 先頭行だったら何もしない
match = splitWords(prevLine.textContent)?.pop();
_log('the previous line: %o', prevLine);
_log('the next word: %o', match);
jumpToLF({id: prevLine.id, shiftKey: visual});
jumpToChar({id: prevLine.id, index: match.index, shiftKey: visual});
return;
}
_log('the present line: %o', line);
_log('the next word: %o', match);
jumpToChar({id: line.id, index: match.index, shiftKey: visual});
},
後方のWordsの末尾に移動する
code:js
goWordEnd: ({count = 1, visual = false, cursor: {line, index}} = {}) => {
//後方検索
_log('splitted words: %o, cursor.line: %o, cursor.index: %o',
splitWords(line.textContent), line, index);
let match = splitWords(line.textContent)
.find(word => word.index + (word.length - 1) > index);
if (!match) {
// なければ次の行に入る
_log('Go to the next line.');
const nextLine = line.nextElementSibling;
if (!nextLine) return; // 最後の行だったら何もしない
match = splitWords(line.textContent)0; _log('the next line: %o', nextLine);
_log('the next word: %o', match);
jumpToLF({id: nextLine.id, shiftKey: visual});
jumpToChar({id: nextLine.id, index: match.index + match.length - 1, shiftKey: visual});
return;
}
_log('the present line: %o', line);
_log('the next word: %o', match);
jumpToChar({id: line.id, index: match.index + match.length - 1, shiftKey: visual});
},
前方のWordsの末尾に移動する
code:js
backWordEnd: ({count = 1, visual = false, cursor: {line, index}} = {}) => {
// 前方検索
_log('splitted words: %o, cursor.line: %o, cursor.index: %o',
splitWords(line.textContent), line, index);
let match = splitWords(line.textContent)
.filter(word => word.index + (word.length - 1) < index)?.pop();
// 単語の末尾が前方になければ前の行に移動する
if (!match) {
// 行末に移動することがわかっているので、Home + ←を使う
_log('Go to the previous line.');
emulator.press('Home', {shiftKey: visual});
emulator.press('ArrowLeft', {shiftKey: visual});
emulator.press('ArrowLeft', {shiftKey: visual});
return;
}
_log('the present line: %o', line);
_log('the next word: %o', match);
jumpToChar({id: line.id, index: match.index + match.length - 1, shiftKey: visual});
},