diff --git a/.changeset/afraid-gifts-sparkle.md b/.changeset/afraid-gifts-sparkle.md
new file mode 100644
index 000000000..ec33c1efc
--- /dev/null
+++ b/.changeset/afraid-gifts-sparkle.md
@@ -0,0 +1,5 @@
+---
+"gitbook": patch
+---
+
+Fix UX issue about highlighting the search term in search result sections
diff --git a/.changeset/curly-rules-learn.md b/.changeset/curly-rules-learn.md
new file mode 100644
index 000000000..47400e919
--- /dev/null
+++ b/.changeset/curly-rules-learn.md
@@ -0,0 +1,5 @@
+---
+"gitbook": minor
+---
+
+Add support for inline icons.
diff --git a/.changeset/famous-melons-compete.md b/.changeset/famous-melons-compete.md
new file mode 100644
index 000000000..17512b6f4
--- /dev/null
+++ b/.changeset/famous-melons-compete.md
@@ -0,0 +1,5 @@
+---
+"gitbook": patch
+---
+
+Fix crash when integration script fails to render block.
diff --git a/.changeset/fuzzy-tables-jump.md b/.changeset/fuzzy-tables-jump.md
new file mode 100644
index 000000000..ada33d1c5
--- /dev/null
+++ b/.changeset/fuzzy-tables-jump.md
@@ -0,0 +1,5 @@
+---
+"gitbook-v2": patch
+---
+
+Optimize performances by using a smarter per-request cache arround data cached functions
diff --git a/.changeset/green-clouds-cough.md b/.changeset/green-clouds-cough.md
new file mode 100644
index 000000000..098860381
--- /dev/null
+++ b/.changeset/green-clouds-cough.md
@@ -0,0 +1,5 @@
+---
+"gitbook": minor
+---
+
+Fix rendering of ogimage with SVG logos.
diff --git a/.changeset/khaki-bees-count.md b/.changeset/khaki-bees-count.md
new file mode 100644
index 000000000..a481690e2
--- /dev/null
+++ b/.changeset/khaki-bees-count.md
@@ -0,0 +1,5 @@
+---
+"gitbook": patch
+---
+
+Fix crash when integration is triggering invalid requests.
diff --git a/.changeset/orange-ears-drop.md b/.changeset/orange-ears-drop.md
new file mode 100644
index 000000000..12dd7fae5
--- /dev/null
+++ b/.changeset/orange-ears-drop.md
@@ -0,0 +1,5 @@
+---
+"@gitbook/react-contentkit": patch
+---
+
+Add basic error handling when transitioning between states.
diff --git a/.changeset/pink-windows-wonder.md b/.changeset/pink-windows-wonder.md
new file mode 100644
index 000000000..d8e9560dc
--- /dev/null
+++ b/.changeset/pink-windows-wonder.md
@@ -0,0 +1,5 @@
+---
+"gitbook": patch
+---
+
+Don't crash ogimage generation on RTL text, as a workaround until we can support it.
diff --git a/.changeset/pretty-balloons-fold.md b/.changeset/pretty-balloons-fold.md
new file mode 100644
index 000000000..b50187984
--- /dev/null
+++ b/.changeset/pretty-balloons-fold.md
@@ -0,0 +1,5 @@
+---
+"gitbook": patch
+---
+
+Fix rendering of ogimage when logo or icon are AVIF images.
diff --git a/.changeset/purple-cougars-breathe.md b/.changeset/purple-cougars-breathe.md
new file mode 100644
index 000000000..2c6d010d4
--- /dev/null
+++ b/.changeset/purple-cougars-breathe.md
@@ -0,0 +1,5 @@
+---
+"gitbook": patch
+---
+
+Add margin to adjacent buttons
diff --git a/.changeset/rare-pens-whisper.md b/.changeset/rare-pens-whisper.md
new file mode 100644
index 000000000..aed0712ba
--- /dev/null
+++ b/.changeset/rare-pens-whisper.md
@@ -0,0 +1,5 @@
+---
+"gitbook": patch
+---
+
+Fix ogimage generation failing with some JPEG images.
diff --git a/.changeset/real-walls-glow.md b/.changeset/real-walls-glow.md
new file mode 100644
index 000000000..277c39ca9
--- /dev/null
+++ b/.changeset/real-walls-glow.md
@@ -0,0 +1,5 @@
+---
+"gitbook-v2": patch
+---
+
+Don't cache unexpected API errors for more than a few minutes.
diff --git a/.changeset/rich-buses-hunt.md b/.changeset/rich-buses-hunt.md
new file mode 100644
index 000000000..fbc59d232
--- /dev/null
+++ b/.changeset/rich-buses-hunt.md
@@ -0,0 +1,5 @@
+---
+"gitbook-v2": patch
+---
+
+Fix an issue where PDF export URLs were not keeping their query params.
diff --git a/.changeset/rotten-donuts-bow.md b/.changeset/rotten-donuts-bow.md
new file mode 100644
index 000000000..d6ebe0650
--- /dev/null
+++ b/.changeset/rotten-donuts-bow.md
@@ -0,0 +1,5 @@
+---
+"gitbook-v2": patch
+---
+
+add a global error boundary
diff --git a/.changeset/slimy-cows-press.md b/.changeset/slimy-cows-press.md
new file mode 100644
index 000000000..17ec44ca8
--- /dev/null
+++ b/.changeset/slimy-cows-press.md
@@ -0,0 +1,5 @@
+---
+"gitbook": patch
+---
+
+Ignore case while highlighting search results.
diff --git a/.changeset/soft-walls-change.md b/.changeset/soft-walls-change.md
new file mode 100644
index 000000000..2086de031
--- /dev/null
+++ b/.changeset/soft-walls-change.md
@@ -0,0 +1,6 @@
+---
+"gitbook": patch
+"gitbook-v2": patch
+---
+
+Fix InlineLinkTooltip having a negative impact on performance, especially on larger pages.
diff --git a/.changeset/thick-cups-shout.md b/.changeset/thick-cups-shout.md
new file mode 100644
index 000000000..3aabb9ad4
--- /dev/null
+++ b/.changeset/thick-cups-shout.md
@@ -0,0 +1,5 @@
+---
+"gitbook": patch
+---
+
+Fix crash during rendering of ogimage for VA sites with default icon.
diff --git a/README.md b/README.md
index 8a9f5c2fd..9b13767ff 100644
--- a/README.md
+++ b/README.md
@@ -56,13 +56,19 @@ git clone https://github.com/gitbookIO/gitbook.git
bun install
```
-4. Start your local development server.
+4. Run build.
+
+```
+bun build:v2
+```
+
+5. Start your local development server.
```
bun dev:v2
```
-5. Open a published GitBook space in your web browser, prefixing it with `http://localhost:3000/`.
+6. Open a published GitBook space in your web browser, prefixing it with `http://localhost:3000/`.
examples:
diff --git a/bun.lock b/bun.lock
index 3ff6d61c4..8afc0e702 100644
--- a/bun.lock
+++ b/bun.lock
@@ -85,8 +85,10 @@
"assert-never": "^1.2.1",
"bun-types": "^1.1.20",
"classnames": "^2.5.1",
+ "direction": "^2.0.1",
"event-iterator": "^2.0.0",
"framer-motion": "^10.16.14",
+ "image-size": "^2.0.2",
"js-cookie": "^3.0.5",
"jsontoxml": "^1.0.1",
"jwt-decode": "^4.0.0",
@@ -169,6 +171,7 @@
"assert-never": "^1.2.1",
"jwt-decode": "^4.0.0",
"next": "^15.3.2",
+ "object-identity": "^0.1.2",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"rison": "^0.1.1",
@@ -283,7 +286,7 @@
"react-dom": "^19.0.0",
},
"catalog": {
- "@gitbook/api": "^0.120.0",
+ "@gitbook/api": "^0.121.0",
},
"packages": {
"@ai-sdk/provider": ["@ai-sdk/provider@1.1.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-0M+qjp+clUD0R1E5eWQFhxEvWLNaOtGQRUaBn8CUABnSKredagq92hUS9VjOzGsTm37xLfpaxl97AVtbeOsHew=="],
@@ -648,7 +651,7 @@
"@fortawesome/fontawesome-svg-core": ["@fortawesome/fontawesome-svg-core@6.6.0", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "6.6.0" } }, "sha512-KHwPkCk6oRT4HADE7smhfsKudt9N/9lm6EJ5BVg0tD1yPA5hht837fB87F8pn15D8JfTqQOjhKTktwmLMiD7Kg=="],
- "@gitbook/api": ["@gitbook/api@0.120.0", "", { "dependencies": { "event-iterator": "^2.0.0", "eventsource-parser": "^3.0.0" } }, "sha512-FiRmPiSBwobMxmNjd14QkkOdM95BAPLDDRShgpS9Vsd8lHjNMyZfrJKVJTsJUuFcgYoi4cqNw9yu/TiUBUgv3g=="],
+ "@gitbook/api": ["@gitbook/api@0.121.0", "", { "dependencies": { "event-iterator": "^2.0.0", "eventsource-parser": "^3.0.0" } }, "sha512-o4/N24RM0Rg8S/3yPDjPmt6TbQF+1iZmg9q9QKxOxMqpQ2bZmMUqS7dSkeqEbEBMALx/m/x0xQlJbEJGbOwteg=="],
"@gitbook/cache-do": ["@gitbook/cache-do@workspace:packages/cache-do"],
@@ -1706,6 +1709,8 @@
"dir-glob": ["dir-glob@3.0.1", "", { "dependencies": { "path-type": "^4.0.0" } }, "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA=="],
+ "direction": ["direction@2.0.1", "", { "bin": { "direction": "cli.js" } }, "sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA=="],
+
"dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="],
"dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="],
@@ -2066,6 +2071,8 @@
"ignore": ["ignore@7.0.3", "", {}, "sha512-bAH5jbK/F3T3Jls4I0SO1hmPR0dKU0a7+SY6n1yzRtG54FLO8d6w/nxLFX2Nb7dBu6cCWXPaAME6cYqFUMmuCA=="],
+ "image-size": ["image-size@2.0.2", "", { "bin": { "image-size": "bin/image-size.js" } }, "sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w=="],
+
"import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
"import-lazy": ["import-lazy@2.1.0", "", {}, "sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A=="],
@@ -2438,6 +2445,8 @@
"object-hash": ["object-hash@3.0.0", "", {}, "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw=="],
+ "object-identity": ["object-identity@0.1.2", "", {}, "sha512-Px5puVllX5L2aBjbcfXpiG5xXeq6OE8RckryTeP2Zq+0PgYrCGJXmC6LblWgknKSJs11Je2W4U2NOWFj3t/QXQ=="],
+
"object-inspect": ["object-inspect@1.13.2", "", {}, "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g=="],
"object-treeify": ["object-treeify@1.1.33", "", {}, "sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A=="],
diff --git a/package.json b/package.json
index 1faa750ba..1cf5326c1 100644
--- a/package.json
+++ b/package.json
@@ -36,7 +36,7 @@
"workspaces": {
"packages": ["packages/*"],
"catalog": {
- "@gitbook/api": "^0.120.0"
+ "@gitbook/api": "^0.121.0"
}
},
"patchedDependencies": {
diff --git a/packages/gitbook-v2/package.json b/packages/gitbook-v2/package.json
index 5723cdbc7..cfa6e71b9 100644
--- a/packages/gitbook-v2/package.json
+++ b/packages/gitbook-v2/package.json
@@ -14,7 +14,8 @@
"react-dom": "^19.0.0",
"rison": "^0.1.1",
"server-only": "^0.0.1",
- "warn-once": "^0.1.1"
+ "warn-once": "^0.1.1",
+ "object-identity": "^0.1.2"
},
"devDependencies": {
"gitbook": "*",
diff --git a/packages/gitbook-v2/src/app/global-error.tsx b/packages/gitbook-v2/src/app/global-error.tsx
new file mode 100644
index 000000000..abf0186a4
--- /dev/null
+++ b/packages/gitbook-v2/src/app/global-error.tsx
@@ -0,0 +1,18 @@
+'use client';
+
+import NextError from 'next/error';
+
+export default function GlobalError({
+ error,
+}: {
+ error: Error & { digest?: string };
+}) {
+ console.error('Global error:', error);
+ return (
+
+
+
+
+
+ );
+}
diff --git a/packages/gitbook-v2/src/lib/cache.test.ts b/packages/gitbook-v2/src/lib/cache.test.ts
new file mode 100644
index 000000000..735b1fb06
--- /dev/null
+++ b/packages/gitbook-v2/src/lib/cache.test.ts
@@ -0,0 +1,80 @@
+import { describe, expect, it } from 'bun:test';
+import { withStableRef } from './cache';
+
+describe('withStableRef', () => {
+ it('should return primitive values as is', () => {
+ const toStableRef = withStableRef();
+
+ expect(toStableRef(42)).toBe(42);
+ expect(toStableRef('hello')).toBe('hello');
+ expect(toStableRef(true)).toBe(true);
+ expect(toStableRef(null)).toBe(null);
+ expect(toStableRef(undefined)).toBe(undefined);
+ });
+
+ it('should return the same reference for identical objects', () => {
+ const toStableRef = withStableRef();
+
+ const obj1 = { a: 1, b: 2 };
+ const obj2 = { a: 1, b: 2 };
+
+ const ref1 = toStableRef(obj1);
+ const ref2 = toStableRef(obj2);
+
+ expect(ref1).toBe(ref2);
+ expect(ref1).toBe(obj1);
+ expect(ref1).not.toBe(obj2);
+ });
+
+ it('should return the same reference for identical arrays', () => {
+ const toStableRef = withStableRef();
+
+ const arr1 = [1, 2, 3];
+ const arr2 = [1, 2, 3];
+
+ const ref1 = toStableRef(arr1);
+ const ref2 = toStableRef(arr2);
+
+ expect(ref1).toBe(ref2);
+ expect(ref1).toBe(arr1);
+ expect(ref1).not.toBe(arr2);
+ });
+
+ it('should return the same reference for identical nested objects', () => {
+ const toStableRef = withStableRef();
+
+ const obj1 = { a: { b: 1 }, c: [2, 3] };
+ const obj2 = { a: { b: 1 }, c: [2, 3] };
+
+ const ref1 = toStableRef(obj1);
+ const ref2 = toStableRef(obj2);
+
+ expect(ref1).toBe(ref2);
+ expect(ref1).toBe(obj1);
+ expect(ref1).not.toBe(obj2);
+ });
+
+ it('should return different references for different objects', () => {
+ const toStableRef = withStableRef();
+
+ const obj1 = { a: 1 };
+ const obj2 = { a: 2 };
+
+ const ref1 = toStableRef(obj1);
+ const ref2 = toStableRef(obj2);
+
+ expect(ref1).not.toBe(ref2);
+ });
+
+ it('should maintain reference stability across multiple calls', () => {
+ const toStableRef = withStableRef();
+
+ const obj = { a: 1 };
+ const ref1 = toStableRef(obj);
+ const ref2 = toStableRef(obj);
+ const ref3 = toStableRef(obj);
+
+ expect(ref1).toBe(ref2);
+ expect(ref2).toBe(ref3);
+ });
+});
diff --git a/packages/gitbook-v2/src/lib/cache.ts b/packages/gitbook-v2/src/lib/cache.ts
new file mode 100644
index 000000000..e05753907
--- /dev/null
+++ b/packages/gitbook-v2/src/lib/cache.ts
@@ -0,0 +1,59 @@
+import { identify } from 'object-identity';
+import * as React from 'react';
+
+/**
+ * Equivalent to `React.cache` but with support for non-primitive arguments.
+ * As `React.cache` only uses `Object.is` to compare arguments, it will not work with non-primitive arguments.
+ */
+export function cache(fn: (...args: Args) => Return) {
+ const cached = React.cache(fn);
+
+ return (...args: Args) => {
+ const toStableRef = getWithStableRef();
+ const stableArgs = args.map((value) => {
+ return toStableRef(value);
+ }) as Args;
+ return cached(...stableArgs);
+ };
+}
+
+/**
+ * To ensure memory is garbage collected between each request, we use a per-request cache to store the ref maps.
+ */
+const getWithStableRef = React.cache(withStableRef);
+
+/**
+ * Create a function that converts a value to a stable reference.
+ */
+export function withStableRef(): (value: T) => T {
+ const reverseIndex = new WeakMap