Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | 1x 26x 26x 26x 26x 13x 26x 4x 4x 26x 3x 3x 6x 6x 1x 1x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 32x 32x 2x 2x 32x 30x 30x 30x 32x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 46x 46x 46x 46x 46x 46x 29x 29x 29x 46x 26x 26x 26x 26x 26x 17x 17x 1x 1x 1x 16x 16x 16x 16x 17x 3x 3x 3x 17x 13x 13x 13x 13x 7x 7x 7x 7x 6x 6x 6x 6x 9x 9x 26x 26x 26x 26x 26x | import { App, Modal, Notice } from "obsidian";
import type LilbeePlugin from "../main";
import { MESSAGES } from "../locales/en";
import { bindEscapeToClose, ensureUrlScheme } from "../utils";
type ParseResult = { value: number | null; error: string | null };
function parseOptionalCount(raw: string, opts: { allowZero: boolean }): ParseResult {
const errMsg = opts.allowZero ? MESSAGES.ERROR_CRAWL_DEPTH_INVALID : MESSAGES.ERROR_CRAWL_MAX_PAGES_POSITIVE;
const trimmed = raw.trim();
if (trimmed === "") return { value: null, error: null };
const n = Number(trimmed);
if (!Number.isFinite(n) || !Number.isInteger(n) || n < 0) {
return { value: null, error: errMsg };
}
if (n === 0 && !opts.allowZero) {
return { value: null, error: errMsg };
}
return { value: n, error: null };
}
const asInput = (el: HTMLElement): HTMLInputElement => el as unknown as HTMLInputElement;
export class CrawlModal extends Modal {
private plugin: LilbeePlugin;
constructor(app: App, plugin: LilbeePlugin) {
super(app);
this.plugin = plugin;
bindEscapeToClose(this);
}
onOpen(): void {
const { contentEl } = this;
contentEl.empty();
contentEl.addClass("lilbee-crawl-modal");
contentEl.createEl("h2", { text: MESSAGES.TITLE_CRAWL_WEB_PAGE });
const urlInput = contentEl.createEl("input", {
cls: "lilbee-crawl-url",
placeholder: MESSAGES.PLACEHOLDER_URL,
attr: { type: "text" },
});
const recursiveRow = contentEl.createDiv({ cls: "lilbee-crawl-recursive-row" });
const recursiveLabel = recursiveRow.createEl("label", { cls: "lilbee-crawl-recursive" });
const recursiveInput = recursiveLabel.createEl("input", {
cls: "lilbee-crawl-recursive-input",
attr: { type: "checkbox" },
});
asInput(recursiveInput).checked = false;
recursiveLabel.createSpan({ text: MESSAGES.LABEL_CRAWL_RECURSIVE });
const infoBtn = recursiveRow.createEl("button", {
cls: "lilbee-crawl-info-btn",
text: "i",
attr: {
type: "button",
"aria-label": MESSAGES.LABEL_CRAWL_RECURSIVE_INFO,
"aria-expanded": "false",
},
});
const notice = contentEl.createDiv({ cls: "lilbee-crawl-notice" });
notice.setAttribute("hidden", "hidden");
notice.textContent = MESSAGES.NOTICE_CRAWL_RECURSIVE;
let noticeOpen = false;
const setNoticeOpen = (open: boolean): void => {
noticeOpen = open;
if (open) {
notice.removeAttribute("hidden");
infoBtn.setAttribute("aria-expanded", "true");
} else {
notice.setAttribute("hidden", "hidden");
infoBtn.setAttribute("aria-expanded", "false");
}
};
infoBtn.addEventListener("click", () => setNoticeOpen(!noticeOpen));
const advanced = contentEl.createEl("details", { cls: "lilbee-crawl-advanced" });
advanced.createEl("summary", { text: MESSAGES.LABEL_CRAWL_ADVANCED });
const options = advanced.createDiv({ cls: "lilbee-crawl-options" });
const depthLabel = options.createEl("label", { text: MESSAGES.LABEL_DEPTH });
const depthInput = depthLabel.createEl("input", {
cls: "lilbee-crawl-depth",
placeholder: MESSAGES.HINT_CRAWL_BLANK_NO_LIMIT,
attr: { type: "number", min: "0" },
});
asInput(depthInput).value = "";
const maxLabel = options.createEl("label", { text: MESSAGES.LABEL_MAX_PAGES });
const maxInput = maxLabel.createEl("input", {
cls: "lilbee-crawl-max-pages",
placeholder: MESSAGES.HINT_CRAWL_BLANK_NO_LIMIT,
attr: { type: "number", min: "1" },
});
asInput(maxInput).value = "";
// Error element lives OUTSIDE the Advanced disclosure so it's visible
// even when the user collapses Advanced after typing bad input.
const errorEl = contentEl.createEl("div", { cls: "lilbee-crawl-error" });
const syncRecursiveState = (): void => {
const recursive = asInput(recursiveInput).checked;
asInput(depthInput).disabled = !recursive;
asInput(maxInput).disabled = !recursive;
advanced.style.display = recursive ? "" : "none";
infoBtn.style.display = recursive ? "" : "none";
if (!recursive) {
errorEl.textContent = "";
setNoticeOpen(false);
}
};
recursiveInput.addEventListener("change", syncRecursiveState);
syncRecursiveState();
const actions = contentEl.createDiv({ cls: "lilbee-crawl-actions" });
const crawlBtn = actions.createEl("button", { text: MESSAGES.BUTTON_CRAWL, cls: "mod-cta" });
crawlBtn.addEventListener("click", () => {
const raw = asInput(urlInput).value.trim();
if (!raw) {
new Notice(MESSAGES.NOTICE_ENTER_URL);
return;
}
const url = ensureUrlScheme(raw);
const recursive = asInput(recursiveInput).checked;
let depth: number | null;
let maxPages: number | null;
if (!recursive) {
depth = 0;
maxPages = null;
errorEl.textContent = "";
} else {
const depthRes = parseOptionalCount(asInput(depthInput).value, { allowZero: true });
const maxRes = parseOptionalCount(asInput(maxInput).value, { allowZero: false });
const err = maxRes.error ?? depthRes.error;
if (err) {
errorEl.textContent = err;
advanced.open = true;
return;
}
errorEl.textContent = "";
depth = depthRes.value;
maxPages = maxRes.value;
}
this.plugin.runCrawl(url, depth, maxPages);
this.close();
});
const cancelBtn = actions.createEl("button", { text: MESSAGES.BUTTON_CANCEL });
cancelBtn.addEventListener("click", () => this.close());
}
}
|