@redocly/theme
Version:
Shared UI components lib
223 lines • 12.2 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.usePageActions = usePageActions;
const react_1 = require("react");
const CopyIcon_1 = require("../../icons/CopyIcon/CopyIcon");
const ChatGptIcon_1 = require("../../icons/ChatGptIcon/ChatGptIcon");
const ClaudeIcon_1 = require("../../icons/ClaudeIcon/ClaudeIcon");
const MarkdownFullIcon_1 = require("../../icons/MarkdownFullIcon/MarkdownFullIcon");
const VSCodeIcon_1 = require("../../icons/VSCodeIcon/VSCodeIcon");
const CursorIcon_1 = require("../../icons/CursorIcon/CursorIcon");
const use_theme_hooks_1 = require("./use-theme-hooks");
const use_theme_config_1 = require("./use-theme-config");
const use_mcp_config_1 = require("./use-mcp-config");
const clipboard_service_1 = require("../utils/clipboard-service");
const dom_1 = require("../utils/dom");
const mcp_1 = require("../utils/mcp");
const urls_1 = require("../utils/urls");
function createPageActionResource(pageSlug, pageUrl) {
return {
id: pageSlug,
object: 'page',
uri: pageUrl,
};
}
const DEFAULT_ENABLED_ACTIONS = [
'copy',
'view',
'chatgpt',
'claude',
'docs-mcp-cursor',
'docs-mcp-vscode',
];
function usePageActions(pageSlug, mcpUrl, actions) {
var _a, _b, _c;
const { useTranslate, usePageData, usePageProps, usePageSharedData, useTelemetry } = (0, use_theme_hooks_1.useThemeHooks)();
const { translate } = useTranslate();
const themeConfig = (0, use_theme_config_1.useThemeConfig)();
const pageProps = usePageProps();
const telemetry = useTelemetry();
const openApiSharedData = usePageSharedData('openAPIDocsStore');
const mcpConfig = (0, use_mcp_config_1.useMCPConfig)();
const shouldHideAllActions = shouldHidePageActions(pageProps, themeConfig, (_a = openApiSharedData === null || openApiSharedData === void 0 ? void 0 : openApiSharedData.options) === null || _a === void 0 ? void 0 : _a.excludeFromSearch);
const { isPublic } = usePageData() || {};
const createMCPHandler = (0, react_1.useCallback)((clientType, requiresMcpUrl = false) => () => {
if (requiresMcpUrl && !mcpUrl)
return null;
if (!requiresMcpUrl && (mcpUrl || mcpConfig.isMcpDisabled))
return null;
const config = requiresMcpUrl
? { serverName: mcpConfig.serverName, url: mcpUrl || '' }
: { serverName: mcpConfig.serverName, url: mcpConfig.serverUrl || '' };
const isDocsMcp = !requiresMcpUrl;
const origin = dom_1.IS_BROWSER ? window.location.origin : '';
const pageUrl = `${origin}${pageSlug}`;
return createMCPAction({
clientType,
mcpConfig: config,
translate,
onClickCallback: isDocsMcp
? () => telemetry.sendPageActionsButtonClickedMessage([
Object.assign(Object.assign({}, createPageActionResource(pageSlug, pageUrl)), { action_type: `docs-mcp-${clientType}` }),
])
: undefined,
});
}, [mcpUrl, mcpConfig, translate, telemetry, pageSlug]);
const result = (0, react_1.useMemo)(() => {
var _a, _b, _c;
if (shouldHideAllActions) {
return [];
}
const origin = dom_1.IS_BROWSER
? window.location.origin
: ((_a = globalThis['SSR_HOSTNAME']) !== null && _a !== void 0 ? _a : '');
const pathname = (0, urls_1.addTrailingSlash)(pageSlug);
const pageUrl = (0, urls_1.combineUrls)(origin, pathname);
const isRoot = (0, urls_1.withoutPathPrefix)(pathname) === '/';
const mdPageUrl = isRoot
? (0, urls_1.combineUrls)(origin, pathname, 'index.html.md')
: (0, urls_1.combineUrls)(origin, (0, urls_1.removeTrailingSlash)(pathname) + '.md');
const actionHandlers = {
'docs-mcp-cursor': createMCPHandler('cursor', false),
'docs-mcp-vscode': createMCPHandler('vscode', false),
'mcp-cursor': createMCPHandler('cursor', true),
'mcp-vscode': createMCPHandler('vscode', true),
copy: () => ({
buttonText: translate('page.actions.copyButtonText', 'Copy'),
title: translate('page.actions.copyTitle', 'Copy for LLM'),
description: translate('page.actions.copyDescription', 'Copy page as Markdown for LLMs'),
iconComponent: CopyIcon_1.CopyIcon,
onClick: () => __awaiter(this, void 0, void 0, function* () {
try {
const result = yield fetch(mdPageUrl);
if (result.status !== 200) {
return;
}
const text = yield result.text();
clipboard_service_1.ClipboardService.copyCustom(text);
telemetry.sendPageActionsButtonClickedMessage([
Object.assign(Object.assign({}, createPageActionResource(pageSlug, pageUrl)), { action_type: 'copy' }),
]);
}
catch (error) {
console.error(error);
}
}),
}),
view: () => ({
buttonText: translate('page.actions.viewAsMdButtonText', 'View as Markdown'),
title: translate('page.actions.viewAsMdTitle', 'View as Markdown'),
description: translate('page.actions.viewAsMdDescription', 'Open this page as Markdown'),
iconComponent: MarkdownFullIcon_1.MarkdownFullIcon,
link: mdPageUrl,
onClick: () => {
telemetry.sendPageActionsButtonClickedMessage([
Object.assign(Object.assign({}, createPageActionResource(pageSlug, pageUrl)), { action_type: 'view' }),
]);
},
}),
chatgpt: () => {
if (!isPublic) {
return null;
}
const link = getExternalAiPromptLink('https://chat.openai.com', mdPageUrl);
return {
buttonText: translate('page.actions.chatGptButtonText', 'Open in ChatGPT'),
title: translate('page.actions.chatGptTitle', 'Open in ChatGPT'),
description: translate('page.actions.chatGptDescription', 'Get insights from ChatGPT'),
iconComponent: ChatGptIcon_1.ChatGptIcon,
link,
onClick: () => {
telemetry.sendPageActionsButtonClickedMessage([
Object.assign(Object.assign({}, createPageActionResource(pageSlug, pageUrl)), { action_type: 'chatgpt' }),
]);
window.location.href = link;
},
};
},
claude: () => {
if (!isPublic) {
return null;
}
const link = getExternalAiPromptLink('https://claude.ai/new', mdPageUrl);
return {
buttonText: translate('page.actions.claudeButtonText', 'Open in Claude'),
title: translate('page.actions.claudeTitle', 'Open in Claude'),
description: translate('page.actions.claudeDescription', 'Get insights from Claude'),
iconComponent: ClaudeIcon_1.ClaudeIcon,
link,
onClick: () => {
telemetry.sendPageActionsButtonClickedMessage([
Object.assign(Object.assign({}, createPageActionResource(pageSlug, pageUrl)), { action_type: 'claude' }),
]);
window.location.href = link;
},
};
},
};
return (((_c = (_b = themeConfig.navigation) === null || _b === void 0 ? void 0 : _b.actions) === null || _c === void 0 ? void 0 : _c.items) || actions || DEFAULT_ENABLED_ACTIONS)
.map((action) => { var _a; return (_a = actionHandlers[action]) === null || _a === void 0 ? void 0 : _a.call(actionHandlers); })
.filter((action) => action !== null);
}, [
shouldHideAllActions,
pageSlug,
(_c = (_b = themeConfig.navigation) === null || _b === void 0 ? void 0 : _b.actions) === null || _c === void 0 ? void 0 : _c.items,
actions,
translate,
isPublic,
createMCPHandler,
telemetry,
]);
return result;
}
function getExternalAiPromptLink(baseUrl, mdPageUrl) {
const externalAiPrompt = `Read ${mdPageUrl} and answer questions based on the content.`;
const url = new URL(baseUrl);
url.searchParams.set('q', externalAiPrompt);
return url.toString();
}
function createMCPAction({ clientType, mcpConfig, translate, onClickCallback, }) {
const url = (0, mcp_1.generateMCPDeepLink)(clientType, mcpConfig);
const sharedProps = {
onClick: () => {
onClickCallback === null || onClickCallback === void 0 ? void 0 : onClickCallback();
window.open(url, '_blank');
},
};
if (clientType === 'cursor') {
return Object.assign(Object.assign({}, sharedProps), { buttonText: translate('page.actions.connectMcp.cursor', 'Connect to Cursor'), title: translate('page.actions.connectMcp.cursor', 'Connect to Cursor'), description: translate('page.actions.connectMcp.cursorDescription', 'Install MCP server on Cursor'), iconComponent: CursorIcon_1.CursorIcon });
}
return Object.assign(Object.assign({}, sharedProps), { buttonText: translate('page.actions.connectMcp.vscode', 'Connect to VS Code'), title: translate('page.actions.connectMcp.vscode', 'Connect to VS Code'), description: translate('page.actions.connectMcp.vscodeDescription', 'Install MCP server on VS Code'), iconComponent: VSCodeIcon_1.VSCodeIcon });
}
function shouldHidePageActions(pageProps, themeConfig, openapiExcludeFromSearch) {
var _a, _b, _c, _d, _e, _f, _g, _h;
// Can't use any actions if search is globally disabled (markdown files are not generated)
if ((_a = themeConfig.search) === null || _a === void 0 ? void 0 : _a.hide) {
return true;
}
// Can't use any actions if no markdown files are generated for LLMs
if ((_c = (_b = pageProps === null || pageProps === void 0 ? void 0 : pageProps.seo) === null || _b === void 0 ? void 0 : _b.llmstxt) === null || _c === void 0 ? void 0 : _c.hide) {
return true;
}
// Page actions are explicitly disabled in config
if ((_e = (_d = themeConfig.navigation) === null || _d === void 0 ? void 0 : _d.actions) === null || _e === void 0 ? void 0 : _e.hide) {
return true;
}
// Page is excluded from search
const isOpenApiPage = ((_f = pageProps === null || pageProps === void 0 ? void 0 : pageProps.metadata) === null || _f === void 0 ? void 0 : _f.type) === 'openapi' || ((_g = pageProps === null || pageProps === void 0 ? void 0 : pageProps.metadata) === null || _g === void 0 ? void 0 : _g.subType) === 'openapi-operation';
const isPageExcludedFromSearch = ((_h = pageProps === null || pageProps === void 0 ? void 0 : pageProps.frontmatter) === null || _h === void 0 ? void 0 : _h.excludeFromSearch) || (isOpenApiPage && openapiExcludeFromSearch);
if (isPageExcludedFromSearch) {
return true;
}
return false;
}
//# sourceMappingURL=use-page-actions.js.map