Compare commits

...

13 Commits

Author SHA1 Message Date
Samy Pessé 7375d3c597 Version Packages (#3645)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-09-12 11:37:22 +02:00
spastorelli 3548fa6dff Fix eval estree expr cjs named import when gitbook/expr is imported in playwright tests (#3643) 2025-09-12 11:20:33 +02:00
conico974 cb73040e0f Bump Next.js to version 15.4.0 (#3644)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-09-12 11:17:43 +02:00
Samy Pessé 872d36b64f Version Packages (#3629)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-09-11 18:06:31 +02:00
spastorelli e1ff17e655 Fix bundling of @gitbook/expr package (#3639) 2025-09-11 17:24:06 +02:00
Utku Ufuk f3010bd28a Stop using deprecated search API param (#3638) 2025-09-11 15:26:35 +03:00
spastorelli 8ff1e3b619 Port latest changes from gbx util-expr (#3636) 2025-09-09 18:55:41 +02:00
Viktor Renkema d7596bf454 Don't show toolbar when rendering within GitBook app preview (#3635) 2025-09-09 15:09:52 +02:00
Zeno Kapitein aea5eb10ae Persist language choice across sections (#3633) 2025-09-09 15:06:28 +02:00
Zeno Kapitein 1165a81cf5 Language selector fixes (#3632) 2025-09-08 17:22:42 +02:00
conico974 61d1a0192e Fix null contentRef handling in RecordColumnValue (#3566)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
2025-09-08 13:54:58 +02:00
Zeno Kapitein f9a2977621 Better handling for external link "mailto:" in Hovered Card in GBO (#3630) 2025-09-08 10:31:30 +00:00
spastorelli 24f601d594 Small optim in resolveTryItPrefillForOperation and remove uneeded dep (#3628) 2025-09-08 10:29:59 +02:00
38 changed files with 581 additions and 453 deletions
+230 -362
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -34,7 +34,7 @@
"workspaces": {
"packages": ["packages/*"],
"catalog": {
"@gitbook/api": "^0.139.0",
"@gitbook/api": "^0.140.0",
"bidc": "^0.0.2"
}
},
+16
View File
@@ -1,5 +1,21 @@
# @gitbook/expr
## 1.1.1
### Patch Changes
- 3548fa6: Fix eval-estree-expr named import
## 1.1.0
### Minor Changes
- e1ff17e: Fix bundling of gitbook/expr package
### Patch Changes
- 8ff1e3b: Add support for every/some array methods
## 1.0.0
### Major Changes
+8 -5
View File
@@ -1,7 +1,7 @@
{
"name": "@gitbook/expr",
"description": "Safely evaluate & parse user-defined GitBook expressions.",
"version": "1.0.0",
"version": "1.1.1",
"type": "module",
"exports": {
".": {
@@ -12,23 +12,26 @@
},
"sideEffects": false,
"dependencies": {
"eval-estree-expression": "^2.0.3",
"eval-estree-expression": "github:jonschlinkert/eval-estree-expression#9cf28d2",
"acorn": "^8.14.0",
"acorn-loose": "8.4.0",
"acorn-walk": "^8.3.4",
"escodegen": "^2.1.0",
"assert-never": "^1.2.1"
},
"devDependencies": {
"bun-types": "^1.1.20",
"tsdown": "^0.15.0",
"@types/estree": "^1.0.6",
"@babel/types": "^7.26.0",
"@types/json-schema": "^7.0.15"
"@types/json-schema": "^7.0.15",
"@types/escodegen": "^0.0.10"
},
"scripts": {
"build": "tsc --project tsconfig.build.json",
"build": "tsdown --project tsconfig.build.json",
"typecheck": "tsc --noEmit",
"unit": "bun test",
"clean": "rm -rf ./dist"
},
"files": ["dist", "src", "README.md", "CHANGELOG.md"]
"files": ["dist", "README.md", "CHANGELOG.md"]
}
@@ -210,6 +210,8 @@ describe('autocomplete', () => {
'visitor.claims.hello.length',
'visitor.claims.hello.at',
'visitor.claims.hello.includes',
'visitor.claims.hello.some',
'visitor.claims.hello.every',
],
},
},
@@ -268,6 +270,8 @@ describe('autocomplete', () => {
'visitor.claims.hello.length',
'visitor.claims.hello.at',
'visitor.claims.hello.includes',
'visitor.claims.hello.some',
'visitor.claims.hello.every',
],
},
},
+20 -2
View File
@@ -42,11 +42,29 @@ describe('ExpressionRuntime', () => {
},
expectedResult: true,
},
{
scenario: 'array method',
condition: 'reviews.every(review => !!review.status)',
inputs: { reviews: [{ status: 'approved' }, { status: 'approved' }] },
expectedResult: true,
},
{
scenario: 'array every',
condition: 'reviews.every(review => review.status === "approved")',
inputs: { reviews: [{ status: 'approved' }, { status: 'approved' }] },
expectedResult: true,
},
{
scenario: 'array map',
condition: '[1, 2, 3].map(n => n * x)',
inputs: { x: 2 },
expectedResult: [2, 4, 6],
},
])(
'should properly evaluate/safeEvaluate a valid conditional expression: $scenario',
({ condition, inputs, expectedResult }) => {
expect(runtime.evaluate(condition, inputs)).toBe(expectedResult);
expect(runtime.safeEvaluate(condition, inputs).value).toBe(expectedResult);
expect(runtime.evaluate(condition, inputs)).toEqual(expectedResult);
expect(runtime.safeEvaluate(condition, inputs).value).toEqual(expectedResult);
}
);
-1
View File
@@ -1,6 +1,5 @@
export * from './errors';
export * from './input-values';
export * from './input-values';
export * from './runtime';
export * from './symbols';
export * from './template';
+4 -1
View File
@@ -9,7 +9,9 @@ import {
tokenizer,
} from 'acorn';
import { parse as parseLoose } from 'acorn-loose';
import { evaluate } from 'eval-estree-expression';
import escodegen from 'escodegen';
import evalESTreeExpr from 'eval-estree-expression';
const { evaluate } = evalESTreeExpr;
import { AutoComplete } from './autocomplete';
import { ExpressionError } from './errors';
@@ -49,6 +51,7 @@ export class ExpressionRuntime {
return evaluate.sync<Expression>(parsed.result, inputs, {
functions: true,
withMembers: true,
generate: escodegen.generate,
});
} catch (error) {
throw error instanceof Error
@@ -338,6 +338,8 @@ describe('ExpressionRuntime', () => {
'visitor.claims.hello.length',
'visitor.claims.hello.at',
'visitor.claims.hello.includes',
'visitor.claims.hello.some',
'visitor.claims.hello.every',
],
});
});
+54
View File
@@ -259,6 +259,60 @@ const StandardLibrary: Partial<
'true if the value searchElement is found within the array (or the part of the array indicated by the index fromIndex, if specified).',
}),
}),
SymbolFunction({
name: 'some',
description:
'Tests whether at least one element in the array passes the test implemented by the provided function.',
link: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some',
args: [
SymbolFunction({
name: 'callback',
description: 'A function that tests each element of the array.',
args: [
{
...arraySymbolDef.items,
name: 'element',
description: 'The current element being processed in the array.',
},
],
returns: SymbolBoolean({
description:
'true if the callback function returns a truthy value for at least one element in the array.',
}),
}),
],
returns: SymbolBoolean({
description:
'true if the callback function returns a truthy value for at least one element in the array.',
}),
}),
SymbolFunction({
name: 'every',
description:
'Tests whether all elements in the array pass the test implemented by the provided function.',
link: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every',
args: [
SymbolFunction({
name: 'callback',
description: 'A function that tests each element of the array.',
args: [
{
...arraySymbolDef.items,
name: 'element',
description: 'The current element being processed in the array.',
},
],
returns: SymbolBoolean({
description:
'true if the callback function returns a truthy value for all elements in the array.',
}),
}),
],
returns: SymbolBoolean({
description:
'true if the callback function returns a truthy value for all elements in the array.',
}),
}),
],
}),
};
+9
View File
@@ -0,0 +1,9 @@
import { defineConfig } from 'tsdown';
export default defineConfig([
{
entry: ['src/index.ts'],
dts: true,
format: ['esm'],
},
]);
+1 -1
View File
@@ -14,7 +14,7 @@ declare module 'eval-estree-expression' {
/**
* Enable support for function statements and expressions by enabling the functions option AND by passing the .generate() function from the escodegen library. Default: undefined
*/
generate?: boolean;
generate?: boolean | ((node: any) => string);
/**
* Enable the =~ regex operator to support testing values without using functions (example name =~ /^a.*c$/). Default: true
*/
+17
View File
@@ -1,5 +1,22 @@
# gitbook
## 0.17.2
### Patch Changes
- @gitbook/react-openapi@1.4.2
## 0.17.1
### Patch Changes
- 24f601d: Small optim in resolveTryItPrefillForOperation
- aea5eb1: Persist language choice across sections if possible
- 1165a81: Language selector edge cases
- f9a2977: Better handling for external link "mailto:" in Hovered Card in GBO
- Updated dependencies [24f601d]
- @gitbook/react-openapi@1.4.1
## 0.17.0
### Minor Changes
@@ -17,7 +17,8 @@
"dev": {
"vars": {
"STAGE": "dev",
"OPEN_NEXT_REQUEST_ID_HEADER": "true"
"OPEN_NEXT_REQUEST_ID_HEADER": "true",
"GITBOOK_URL": "http://localhost:8771"
},
"r2_buckets": [
{
+2 -3
View File
@@ -1,13 +1,12 @@
{
"name": "gitbook",
"version": "0.17.0",
"version": "0.17.2",
"private": true,
"dependencies": {
"@gitbook/api": "catalog:",
"@gitbook/browser-types": "workspace:*",
"@gitbook/cache-tags": "workspace:*",
"@gitbook/colors": "workspace:*",
"@gitbook/expr": "workspace:*",
"@gitbook/emoji-codepoints": "workspace:*",
"@gitbook/fonts": "workspace:*",
"@gitbook/icons": "workspace:*",
@@ -47,7 +46,7 @@
"memoizee": "^0.4.17",
"micromark-extension-frontmatter": "^2.0.0",
"micromark-extension-gfm": "^3.0.0",
"next": "15.3.5",
"next": "15.4.0",
"next-themes": "^0.2.1",
"nuqs": "^2.2.3",
"object-hash": "^3.0.0",
@@ -1,11 +1,10 @@
import type { GitBookSiteContext } from '@/lib/context';
import { Icon } from '@gitbook/icons';
import { headers } from 'next/headers';
import React from 'react';
import { tcls } from '@/lib/tailwind';
import { DateRelative } from '../primitives';
import { IframeWrapper } from './IframeWrapper';
import { RefreshChangeRequestButton } from './RefreshChangeRequestButton';
import { Toolbar, ToolbarBody, ToolbarButton, ToolbarButtonGroups } from './Toolbar';
@@ -47,19 +46,21 @@ function ToolbarLayout(props: { children: React.ReactNode }) {
*/
export async function AdminToolbar(props: AdminToolbarProps) {
const { context } = props;
const mode = (await headers()).get('x-gitbook-mode');
if (mode === 'multi-id') {
// We don't show the admin toolbar in multi-id mode, as it's used for previewing in the dashboard.
return null;
}
if (context.changeRequest) {
return <ChangeRequestToolbar context={context} />;
return (
<IframeWrapper>
<ChangeRequestToolbar context={context} />
</IframeWrapper>
);
}
if (context.revisionId !== context.space.revision) {
return <RevisionToolbar context={context} />;
return (
<IframeWrapper>
<RevisionToolbar context={context} />
</IframeWrapper>
);
}
return null;
@@ -0,0 +1,27 @@
'use client';
import React from 'react';
interface IframeWrapperProps {
children: React.ReactNode;
}
/**
* Client component that detects if we're in an iframe and conditionally renders children
*/
export function IframeWrapper({ children }: IframeWrapperProps) {
const [isInIframe, setIsInIframe] = React.useState(false);
React.useEffect(() => {
// Check if we're running inside an iframe
const inIframe = window !== window.parent;
setIsInIframe(inIframe);
}, []);
// Don't render children if we're in an iframe (GitBook app preview)
if (isInIframe) {
return null;
}
return children;
}
@@ -1 +1,2 @@
export * from './AdminToolbar';
export * from './IframeWrapper';
@@ -46,6 +46,7 @@ export async function InlineLink(props: InlineProps<DocumentInlineLink>) {
);
}
const isExternal = inline.data.ref.kind === 'url';
const isMailto = resolved.href.startsWith('mailto:');
const content = (
<StyledLink
href={resolved.href}
@@ -63,7 +64,12 @@ export async function InlineLink(props: InlineProps<DocumentInlineLink>) {
nodes={inline.nodes}
ancestorInlines={[...ancestorInlines, inline]}
/>
{isExternal ? (
{isMailto ? (
<Icon
icon="envelope"
className="ml-1 inline size-3 links-accent:text-tint-subtle"
/>
) : isExternal ? (
<Icon
icon="arrow-up-right"
className="ml-0.5 inline size-3 links-accent:text-tint-subtle"
@@ -96,16 +102,24 @@ function InlineLinkTooltipWrapper(props: {
const { inline, language, resolved, children } = props;
let breadcrumbs = resolved.ancestors ?? [];
const isMailto = resolved.href.startsWith('mailto:');
const isExternal = inline.data.ref.kind === 'url';
const isSamePage = inline.data.ref.kind === 'anchor' && inline.data.ref.page === undefined;
if (isExternal) {
if (isMailto) {
resolved.text = resolved.text.split('mailto:')[1] ?? resolved.text;
breadcrumbs = [
{
label: tString(language, 'link_tooltip_email'),
},
];
} else if (isExternal) {
breadcrumbs = [
{
label: tString(language, 'link_tooltip_external_link'),
},
];
}
if (isSamePage) {
} else if (isSamePage) {
breadcrumbs = [
{
label: tString(language, 'link_tooltip_page_anchor'),
@@ -15,12 +15,17 @@ import { getSimplifiedContentType } from '@/lib/files';
import { resolveContentRef } from '@/lib/references';
import { tcls } from '@/lib/tailwind';
import { filterOutNullable } from '@/lib/typescript';
import type { BlockProps } from '../Block';
import { Blocks } from '../Blocks';
import { FileIcon } from '../FileIcon';
import type { TableRecordKV } from './Table';
import { type VerticalAlignment, getColumnAlignment } from './utils';
import {
type VerticalAlignment,
getColumnAlignment,
isContentRef,
isDocumentTableImageRecord,
isStringArray,
} from './utils';
const alignmentMap: Record<'text-left' | 'text-center' | 'text-right', string> = {
'text-left': '**:text-left text-left',
@@ -58,18 +63,28 @@ export async function RecordColumnValue<Tag extends React.ElementType = 'div'>(
return null;
}
// Because definition and value depends on column, we have to check typing in each case at runtime.
// Validation should have been done at the API level, but we can't know typing based on `definition.type`.
// OpenAPI types cannot really handle discriminated unions based on a dynamic key.
switch (definition.type) {
case 'checkbox':
case 'checkbox': {
if (value === null || typeof value !== 'boolean') {
return null;
}
return (
<Checkbox
className={tcls('w-5', 'h-5')}
checked={value as boolean}
checked={value}
disabled={true}
aria-labelledby={ariaLabelledBy}
/>
);
}
case 'rating': {
const rating = value as number;
if (typeof value !== 'number') {
return null;
}
const rating = value;
const max = definition.max;
return (
@@ -108,15 +123,21 @@ export async function RecordColumnValue<Tag extends React.ElementType = 'div'>(
</Tag>
);
}
case 'number':
case 'number': {
if (typeof value !== 'number') {
return null;
}
return (
<Tag
className={tcls('text-base', 'tabular-nums', 'tracking-tighter')}
aria-labelledby={ariaLabelledBy}
>{`${value}`}</Tag>
);
}
case 'text': {
// @ts-ignore
if (typeof value !== 'string') {
return null;
}
const fragment = getNodeFragmentByName(block, value);
if (!fragment) {
return <Tag className={tcls(['w-full', verticalAlignment])}>{''}</Tag>;
@@ -149,8 +170,11 @@ export async function RecordColumnValue<Tag extends React.ElementType = 'div'>(
);
}
case 'files': {
if (!isStringArray(value)) {
return null;
}
const files = await Promise.all(
(value as string[]).map((fileId) =>
value.map((fileId) =>
context.contentContext
? resolveContentRef(
{
@@ -221,10 +245,12 @@ export async function RecordColumnValue<Tag extends React.ElementType = 'div'>(
);
}
case 'content-ref': {
const contentRef = value ? (value as ContentRef) : null;
if (value === null || !isContentRef(value)) {
return null;
}
const resolved =
contentRef && context.contentContext
? await resolveContentRef(contentRef, context.contentContext, {
value && context.contentContext
? await resolveContentRef(value, context.contentContext, {
resolveAnchorText: true,
iconStyle: ['mr-2', 'text-tint-subtle'],
})
@@ -239,11 +265,11 @@ export async function RecordColumnValue<Tag extends React.ElementType = 'div'>(
<StyledLink
href={resolved.href}
insights={
contentRef
value
? {
type: 'link_click',
link: {
target: contentRef,
target: value,
position: SiteInsightsLinkPosition.Content,
},
}
@@ -257,8 +283,11 @@ export async function RecordColumnValue<Tag extends React.ElementType = 'div'>(
);
}
case 'users': {
if (!isStringArray(value)) {
return null;
}
const resolved = await Promise.all(
(value as string[]).map(async (userId) => {
value.map(async (userId) => {
const contentRef: ContentRefUser = {
kind: 'user',
user: userId,
@@ -295,10 +324,13 @@ export async function RecordColumnValue<Tag extends React.ElementType = 'div'>(
);
}
case 'select': {
if (!isStringArray(value)) {
return null;
}
return (
<Tag aria-labelledby={ariaLabelledBy}>
<span className={tcls('inline-flex', 'gap-2', 'flex-wrap')}>
{(value as string[]).map((selectId) => {
{value.map((selectId) => {
const option = definition.options.find(
(option) => option.value === selectId
);
@@ -329,8 +361,15 @@ export async function RecordColumnValue<Tag extends React.ElementType = 'div'>(
);
}
case 'image': {
if (!isDocumentTableImageRecord(value)) {
return null;
}
const image = context.contentContext
? await resolveContentRef(value as ContentRef, context.contentContext)
? await resolveContentRef(
'ref' in value ? value.ref : value,
context.contentContext
)
: null;
if (!image) {
@@ -123,3 +123,35 @@ export function getColumnVerticalAlignment(column: DocumentTableDefinition): Ver
return 'self-center';
}
/**
* Check if a value is a ContentRef.
* @param ref The value to check.
* @returns True if the value is a ContentRef, false otherwise.
*/
export function isContentRef(ref?: DocumentTableRecord['values'][string]): ref is ContentRef {
return Boolean(ref && typeof ref === 'object' && 'kind' in ref);
}
/**
* Check if a value is an array of strings.
* @param value The value to check.
* @returns True if the value is an array of strings, false otherwise.
*/
export function isStringArray(value?: DocumentTableRecord['values'][string]): value is string[] {
return Array.isArray(value) && value.every((v) => typeof v === 'string');
}
/**
* Check if a value is a DocumentTableImageRecord.
* @param value The value to check.
* @returns True if the value is a DocumentTableImageRecord, false otherwise.
*/
export function isDocumentTableImageRecord(
value?: DocumentTableRecord['values'][string]
): value is DocumentTableImageRecord {
if (isContentRef(value) && (value.kind === 'file' || value.kind === 'url')) {
return true;
}
return Boolean(value && typeof value === 'object' && 'ref' in value && isContentRef(value.ref));
}
@@ -80,13 +80,14 @@ export function Header(props: {
>
<HeaderMobileMenu
className={tcls(
'lg:hidden',
'-ml-2',
'text-tint-strong',
'theme-bold:text-header-link',
'hover:bg-tint-hover',
'hover:theme-bold:bg-header-link/3',
withVariants === 'generic' ? '' : 'page-no-toc:hidden'
withVariants === 'generic'
? 'xl:hidden'
: 'page-no-toc:hidden lg:hidden'
)}
/>
<HeaderLogo context={context} />
@@ -123,8 +123,7 @@ export async function streamAskQuestion({
},
scope: {
mode: 'default',
// Include the current site space regardless.
includedSiteSpaces: [context.siteSpace.id],
currentSiteSpace: context.siteSpace.id,
},
},
{ format: 'document' }
@@ -30,9 +30,7 @@ export function encodeClientSiteSections(context: GitBookSiteContext, sections:
for (const item of list) {
switch (item.object) {
case 'site-section-group': {
const sections = item.sections
.filter((section) => shouldIncludeSection(context, section))
.map((section) => encodeSection(context, section));
const sections = item.sections.map((section) => encodeSection(context, section));
// Skip empty groups
if (sections.length === 0) {
@@ -74,38 +72,13 @@ function encodeSection(context: GitBookSiteContext, section: SiteSection) {
};
}
/**
* Test if a section should be included in the list of sections.
*/
function shouldIncludeSection(context: GitBookSiteContext, section: SiteSection) {
if (context.site.id !== 'site_JOVzv') {
return true;
}
// Testing for a new mode of navigation where the multi-variants section are hidden
// if they do not include an equivalent of the current site space.
// TODO: replace with a proper flag on the section
const withNavigateOnlyIfEquivalent = section.id === 'sitesc_4jvEm';
if (!withNavigateOnlyIfEquivalent) {
return true;
}
const { siteSpace: currentSiteSpace } = context;
if (section.siteSpaces.length === 1) {
return true;
}
return section.siteSpaces.some((siteSpace) =>
areSiteSpacesEquivalent(siteSpace, currentSiteSpace)
);
}
/**
* Find the best default site space to navigate to for a givent section:
* 1. If we are on the default, continue on the default.
* 2. If a site space has the same path as the current one, return it.
* 3. Otherwise, return the default one.
* 2. If there are site spaces with the same language as the current, filter by language.
* 3. If a site space has the same path as the current one, return it.
* 4. Otherwise, return the default first language match.
* 5. Otherwise, return the default one.
*/
function findBestTargetURL(context: GitBookSiteContext, section: SiteSection) {
const { siteSpace: currentSiteSpace } = context;
@@ -114,9 +87,15 @@ function findBestTargetURL(context: GitBookSiteContext, section: SiteSection) {
return getSectionURL(context, section);
}
const bestMatch = section.siteSpaces.find((siteSpace) =>
areSiteSpacesEquivalent(siteSpace, currentSiteSpace)
);
const possibleMatches =
section.siteSpaces.filter((siteSpace) =>
areSiteSpacesSameLanguage(siteSpace, currentSiteSpace)
) ?? section.siteSpaces;
const bestMatch =
possibleMatches.find((siteSpace) => areSiteSpacesEquivalent(siteSpace, currentSiteSpace)) ??
possibleMatches[0];
if (bestMatch) {
return getSiteSpaceURL(context, bestMatch);
}
@@ -130,3 +109,7 @@ function findBestTargetURL(context: GitBookSiteContext, section: SiteSection) {
function areSiteSpacesEquivalent(siteSpace1: SiteSpace, siteSpace2: SiteSpace) {
return siteSpace1.path === siteSpace2.path;
}
function areSiteSpacesSameLanguage(siteSpace1: SiteSpace, siteSpace2: SiteSpace) {
return siteSpace1.space.language === siteSpace2.space.language;
}
@@ -20,7 +20,7 @@ import type { RenderAIMessageOptions } from '../AI';
import { AIChat } from '../AIChat';
import { AdaptiveVisitorContextProvider } from '../Adaptive';
import { Announcement } from '../Announcement';
import { SpacesDropdown } from '../Header/SpacesDropdown';
import { SpacesDropdown, TranslationsDropdown } from '../Header/SpacesDropdown';
import { InsightsProvider } from '../Insights';
import { SearchContainer } from '../Search';
import { SiteSectionList, encodeClientSiteSections } from '../SiteSections';
@@ -156,12 +156,20 @@ export function SpaceLayout(props: SpaceLayoutProps) {
'pr-4',
'lg:flex',
'grow-0',
'flex-wrap',
'dark:shadow-light/1',
'text-base/tight'
'text-base/tight',
'items-center'
)}
>
<HeaderLogo context={context} />
{withVariants === 'translations' ? (
<TranslationsDropdown
context={context}
siteSpace={siteSpace}
siteSpaces={siteSpaces}
className="[&_.button-leading-icon]:block! ml-auto py-2 [&_.button-content]:hidden"
/>
) : null}
</div>
)
}
@@ -68,6 +68,7 @@ export const de = {
pdf_limit_reached_continue: 'Mit ${1} weiteren Seiten erweitern.',
more: 'Mehr',
link_tooltip_external_link: 'Externe Verlinkung zu',
link_tooltip_email: 'E-Mail senden an',
link_tooltip_page_anchor: 'Zum Abschnitt springen',
open_in_new_tab: 'In neuem Tab öffnen',
ai_chat_assistant_name: 'GitBook-Assistent',
@@ -65,6 +65,7 @@ export const en = {
pdf_limit_reached_continue: 'Extend with ${1} more pages.',
more: 'More',
link_tooltip_external_link: 'External link to',
link_tooltip_email: 'Send an email to',
link_tooltip_page_anchor: 'Jump to section',
open_in_new_tab: 'Open in new tab',
ai_chat_assistant_name: 'GitBook Assistant',
@@ -69,6 +69,7 @@ export const es: TranslationLanguage = {
pdf_limit_reached_continue: 'Extender con ${1} páginas más.',
more: 'Más',
link_tooltip_external_link: 'Enlace externo a',
link_tooltip_email: 'Enviar un correo electrónico a',
link_tooltip_page_anchor: 'Saltar a la sección',
open_in_new_tab: 'Abrir en una nueva pestaña',
ai_chat_assistant_name: 'Asistente de GitBook',
@@ -64,6 +64,7 @@ export const fr = {
pdf_limit_reached_continue: 'Ajouter ${1} pages supplémentaires',
more: 'Plus',
link_tooltip_external_link: 'Lien externe vers',
link_tooltip_email: 'Envoyer un e-mail à',
link_tooltip_page_anchor: 'Aller à la section',
open_in_new_tab: 'Ouvrir dans un nouvel onglet',
ai_chat_assistant_name: 'Assistant GitBook',
@@ -67,6 +67,7 @@ export const ja: TranslationLanguage = {
pdf_limit_reached_continue: 'さらに${1}ページで拡張',
more: '詳細',
link_tooltip_external_link: '外部リンク先',
link_tooltip_email: 'メールを送信',
link_tooltip_page_anchor: 'ページ内リンク先',
open_in_new_tab: '新しいタブで開く',
ai_chat_assistant_name: 'GitBookアシスタント',
@@ -67,6 +67,7 @@ export const nl: TranslationLanguage = {
pdf_limit_reached_continue: "Verleng met ${1} extra pagina's.",
more: 'Meer',
link_tooltip_external_link: 'Externe link naar',
link_tooltip_email: 'E-mail versturen naar',
link_tooltip_page_anchor: 'Spring naar sectie',
open_in_new_tab: 'Open in nieuw tabblad',
ai_chat_assistant_name: 'GitBook Assistent',
@@ -68,6 +68,7 @@ export const no: TranslationLanguage = {
pdf_limit_reached_continue: 'Utvid med ${1} flere sider.',
more: 'Mer',
link_tooltip_external_link: 'Ekstern lenke til',
link_tooltip_email: 'Send e-post til',
link_tooltip_page_anchor: 'Hopp til seksjon',
open_in_new_tab: 'Åpne i ny fane',
ai_chat_assistant_name: 'GitBook-assistent',
@@ -67,6 +67,7 @@ export const pt_br = {
pdf_limit_reached_continue: 'Extender com mais ${1} páginas.',
more: 'Mais',
link_tooltip_external_link: 'Link externo para',
link_tooltip_email: 'Enviar e-mail para',
link_tooltip_page_anchor: 'Pular para a seção',
open_in_new_tab: 'Abrir em uma nova guia',
ai_chat_assistant_name: 'Assistente do GitBook',
@@ -66,6 +66,7 @@ export const ru = {
pdf_limit_reached_continue: 'Расширьте ещё на ${1} страниц.',
more: 'Ещё',
link_tooltip_external_link: 'Внешняя ссылка на',
link_tooltip_email: 'Отправить письмо на',
link_tooltip_page_anchor: 'Перейти к разделу',
open_in_new_tab: 'Открыть в новой вкладке',
ai_chat_assistant_name: 'GitBook-помощник',
@@ -65,6 +65,7 @@ export const zh: TranslationLanguage = {
pdf_limit_reached_continue: '使用${1}页进行扩展。',
more: '更多',
link_tooltip_external_link: '外部链接到',
link_tooltip_email: '发送邮件到',
link_tooltip_page_anchor: '跳转到页面',
open_in_new_tab: '在新标签页中打开',
ai_chat_assistant_name: 'GitBook 助手',
+16
View File
@@ -1,5 +1,21 @@
# @gitbook/react-openapi
## 1.4.2
### Patch Changes
- Updated dependencies [3548fa6]
- @gitbook/expr@1.1.1
## 1.4.1
### Patch Changes
- 24f601d: Small optim in resolveTryItPrefillForOperation
- Updated dependencies [e1ff17e]
- Updated dependencies [8ff1e3b]
- @gitbook/expr@1.1.0
## 1.4.0
### Minor Changes
+1 -1
View File
@@ -8,7 +8,7 @@
"default": "./dist/index.js"
}
},
"version": "1.4.0",
"version": "1.4.2",
"sideEffects": false,
"dependencies": {
"@gitbook/expr": "workspace:*",
@@ -27,12 +27,16 @@ export function resolveTryItPrefillForOperation(args: {
prefillInputContext,
} = args;
// Fixed ExpressionRuntime and resolveTryItPrefillExpression function
if (!prefillInputContext) {
return {};
}
const runtime = new ExpressionRuntime();
const resolveTryItPrefillExpression = (expr: string) => {
if (!prefillInputContext) return undefined;
const parts = parseTemplate(expr);
if (!parts.length) return undefined;
if (!parts.length) {
return undefined;
}
return runtime.evaluateTemplate(expr, prefillInputContext);
};