react-notion-x
Version:
Fast and accurate React renderer for Notion.
1,336 lines (1,308 loc) • 84.6 kB
JavaScript
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
// ../../node_modules/.pnpm/clipboard-copy@4.0.1/node_modules/clipboard-copy/index.js
var require_clipboard_copy = __commonJS({
"../../node_modules/.pnpm/clipboard-copy@4.0.1/node_modules/clipboard-copy/index.js"(exports, module) {
"use strict";
module.exports = clipboardCopy;
function makeError() {
return new DOMException("The request is not allowed", "NotAllowedError");
}
async function copyClipboardApi(text) {
if (!navigator.clipboard) {
throw makeError();
}
return navigator.clipboard.writeText(text);
}
async function copyExecCommand(text) {
const span = document.createElement("span");
span.textContent = text;
span.style.whiteSpace = "pre";
span.style.webkitUserSelect = "auto";
span.style.userSelect = "all";
document.body.appendChild(span);
const selection = window.getSelection();
const range = window.document.createRange();
selection.removeAllRanges();
range.selectNode(span);
selection.addRange(range);
let success = false;
try {
success = window.document.execCommand("copy");
} finally {
selection.removeAllRanges();
window.document.body.removeChild(span);
}
if (!success) throw makeError();
}
async function clipboardCopy(text) {
try {
await copyClipboardApi(text);
} catch (err) {
try {
await copyExecCommand(text);
} catch (err2) {
throw err2 || err || makeError();
}
}
}
}
});
// ../../node_modules/.pnpm/lodash.throttle@4.1.1/node_modules/lodash.throttle/index.js
var require_lodash = __commonJS({
"../../node_modules/.pnpm/lodash.throttle@4.1.1/node_modules/lodash.throttle/index.js"(exports, module) {
"use strict";
var FUNC_ERROR_TEXT = "Expected a function";
var NAN = 0 / 0;
var symbolTag = "[object Symbol]";
var reTrim = /^\s+|\s+$/g;
var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
var reIsBinary = /^0b[01]+$/i;
var reIsOctal = /^0o[0-7]+$/i;
var freeParseInt = parseInt;
var freeGlobal = typeof global == "object" && global && global.Object === Object && global;
var freeSelf = typeof self == "object" && self && self.Object === Object && self;
var root = freeGlobal || freeSelf || Function("return this")();
var objectProto = Object.prototype;
var objectToString = objectProto.toString;
var nativeMax = Math.max;
var nativeMin = Math.min;
var now = function() {
return root.Date.now();
};
function debounce(func, wait, options) {
var lastArgs, lastThis, maxWait, result, timerId, lastCallTime, lastInvokeTime = 0, leading = false, maxing = false, trailing = true;
if (typeof func != "function") {
throw new TypeError(FUNC_ERROR_TEXT);
}
wait = toNumber(wait) || 0;
if (isObject(options)) {
leading = !!options.leading;
maxing = "maxWait" in options;
maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
trailing = "trailing" in options ? !!options.trailing : trailing;
}
function invokeFunc(time) {
var args = lastArgs, thisArg = lastThis;
lastArgs = lastThis = void 0;
lastInvokeTime = time;
result = func.apply(thisArg, args);
return result;
}
function leadingEdge(time) {
lastInvokeTime = time;
timerId = setTimeout(timerExpired, wait);
return leading ? invokeFunc(time) : result;
}
function remainingWait(time) {
var timeSinceLastCall = time - lastCallTime, timeSinceLastInvoke = time - lastInvokeTime, result2 = wait - timeSinceLastCall;
return maxing ? nativeMin(result2, maxWait - timeSinceLastInvoke) : result2;
}
function shouldInvoke(time) {
var timeSinceLastCall = time - lastCallTime, timeSinceLastInvoke = time - lastInvokeTime;
return lastCallTime === void 0 || timeSinceLastCall >= wait || timeSinceLastCall < 0 || maxing && timeSinceLastInvoke >= maxWait;
}
function timerExpired() {
var time = now();
if (shouldInvoke(time)) {
return trailingEdge(time);
}
timerId = setTimeout(timerExpired, remainingWait(time));
}
function trailingEdge(time) {
timerId = void 0;
if (trailing && lastArgs) {
return invokeFunc(time);
}
lastArgs = lastThis = void 0;
return result;
}
function cancel() {
if (timerId !== void 0) {
clearTimeout(timerId);
}
lastInvokeTime = 0;
lastArgs = lastCallTime = lastThis = timerId = void 0;
}
function flush() {
return timerId === void 0 ? result : trailingEdge(now());
}
function debounced() {
var time = now(), isInvoking = shouldInvoke(time);
lastArgs = arguments;
lastThis = this;
lastCallTime = time;
if (isInvoking) {
if (timerId === void 0) {
return leadingEdge(lastCallTime);
}
if (maxing) {
timerId = setTimeout(timerExpired, wait);
return invokeFunc(lastCallTime);
}
}
if (timerId === void 0) {
timerId = setTimeout(timerExpired, wait);
}
return result;
}
debounced.cancel = cancel;
debounced.flush = flush;
return debounced;
}
function throttle2(func, wait, options) {
var leading = true, trailing = true;
if (typeof func != "function") {
throw new TypeError(FUNC_ERROR_TEXT);
}
if (isObject(options)) {
leading = "leading" in options ? !!options.leading : leading;
trailing = "trailing" in options ? !!options.trailing : trailing;
}
return debounce(func, wait, {
"leading": leading,
"maxWait": wait,
"trailing": trailing
});
}
function isObject(value) {
var type = typeof value;
return !!value && (type == "object" || type == "function");
}
function isObjectLike(value) {
return !!value && typeof value == "object";
}
function isSymbol(value) {
return typeof value == "symbol" || isObjectLike(value) && objectToString.call(value) == symbolTag;
}
function toNumber(value) {
if (typeof value == "number") {
return value;
}
if (isSymbol(value)) {
return NAN;
}
if (isObject(value)) {
var other = typeof value.valueOf == "function" ? value.valueOf() : value;
value = isObject(other) ? other + "" : other;
}
if (typeof value != "string") {
return value === 0 ? value : +value;
}
value = value.replace(reTrim, "");
var isBinary = reIsBinary.test(value);
return isBinary || reIsOctal.test(value) ? freeParseInt(value.slice(2), isBinary ? 2 : 8) : reIsBadHex.test(value) ? NAN : +value;
}
module.exports = throttle2;
}
});
// src/third-party/code.tsx
var import_clipboard_copy = __toESM(require_clipboard_copy(), 1);
import "prismjs";
import "prismjs/components/prism-clike.min.js";
import "prismjs/components/prism-css-extras.min.js";
import "prismjs/components/prism-css.min.js";
import "prismjs/components/prism-javascript.min.js";
import "prismjs/components/prism-js-extras.min.js";
import "prismjs/components/prism-json.min.js";
import "prismjs/components/prism-jsx.min.js";
import "prismjs/components/prism-tsx.min.js";
import "prismjs/components/prism-typescript.min.js";
import "notion-types";
import { getBlockTitle as getBlockTitle4 } from "notion-utils";
import prism from "prismjs";
import React16 from "react";
// src/components/text.tsx
import "notion-types";
import { getBlockValue, parsePageId as parsePageId2 } from "notion-utils";
import React15 from "react";
// src/context.tsx
import "notion-types";
import { defaultMapImageUrl, defaultMapPageUrl } from "notion-utils";
import React13 from "react";
// src/components/asset-wrapper.tsx
import "notion-types";
import { parsePageId } from "notion-utils";
// src/utils.ts
import { formatDate, formatNotionDateTime, isUrl } from "notion-utils";
var cs = (...classes) => classes.filter((a) => !!a).join(" ");
var getHashFragmentValue = (url) => {
return url.includes("#") ? url.replace(/^.+(#.+)$/, "$1") : "";
};
var isBrowser = !!globalThis.window;
var youtubeDomains = /* @__PURE__ */ new Set([
"youtu.be",
"youtube.com",
"www.youtube.com",
"youtube-nocookie.com",
"www.youtube-nocookie.com"
]);
var getYoutubeId = (url) => {
var _a;
try {
const { hostname } = new URL(url);
if (!youtubeDomains.has(hostname)) {
return null;
}
const regExp = /^.*(youtu\.be\/|v\/|u\/\w\/|embed\/|watch\?v=|&v=)([^#&?]*).*/i;
const match = url.match(regExp);
if (match && ((_a = match[2]) == null ? void 0 : _a.length) === 11) {
return match[2];
}
} catch (e) {
}
return null;
};
var getUrlParams = (url) => {
try {
const { searchParams } = new URL(url);
const result = {};
for (const [key, value] of searchParams.entries()) {
result[key] = value;
}
return result;
} catch (e) {
}
return;
};
// src/components/header.tsx
import { getPageBreadcrumbs } from "notion-utils";
import React9 from "react";
import { useHotkeys } from "react-hotkeys-hook";
// src/icons/search-icon.tsx
import "react";
import { jsx } from "react/jsx-runtime";
function SearchIcon(props) {
const { className, ...rest } = props;
return /* @__PURE__ */ jsx("svg", { className: cs("notion-icon", className), viewBox: "0 0 17 17", ...rest, children: /* @__PURE__ */ jsx("path", { d: "M6.78027 13.6729C8.24805 13.6729 9.60156 13.1982 10.709 12.4072L14.875 16.5732C15.0684 16.7666 15.3232 16.8633 15.5957 16.8633C16.167 16.8633 16.5713 16.4238 16.5713 15.8613C16.5713 15.5977 16.4834 15.3516 16.29 15.1582L12.1504 11.0098C13.0205 9.86719 13.5391 8.45215 13.5391 6.91406C13.5391 3.19629 10.498 0.155273 6.78027 0.155273C3.0625 0.155273 0.0214844 3.19629 0.0214844 6.91406C0.0214844 10.6318 3.0625 13.6729 6.78027 13.6729ZM6.78027 12.2139C3.87988 12.2139 1.48047 9.81445 1.48047 6.91406C1.48047 4.01367 3.87988 1.61426 6.78027 1.61426C9.68066 1.61426 12.0801 4.01367 12.0801 6.91406C12.0801 9.81445 9.68066 12.2139 6.78027 12.2139Z" }) });
}
// src/components/page-icon.tsx
import "notion-types";
import { getBlockIcon, getBlockTitle } from "notion-utils";
import React4 from "react";
// src/icons/default-page-icon.tsx
import "react";
import { jsx as jsx2 } from "react/jsx-runtime";
function DefaultPageIcon(props) {
const { className, ...rest } = props;
return /* @__PURE__ */ jsx2("svg", { className, ...rest, viewBox: "0 0 30 30", width: "16", children: /* @__PURE__ */ jsx2("path", { d: "M16,1H4v28h22V11L16,1z M16,3.828L23.172,11H16V3.828z M24,27H6V3h8v10h10V27z M8,17h14v-2H8V17z M8,21h14v-2H8V21z M8,25h14v-2H8V25z" }) });
}
// src/components/lazy-image.tsx
import { normalizeUrl } from "notion-utils";
import React3 from "react";
// src/components/lazy-image-full.tsx
import { Component } from "react";
import { InView } from "react-intersection-observer";
import { ofType, unionize } from "unionize";
import { jsx as jsx3 } from "react/jsx-runtime";
var LazyImageFullState = unionize({
NotAsked: {},
Buffering: {},
// Could try to make it Promise<HTMLImageElement>,
// but we don't use the element anyway, and we cache promises
Loading: {},
LoadSuccess: {},
LoadError: ofType()
});
var Action = unionize({
ViewChanged: ofType(),
BufferingEnded: {},
// MAYBE: Load: {},
LoadSuccess: {},
LoadError: ofType()
});
var getBufferingCmd = (durationMs) => (instance) => {
const bufferingPromise = makeCancelable(delayedPromise(durationMs));
bufferingPromise.promise.then(() => instance.update(Action.BufferingEnded())).catch(
(_err) => {
}
//console.log({ isCanceled: _reason.isCanceled })
);
instance.promiseCache.buffering = bufferingPromise;
};
var getLoadingCmd = (imageProps, experimentalDecode) => (instance) => {
const loadingPromise = makeCancelable(
loadImage(imageProps, experimentalDecode)
);
loadingPromise.promise.then((_res) => instance.update(Action.LoadSuccess({}))).catch((err) => {
if (!err.isCanceled) {
instance.update(new Action.LoadError({ msg: "LoadError" }));
}
});
instance.promiseCache.loading = loadingPromise;
};
var cancelBufferingCmd = (instance) => {
var _a;
(_a = instance.promiseCache.buffering) == null ? void 0 : _a.cancel();
};
var _LazyImageFull = class _LazyImageFull extends Component {
constructor(props) {
super(props);
/** A central place to store promises.
* A bit silly, but passing promises directly in the state
* was giving me weird timing issues. This way we can keep
* the promises in check, and pick them up from the respective methods.
* FUTURE: Could pass the relevant key in Buffering and Loading, so
* that at least we know where they are from a single source.
*/
__publicField(this, "promiseCache", {});
__publicField(this, "initialState", LazyImageFullState.NotAsked());
this.state = this.initialState;
this.update = this.update.bind(this);
}
/** Emit the next state based on actions.
* This is the core of the component!
*/
static reducer(action, prevState, props) {
return Action.match(action, {
ViewChanged: ({ inView }) => {
if (inView === true) {
if (!props.src) {
return { nextState: LazyImageFullState.LoadSuccess() };
} else {
return LazyImageFullState.match(prevState, {
NotAsked: () => {
if (props.debounceDurationMs) {
return {
nextState: LazyImageFullState.Buffering(),
cmd: getBufferingCmd(props.debounceDurationMs)
};
} else {
return {
nextState: LazyImageFullState.Loading(),
cmd: getLoadingCmd(props, props.experimentalDecode)
};
}
},
// Do nothing in other states
default: () => ({ nextState: prevState })
});
}
} else {
return LazyImageFullState.match(prevState, {
Buffering: () => ({
nextState: LazyImageFullState.NotAsked(),
cmd: cancelBufferingCmd
}),
// Do nothing in other states
default: () => ({ nextState: prevState })
});
}
},
// Buffering has ended/succeeded, kick off request for image
BufferingEnded: () => ({
nextState: LazyImageFullState.Loading(),
cmd: getLoadingCmd(props, props.experimentalDecode)
}),
// Loading the image succeeded, simple
LoadSuccess: () => ({ nextState: LazyImageFullState.LoadSuccess() }),
//@ts-expect-error No need for changing structure
LoadError: (e) => ({ nextState: new LazyImageFullState.LoadError(e) })
});
}
update(action) {
const { nextState, cmd } = _LazyImageFull.reducer(
action,
this.state,
this.props
);
if (this.props.debugActions) {
if (false) {
console.warn(
'You are running LazyImage with debugActions="true" in production. This might have performance implications.'
);
}
console.log({ action, prevState: this.state, nextState });
}
this.setState(nextState, () => cmd && cmd(this));
}
componentWillUnmount() {
if (this.promiseCache.loading) {
this.promiseCache.loading.cancel();
}
if (this.promiseCache.buffering) {
this.promiseCache.buffering.cancel();
}
this.promiseCache = {};
}
// Render function
render() {
const { children, loadEagerly, observerProps, ...imageProps } = this.props;
if (loadEagerly) {
return children({
// We know that the state tags and the enum match up
imageState: LazyImageFullState.LoadSuccess().tag,
imageProps
});
} else {
return /* @__PURE__ */ jsx3(
InView,
{
rootMargin: "50px 0px",
threshold: 0.01,
...observerProps,
onChange: (inView) => this.update(Action.ViewChanged({ inView })),
children: ({ ref }) => children({
// We know that the state tags and the enum match up, apart
// from Buffering not being exposed
imageState: this.state.tag === "Buffering" ? "Loading" /* Loading */ : this.state.tag,
imageProps,
ref
})
}
);
}
}
};
__publicField(_LazyImageFull, "displayName", "LazyImageFull");
var LazyImageFull = _LazyImageFull;
var loadImage = ({ src, srcSet, alt, sizes }, experimentalDecode = false) => new Promise((resolve, reject) => {
const image = new Image();
if (srcSet) {
image.srcset = srcSet;
}
if (alt) {
image.alt = alt;
}
if (sizes) {
image.sizes = sizes;
}
image.src = src;
if (experimentalDecode && "decode" in image) {
return image.decode().then(() => resolve(image)).catch((err) => reject(err));
}
image.addEventListener("load", resolve);
image.addEventListener("error", reject);
});
var delayedPromise = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
var makeCancelable = (promise) => {
let hasCanceled_ = false;
const wrappedPromise = new Promise((resolve, reject) => {
void promise.then(
(val) => hasCanceled_ ? reject({ isCanceled: true }) : resolve(val)
);
promise.catch(
(err) => hasCanceled_ ? reject({ isCanceled: true }) : reject(err)
);
});
return {
promise: wrappedPromise,
cancel() {
hasCanceled_ = true;
}
};
};
// src/components/lazy-image.tsx
import { jsx as jsx4, jsxs } from "react/jsx-runtime";
function LazyImage({
src,
alt,
className,
style,
zoomable = false,
priority = false,
height,
...rest
}) {
var _a, _b, _c;
const { recordMap, zoom, previewImages, forceCustomImages, components } = useNotionContext();
const zoomRef = React3.useRef(zoom ? zoom.clone() : null);
const previewImage = previewImages ? (_c = (_a = recordMap == null ? void 0 : recordMap.preview_images) == null ? void 0 : _a[src]) != null ? _c : (_b = recordMap == null ? void 0 : recordMap.preview_images) == null ? void 0 : _b[normalizeUrl(src)] : null;
const onLoad = React3.useCallback(
(e) => {
if (zoomable && (e.target.src || e.target.srcset)) {
if (zoomRef.current) {
;
zoomRef.current.attach(e.target);
}
}
},
[zoomRef, zoomable]
);
const attachZoom = React3.useCallback(
(image) => {
if (zoomRef.current && image) {
;
zoomRef.current.attach(image);
}
},
[zoomRef]
);
const attachZoomRef = React3.useMemo(
() => zoomable ? attachZoom : void 0,
[zoomable, attachZoom]
);
if (previewImage) {
const aspectRatio = previewImage.originalHeight / previewImage.originalWidth;
if (components.Image) {
return /* @__PURE__ */ jsx4(
components.Image,
{
src,
alt,
style,
className,
width: previewImage.originalWidth,
height: previewImage.originalHeight,
blurDataURL: previewImage.dataURIBase64,
placeholder: "blur",
priority,
onLoad
}
);
}
return /* @__PURE__ */ jsx4(LazyImageFull, { src, ...rest, experimentalDecode: true, children: ({ imageState, ref }) => {
const isLoaded = imageState === "LoadSuccess" /* LoadSuccess */;
const wrapperStyle = {
width: "100%"
};
const imgStyle = {};
if (height) {
wrapperStyle.height = height;
} else {
imgStyle.position = "absolute";
wrapperStyle.paddingBottom = `${aspectRatio * 100}%`;
}
return /* @__PURE__ */ jsxs(
"div",
{
className: cs(
"lazy-image-wrapper",
isLoaded && "lazy-image-loaded",
className
),
style: wrapperStyle,
children: [
/* @__PURE__ */ jsx4(
"img",
{
className: "lazy-image-preview",
src: previewImage.dataURIBase64,
alt,
ref,
style,
decoding: "async"
}
),
/* @__PURE__ */ jsx4(
"img",
{
className: "lazy-image-real",
src,
alt,
ref: attachZoomRef,
style: {
...style,
...imgStyle
},
width: previewImage.originalWidth,
height: previewImage.originalHeight,
decoding: "async",
loading: "lazy"
}
)
]
}
);
} });
} else {
if (components.Image && forceCustomImages) {
return /* @__PURE__ */ jsx4(
components.Image,
{
src,
alt,
className,
style,
width: null,
height: height || null,
priority,
onLoad
}
);
}
return /* @__PURE__ */ jsx4(
"img",
{
className,
style,
src,
alt,
ref: attachZoomRef,
loading: "lazy",
decoding: "async",
...rest
}
);
}
}
// src/components/page-icon.tsx
import { jsx as jsx5 } from "react/jsx-runtime";
var isIconBlock = (value) => {
return value.type === "page" || value.type === "callout" || value.type === "collection_view" || value.type === "collection_view_page";
};
function PageIconImpl({
block,
className,
inline = true,
hideDefaultIcon = false,
defaultIcon
}) {
var _a;
const { mapImageUrl, recordMap, darkMode } = useNotionContext();
let isImage = false;
let content = null;
if (isIconBlock(block)) {
const icon = ((_a = getBlockIcon(block, recordMap)) == null ? void 0 : _a.trim()) || defaultIcon;
const title = getBlockTitle(block, recordMap);
if (icon && isUrl(icon)) {
const url = mapImageUrl(icon, block);
isImage = true;
content = /* @__PURE__ */ jsx5(
LazyImage,
{
src: url,
alt: title || "page icon",
className: cs(className, "notion-page-icon")
}
);
} else if (icon && icon.startsWith("/icons/")) {
const url = "https://www.notion.so" + icon + "?mode=" + (darkMode ? "dark" : "light");
content = /* @__PURE__ */ jsx5(
LazyImage,
{
src: url,
alt: title || "page icon",
className: cs(className, "notion-page-icon")
}
);
} else if (!icon) {
if (!hideDefaultIcon) {
isImage = true;
content = /* @__PURE__ */ jsx5(
DefaultPageIcon,
{
className: cs(className, "notion-page-icon"),
alt: title || "page icon"
}
);
}
} else {
isImage = false;
content = /* @__PURE__ */ jsx5(
"span",
{
className: cs(className, "notion-page-icon"),
role: "img",
"aria-label": icon,
children: icon
}
);
}
}
if (!content) {
return null;
}
return /* @__PURE__ */ jsx5(
"div",
{
className: cs(
inline ? "notion-page-icon-inline" : "notion-page-icon-hero",
isImage ? "notion-page-icon-image" : "notion-page-icon-span"
),
children: content
}
);
}
var PageIcon = React4.memo(PageIconImpl);
// src/components/search-dialog.tsx
var import_lodash = __toESM(require_lodash(), 1);
import { getBlockParentPage, getBlockTitle as getBlockTitle3 } from "notion-utils";
import React8 from "react";
// src/icons/clear-icon.tsx
import "react";
import { jsx as jsx6 } from "react/jsx-runtime";
function ClearIcon(props) {
const { className, ...rest } = props;
return /* @__PURE__ */ jsx6("svg", { className: cs("notion-icon", className), ...rest, viewBox: "0 0 30 30", children: /* @__PURE__ */ jsx6("path", { d: "M15,0C6.716,0,0,6.716,0,15s6.716,15,15,15s15-6.716,15-15S23.284,0,15,0z M22,20.6L20.6,22L15,16.4L9.4,22L8,20.6l5.6-5.6 L8,9.4L9.4,8l5.6,5.6L20.6,8L22,9.4L16.4,15L22,20.6z" }) });
}
// src/icons/loading-icon.tsx
import "react";
import { jsx as jsx7, jsxs as jsxs2 } from "react/jsx-runtime";
function LoadingIcon(props) {
const { className, ...rest } = props;
return /* @__PURE__ */ jsxs2("svg", { className: cs("notion-icon", className), ...rest, viewBox: "0 0 24 24", children: [
/* @__PURE__ */ jsx7("defs", { children: /* @__PURE__ */ jsxs2(
"linearGradient",
{
x1: "28.1542969%",
y1: "63.7402344%",
x2: "74.6289062%",
y2: "17.7832031%",
id: "linearGradient-1",
children: [
/* @__PURE__ */ jsx7("stop", { stopColor: "rgba(164, 164, 164, 1)", offset: "0%" }),
/* @__PURE__ */ jsx7(
"stop",
{
stopColor: "rgba(164, 164, 164, 0)",
stopOpacity: "0",
offset: "100%"
}
)
]
}
) }),
/* @__PURE__ */ jsx7("g", { id: "Page-1", stroke: "none", strokeWidth: "1", fill: "none", children: /* @__PURE__ */ jsx7("g", { transform: "translate(-236.000000, -286.000000)", children: /* @__PURE__ */ jsxs2("g", { transform: "translate(238.000000, 286.000000)", children: [
/* @__PURE__ */ jsx7(
"circle",
{
id: "Oval-2",
stroke: "url(#linearGradient-1)",
strokeWidth: "4",
cx: "10",
cy: "12",
r: "10"
}
),
/* @__PURE__ */ jsx7(
"path",
{
d: "M10,2 C4.4771525,2 0,6.4771525 0,12",
id: "Oval-2",
stroke: "rgba(164, 164, 164, 1)",
strokeWidth: "4"
}
),
/* @__PURE__ */ jsx7(
"rect",
{
id: "Rectangle-1",
fill: "rgba(164, 164, 164, 1)",
x: "8",
y: "0",
width: "4",
height: "4",
rx: "8"
}
)
] }) }) })
] });
}
// src/components/page-title.tsx
import "notion-types";
import { getBlockTitle as getBlockTitle2 } from "notion-utils";
import React7 from "react";
import { jsx as jsx8, jsxs as jsxs3 } from "react/jsx-runtime";
function PageTitleImpl({
block,
className,
defaultIcon,
...rest
}) {
var _a, _b;
const { recordMap } = useNotionContext();
if (!block) return null;
if (block.type === "collection_view_page" || block.type === "collection_view") {
const title = getBlockTitle2(block, recordMap);
if (!title) {
return null;
}
const titleDecoration = [[title]];
return /* @__PURE__ */ jsxs3("span", { className: cs("notion-page-title", className), ...rest, children: [
/* @__PURE__ */ jsx8(
PageIcon,
{
block,
defaultIcon,
className: "notion-page-title-icon"
}
),
/* @__PURE__ */ jsx8("span", { className: "notion-page-title-text", children: /* @__PURE__ */ jsx8(Text, { value: titleDecoration, block }) })
] });
}
if (!((_a = block.properties) == null ? void 0 : _a.title)) {
return null;
}
return /* @__PURE__ */ jsxs3("span", { className: cs("notion-page-title", className), ...rest, children: [
/* @__PURE__ */ jsx8(
PageIcon,
{
block,
defaultIcon,
className: "notion-page-title-icon"
}
),
/* @__PURE__ */ jsx8("span", { className: "notion-page-title-text", children: /* @__PURE__ */ jsx8(Text, { value: (_b = block.properties) == null ? void 0 : _b.title, block }) })
] });
}
var PageTitle = React7.memo(PageTitleImpl);
// src/components/search-dialog.tsx
import { Fragment, jsx as jsx9, jsxs as jsxs4 } from "react/jsx-runtime";
var SearchDialog = class extends React8.Component {
constructor(props) {
super(props);
__publicField(this, "state", {
isLoading: false,
query: "",
searchResult: null,
searchError: null
});
__publicField(this, "_inputRef");
__publicField(this, "_search");
__publicField(this, "_onAfterOpen", () => {
if (this._inputRef.current) {
this._inputRef.current.focus();
}
});
__publicField(this, "_onChangeQuery", (e) => {
const query = e.target.value;
this.setState({ query });
if (!query.trim()) {
this.setState({ isLoading: false, searchResult: null, searchError: null });
return;
} else {
this._search();
}
});
__publicField(this, "_onClearQuery", () => {
this._onChangeQuery({ target: { value: "" } });
});
__publicField(this, "_warmupSearch", async () => {
const { searchNotion, rootBlockId } = this.props;
await searchNotion({
query: "",
ancestorId: rootBlockId
});
});
__publicField(this, "_searchImpl", async () => {
const { searchNotion, rootBlockId } = this.props;
const { query } = this.state;
if (!query.trim()) {
this.setState({ isLoading: false, searchResult: null, searchError: null });
return;
}
this.setState({ isLoading: true });
const result = await searchNotion({
query,
ancestorId: rootBlockId
});
console.log("search", query, result);
let searchResult = null;
let searchError = null;
if (result.error || result.errorId) {
searchError = result;
} else {
searchResult = { ...result };
const results = searchResult.results.map((result2) => {
var _a, _b;
const block = (_a = searchResult.recordMap.block[result2.id]) == null ? void 0 : _a.value;
if (!block) return;
const title = getBlockTitle3(block, searchResult.recordMap);
if (!title) {
return;
}
result2.title = title;
result2.block = block;
result2.recordMap = searchResult.recordMap;
result2.page = getBlockParentPage(block, searchResult.recordMap, {
inclusive: true
}) || block;
if (!result2.page.id) {
return;
}
if ((_b = result2.highlight) == null ? void 0 : _b.text) {
result2.highlight.html = result2.highlight.text.replaceAll(/<gzknfouu>/gi, "<b>").replaceAll(/<\/gzknfouu>/gi, "</b>");
}
return result2;
}).filter(Boolean);
const searchResultsMap = Object.fromEntries(
results.map((result2) => [result2.page.id, result2])
);
searchResult.results = Object.values(searchResultsMap);
}
if (this.state.query === query) {
this.setState({ isLoading: false, searchResult, searchError });
}
});
this._inputRef = React8.createRef();
}
componentDidMount() {
this._search = (0, import_lodash.default)(this._searchImpl.bind(this), 1e3);
void this._warmupSearch();
}
render() {
const { isOpen, onClose } = this.props;
const { isLoading, query, searchResult, searchError } = this.state;
const hasQuery = !!query.trim();
return /* @__PURE__ */ jsx9(NotionContextConsumer, { children: (ctx2) => {
const { components, defaultPageIcon, mapPageUrl } = ctx2;
return /* @__PURE__ */ jsx9(
components.Modal,
{
isOpen,
contentLabel: "Search",
className: "notion-search",
overlayClassName: "notion-search-overlay",
onRequestClose: onClose,
onAfterOpen: this._onAfterOpen,
children: /* @__PURE__ */ jsxs4("div", { className: "quickFindMenu", children: [
/* @__PURE__ */ jsxs4("div", { className: "searchBar", children: [
/* @__PURE__ */ jsx9("div", { className: "inlineIcon", children: isLoading ? /* @__PURE__ */ jsx9(LoadingIcon, { className: "loadingIcon" }) : /* @__PURE__ */ jsx9(SearchIcon, {}) }),
/* @__PURE__ */ jsx9(
"input",
{
className: "searchInput",
placeholder: "Search",
value: query,
ref: this._inputRef,
onChange: this._onChangeQuery
}
),
query && /* @__PURE__ */ jsx9(
"div",
{
role: "button",
className: "clearButton",
onClick: this._onClearQuery,
children: /* @__PURE__ */ jsx9(ClearIcon, { className: "clearIcon" })
}
)
] }),
hasQuery && searchResult && /* @__PURE__ */ jsx9(Fragment, { children: searchResult.results.length ? /* @__PURE__ */ jsxs4(
NotionContextProvider,
{
...ctx2,
recordMap: searchResult.recordMap,
children: [
/* @__PURE__ */ jsx9("div", { className: "resultsPane", children: searchResult.results.map((result) => {
var _a;
return /* @__PURE__ */ jsxs4(
components.PageLink,
{
className: cs("result", "notion-page-link"),
href: mapPageUrl(
result.page.id,
// TODO
searchResult.recordMap
),
children: [
/* @__PURE__ */ jsx9(
PageTitle,
{
block: result.page,
defaultIcon: defaultPageIcon
}
),
((_a = result.highlight) == null ? void 0 : _a.html) && /* @__PURE__ */ jsx9(
"div",
{
className: "notion-search-result-highlight",
dangerouslySetInnerHTML: {
__html: result.highlight.html
}
}
)
]
},
result.id
);
}) }),
/* @__PURE__ */ jsx9("footer", { className: "resultsFooter", children: /* @__PURE__ */ jsxs4("div", { children: [
/* @__PURE__ */ jsx9("span", { className: "resultsCount", children: searchResult.total }),
searchResult.total === 1 ? " result" : " results"
] }) })
]
}
) : /* @__PURE__ */ jsxs4("div", { className: "noResultsPane", children: [
/* @__PURE__ */ jsx9("div", { className: "noResults", children: "No results" }),
/* @__PURE__ */ jsx9("div", { className: "noResultsDetail", children: "Try different search terms" })
] }) }),
hasQuery && !searchResult && searchError && /* @__PURE__ */ jsx9("div", { className: "noResultsPane", children: /* @__PURE__ */ jsx9("div", { className: "noResults", children: "Search error" }) })
] })
}
);
} });
}
};
// src/components/header.tsx
import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
function Header({
block
}) {
return /* @__PURE__ */ jsx10("header", { className: "notion-header", children: /* @__PURE__ */ jsxs5("div", { className: "notion-nav-header", children: [
/* @__PURE__ */ jsx10(Breadcrumbs, { block }),
/* @__PURE__ */ jsx10(Search, { block })
] }) });
}
function Breadcrumbs({
block,
rootOnly = false
}) {
const { recordMap, mapPageUrl, components } = useNotionContext();
const breadcrumbs = React9.useMemo(() => {
const tempBreadcrumbs = getPageBreadcrumbs(recordMap, block.id);
if (rootOnly) {
return [tempBreadcrumbs == null ? void 0 : tempBreadcrumbs[0]].filter(Boolean);
}
return tempBreadcrumbs;
}, [recordMap, block.id, rootOnly]);
return /* @__PURE__ */ jsx10("div", { className: "breadcrumbs", children: breadcrumbs == null ? void 0 : breadcrumbs.map((breadcrumb, index) => {
if (!breadcrumb) {
return null;
}
const pageLinkProps = {};
const componentMap = {
pageLink: components.PageLink
};
if (breadcrumb.active) {
componentMap.pageLink = (props) => /* @__PURE__ */ jsx10("div", { ...props });
} else {
pageLinkProps.href = mapPageUrl(breadcrumb.pageId);
}
return /* @__PURE__ */ jsxs5(React9.Fragment, { children: [
/* @__PURE__ */ jsxs5(
componentMap.pageLink,
{
className: cs("breadcrumb", breadcrumb.active && "active"),
...pageLinkProps,
children: [
breadcrumb.icon && /* @__PURE__ */ jsx10(PageIcon, { className: "icon", block: breadcrumb.block }),
breadcrumb.title && /* @__PURE__ */ jsx10("span", { className: "title", children: breadcrumb.title })
]
}
),
index < breadcrumbs.length - 1 && /* @__PURE__ */ jsx10("span", { className: "spacer", children: "/" })
] }, breadcrumb.pageId);
}) }, "breadcrumbs");
}
function Search({
block,
search,
title = "Search"
}) {
const { searchNotion, rootPageId, isShowingSearch, onHideSearch } = useNotionContext();
const onSearchNotion = search || searchNotion;
const [isSearchOpen, setIsSearchOpen] = React9.useState(isShowingSearch);
React9.useEffect(() => {
setIsSearchOpen(isShowingSearch);
}, [isShowingSearch]);
const onOpenSearch = React9.useCallback(() => {
setIsSearchOpen(true);
}, []);
const onCloseSearch = React9.useCallback(() => {
setIsSearchOpen(false);
if (onHideSearch) {
onHideSearch();
}
}, [onHideSearch]);
useHotkeys("cmd+p", (event) => {
onOpenSearch();
event.preventDefault();
event.stopPropagation();
});
useHotkeys("cmd+k", (event) => {
onOpenSearch();
event.preventDefault();
event.stopPropagation();
});
const hasSearch = !!onSearchNotion;
return /* @__PURE__ */ jsxs5(Fragment2, { children: [
hasSearch && /* @__PURE__ */ jsxs5(
"div",
{
role: "button",
className: cs("breadcrumb", "button", "notion-search-button"),
onClick: onOpenSearch,
children: [
/* @__PURE__ */ jsx10(SearchIcon, { className: "searchIcon" }),
title && /* @__PURE__ */ jsx10("span", { className: "title", children: title })
]
}
),
isSearchOpen && hasSearch && /* @__PURE__ */ jsx10(
SearchDialog,
{
isOpen: isSearchOpen,
rootBlockId: rootPageId || (block == null ? void 0 : block.id),
onClose: onCloseSearch,
searchNotion: onSearchNotion
}
)
] });
}
// src/components/eoi.tsx
import "notion-types";
// src/icons/type-github.tsx
import { jsx as jsx11 } from "react/jsx-runtime";
function SvgTypeGitHub(props) {
return /* @__PURE__ */ jsx11("svg", { viewBox: "0 0 260 260", ...props, children: /* @__PURE__ */ jsx11("g", { children: /* @__PURE__ */ jsx11(
"path",
{
d: "M128.00106,0 C57.3172926,0 0,57.3066942 0,128.00106 C0,184.555281 36.6761997,232.535542 87.534937,249.460899 C93.9320223,250.645779 96.280588,246.684165 96.280588,243.303333 C96.280588,240.251045 96.1618878,230.167899 96.106777,219.472176 C60.4967585,227.215235 52.9826207,204.369712 52.9826207,204.369712 C47.1599584,189.574598 38.770408,185.640538 38.770408,185.640538 C27.1568785,177.696113 39.6458206,177.859325 39.6458206,177.859325 C52.4993419,178.762293 59.267365,191.04987 59.267365,191.04987 C70.6837675,210.618423 89.2115753,204.961093 96.5158685,201.690482 C97.6647155,193.417512 100.981959,187.77078 104.642583,184.574357 C76.211799,181.33766 46.324819,170.362144 46.324819,121.315702 C46.324819,107.340889 51.3250588,95.9223682 59.5132437,86.9583937 C58.1842268,83.7344152 53.8029229,70.715562 60.7532354,53.0843636 C60.7532354,53.0843636 71.5019501,49.6441813 95.9626412,66.2049595 C106.172967,63.368876 117.123047,61.9465949 128.00106,61.8978432 C138.879073,61.9465949 149.837632,63.368876 160.067033,66.2049595 C184.49805,49.6441813 195.231926,53.0843636 195.231926,53.0843636 C202.199197,70.715562 197.815773,83.7344152 196.486756,86.9583937 C204.694018,95.9223682 209.660343,107.340889 209.660343,121.315702 C209.660343,170.478725 179.716133,181.303747 151.213281,184.472614 C155.80443,188.444828 159.895342,196.234518 159.895342,208.176593 C159.895342,225.303317 159.746968,239.087361 159.746968,243.303333 C159.746968,246.709601 162.05102,250.70089 168.53925,249.443941 C219.370432,232.499507 256,184.536204 256,128.00106 C256,57.3066942 198.691187,0 128.00106,0 Z M47.9405593,182.340212 C47.6586465,182.976105 46.6581745,183.166873 45.7467277,182.730227 C44.8183235,182.312656 44.2968914,181.445722 44.5978808,180.80771 C44.8734344,180.152739 45.876026,179.97045 46.8023103,180.409216 C47.7328342,180.826786 48.2627451,181.702199 47.9405593,182.340212 Z M54.2367892,187.958254 C53.6263318,188.524199 52.4329723,188.261363 51.6232682,187.366874 C50.7860088,186.474504 50.6291553,185.281144 51.2480912,184.70672 C51.8776254,184.140775 53.0349512,184.405731 53.8743302,185.298101 C54.7115892,186.201069 54.8748019,187.38595 54.2367892,187.958254 Z M58.5562413,195.146347 C57.7719732,195.691096 56.4895886,195.180261 55.6968417,194.042013 C54.9125733,192.903764 54.9125733,191.538713 55.713799,190.991845 C56.5086651,190.444977 57.7719732,190.936735 58.5753181,192.066505 C59.3574669,193.22383 59.3574669,194.58888 58.5562413,195.146347 Z M65.8613592,203.471174 C65.1597571,204.244846 63.6654083,204.03712 62.5716717,202.981538 C61.4524999,201.94927 61.1409122,200.484596 61.8446341,199.710926 C62.5547146,198.935137 64.0575422,199.15346 65.1597571,200.200564 C66.2704506,201.230712 66.6095936,202.705984 65.8613592,203.471174 Z M75.3025151,206.281542 C74.9930474,207.284134 73.553809,207.739857 72.1039724,207.313809 C70.6562556,206.875043 69.7087748,205.700761 70.0012857,204.687571 C70.302275,203.678621 71.7478721,203.20382 73.2083069,203.659543 C74.6539041,204.09619 75.6035048,205.261994 75.3025151,206.281542 Z M86.046947,207.473627 C86.0829806,208.529209 84.8535871,209.404622 83.3316829,209.4237 C81.8013,209.457614 80.563428,208.603398 80.5464708,207.564772 C80.5464708,206.498591 81.7483088,205.631657 83.2786917,205.606221 C84.8005962,205.576546 86.046947,206.424403 86.046947,207.473627 Z M96.6021471,207.069023 C96.7844366,208.099171 95.7267341,209.156872 94.215428,209.438785 C92.7295577,209.710099 91.3539086,209.074206 91.1652603,208.052538 C90.9808515,206.996955 92.0576306,205.939253 93.5413813,205.66582 C95.054807,205.402984 96.4092596,206.021919 96.6021471,207.069023 Z",
fill: "#161614"
}
) }) });
}
var type_github_default = SvgTypeGitHub;
// src/components/mention-preview-card.tsx
import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
function capitalizeFirstLetter(str) {
if (!str) return "";
return str.charAt(0).toUpperCase() + str.slice(1);
}
function MentionPreviewCard({
owner,
lastUpdated,
externalImage,
title,
domain
}) {
return /* @__PURE__ */ jsxs6("div", { className: "notion-external-subtitle", children: [
externalImage && /* @__PURE__ */ jsxs6("div", { className: "notion-preview-card-domain-warp", children: [
/* @__PURE__ */ jsx12("div", { className: "notion-preview-card-logo", children: externalImage }),
/* @__PURE__ */ jsx12("div", { className: "notion-preview-card-domain", children: capitalizeFirstLetter(domain.split(".")[0]) })
] }),
/* @__PURE__ */ jsx12("div", { className: "notion-preview-card-title", children: title }),
owner && /* @__PURE__ */ jsxs6("div", { className: "notion-external-subtitle-item", children: [
/* @__PURE__ */ jsx12("div", { className: "notion-external-subtitle-item-name", children: "Owner" }),
/* @__PURE__ */ jsx12("span", { className: "notion-external-subtitle-item-desc", children: owner })
] }),
lastUpdated && /* @__PURE__ */ jsxs6("div", { className: "notion-external-subtitle-item", children: [
/* @__PURE__ */ jsx12("div", { className: "notion-external-subtitle-item-name", children: "Updated" }),
/* @__PURE__ */ jsx12("span", { className: "notion-external-subtitle-item-desc", children: lastUpdated })
] }),
domain === "github.com" && /* @__PURE__ */ jsxs6("div", { className: "notion-preview-card-github-shields", children: [
/* @__PURE__ */ jsx12(
"img",
{
src: `https://img.shields.io/github/stars/${owner}/${title}?logo=github`,
alt: ""
}
),
/* @__PURE__ */ jsx12(
"img",
{
src: `https://img.shields.io/github/last-commit/${owner}/${title}`,
alt: ""
}
)
] })
] });
}
// src/components/eoi.tsx
import { jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
function EOI({
block,
inline,
className
}) {
var _a, _b, _c;
const { components } = useNotionContext();
const { original_url, attributes, domain } = (block == null ? void 0 : block.format) || {};
if (!original_url || !attributes) {
return null;
}
const title = (_a = attributes.find((attr) => attr.id === "title")) == null ? void 0 : _a.values[0];
let owner = (_b = attributes.find((attr) => attr.id === "owner")) == null ? void 0 : _b.values[0];
const lastUpdatedAt = (_c = attributes.find((attr) => attr.id === "updated_at")) == null ? void 0 : _c.values[0];
const lastUpdated = lastUpdatedAt ? formatNotionDateTime(lastUpdatedAt) : null;
let externalImage;
switch (domain) {
case "github.com":
externalImage = /* @__PURE__ */ jsx13(type_github_default, {});
if (owner) {
const parts = owner.split("/");
owner = parts.at(-1);
}
break;
default:
if (true) {
console.log(
`Unsupported external_object_instance domain "${domain}"`,
JSON.stringify(block, null, 2)
);
}
return null;
}
return /* @__PURE__ */ jsxs7(
components.Link,
{
target: "_blank",
rel: "noopener noreferrer",
href: original_url,
className: cs(
"notion-external",
inline ? "notion-external-mention" : "notion-external-block notion-row",
className
),
children: [
externalImage && /* @__PURE__ */ jsx13("div", { className: "notion-external-image", children: externalImage }),
/* @__PURE__ */ jsxs7("div", { className: "notion-external-description", children: [
/* @__PURE__ */ jsx13("div", { className: "notion-external-title", children: title }),
!inline && owner ? /* @__PURE__ */ jsxs7("div", { className: "notion-external-block-desc", children: [
owner,
lastUpdated && /* @__PURE__ */ jsx13("span", { children: " \u2022 " }),
lastUpdated && `Updated ${lastUpdated}`
] }) : null,
inline && (owner || lastUpdated) && /* @__PURE__ */ jsx13(
MentionPreviewCard,
{
title,
owner,
lastUpdated,
domain,
externalImage
}
)
] })
]
}
);
}
// src/components/graceful-image.tsx
import "react";
import { Img } from "react-image";
import { jsx as jsx14 } from "react/jsx-runtime";
function GracefulImage(props) {
if (isBrowser) {
return /* @__PURE__ */ jsx14(Img, { ...props });
} else {
return /* @__PURE__ */ jsx14("img", { ...props });
}
}
// src/components/asset.tsx
import "notion-types";
import { getTextContent } from "notion-utils";
// src/components/lite-youtube-embed.tsx
import React11 from "react";
import { Fragment as Fragment3, jsx as jsx15, jsxs as jsxs8 } from "react/jsx-runtime";
var qs = (params) => {
return Object.keys(params).map(
(key) => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`
).join("&"