Skip to content
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 23 additions & 14 deletions src/core/fetch/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@
}
}

_fetchCover() {
_fetchCover(cb = noop) {
const { coverpage, requestHeaders } = this.config;
const query = this.route.query;
const root = getParentPath(this.route.path);
Expand All @@ -206,17 +206,26 @@
}

const coverOnly = Boolean(path) && this.config.onlyCover;
const next = () => cb(coverOnly);

Check failure on line 209 in src/core/fetch/index.js

View workflow job for this annotation

GitHub Actions / test-playwright (lts/*)

Expected 0 arguments, but got 1.

Check failure on line 209 in src/core/fetch/index.js

View workflow job for this annotation

GitHub Actions / lint (lts/*)

Expected 0 arguments, but got 1.

Check failure on line 209 in src/core/fetch/index.js

View workflow job for this annotation

GitHub Actions / test-jest (lts/*, ubuntu-latest)

Expected 0 arguments, but got 1.

Check failure on line 209 in src/core/fetch/index.js

View workflow job for this annotation

GitHub Actions / test-jest (lts/*, macos-latest)

Expected 0 arguments, but got 1.

Check failure on line 209 in src/core/fetch/index.js

View workflow job for this annotation

GitHub Actions / test-jest (lts/*, windows-latest)

Expected 0 arguments, but got 1.
if (path) {
path = this.router.getFile(root + path);
this.coverIsHTML = /\.html$/g.test(path);
get(path + stringifyQuery(query, ['id']), false, requestHeaders).then(
text => this._renderCover(text, coverOnly),
text => this._renderCover(text, coverOnly, next),
(event, response) => {
this.coverIsHTML = false;
this._renderCover(
`# ${response.status} - ${response.statusText}`,
coverOnly,
next,
);
},
);
} else {
this._renderCover(null, coverOnly);
this._renderCover(null, coverOnly, next);
}

return coverOnly;
} else {
cb(false);

Check failure on line 228 in src/core/fetch/index.js

View workflow job for this annotation

GitHub Actions / test-playwright (lts/*)

Expected 0 arguments, but got 1.

Check failure on line 228 in src/core/fetch/index.js

View workflow job for this annotation

GitHub Actions / lint (lts/*)

Expected 0 arguments, but got 1.

Check failure on line 228 in src/core/fetch/index.js

View workflow job for this annotation

GitHub Actions / test-jest (lts/*, ubuntu-latest)

Expected 0 arguments, but got 1.

Check failure on line 228 in src/core/fetch/index.js

View workflow job for this annotation

GitHub Actions / test-jest (lts/*, macos-latest)

Expected 0 arguments, but got 1.

Check failure on line 228 in src/core/fetch/index.js

View workflow job for this annotation

GitHub Actions / test-jest (lts/*, windows-latest)

Expected 0 arguments, but got 1.
}
}

Expand All @@ -226,16 +235,16 @@
cb();
};

const onlyCover = this._fetchCover();

if (onlyCover) {
done();
} else {
this._fetch(() => {
onNavigate();
this._fetchCover(onlyCover => {
if (onlyCover) {
done();
});
}
} else {
this._fetch(() => {
onNavigate();
done();
});
}
});
}

/**
Expand Down
115 changes: 36 additions & 79 deletions src/core/render/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@
});
}

_renderCover(text, coverOnly) {
_renderCover(text, coverOnly, next) {
const el = dom.getNode('.cover');
const rootElm = document.documentElement;
const coverBg = getComputedStyle(rootElm).getPropertyValue('--cover-bg');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This variable is now unused in the PR. Is the feature relating to that now deleted? If so, we need to fix that.

Also there are type errors I'm fixing (my recent changes added type checking to the code base).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fixed the error, a method moved to the utils file.

Now the only thing failing is snapshots for the cover page. The diff shows this failure:

Screenshot 2025-12-05 at 7 01 30 PM

Looks like a couple classes are missing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's the visual difference:

Before:

Screenshot 2025-12-05 at 7 05 51 PM

After (broken):

Screenshot 2025-12-05 at 7 05 49 PM

Maybe I missed something when merging in develop?

Expand All @@ -453,99 +453,56 @@

if (!text) {
el.classList.remove('show');
next();
return;
}

el.classList.add('show');

let html = this.coverIsHTML
? text
: /** @type {Compiler} */ (this.compiler).cover(text);

if (!coverBg) {
const mdBgMatch = html
const callback = html => {
const m = html
.trim()
.match(
'<p><img.*?data-origin="(.*?)".*?alt="(.*?)"[^>]*?>([^<]*?)</p>$',
);

let mdCoverBg;
.match('<p><img.*?data-origin="(.*?)"[^a]+alt="(.*?)">([^<]*?)</p>$');

if (mdBgMatch) {
const [bgMatch, bgValue, bgType] = mdBgMatch;
if (m) {
if (m[2] === 'color') {
el.style.background = m[1] + (m[3] || '');

Check failure on line 469 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / test-playwright (lts/*)

Property 'style' does not exist on type 'Element'.

Check failure on line 469 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / lint (lts/*)

Property 'style' does not exist on type 'Element'.

Check failure on line 469 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / test-jest (lts/*, ubuntu-latest)

Property 'style' does not exist on type 'Element'.

Check failure on line 469 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / test-jest (lts/*, macos-latest)

Property 'style' does not exist on type 'Element'.

Check failure on line 469 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / test-jest (lts/*, windows-latest)

Property 'style' does not exist on type 'Element'.
} else {
let path = m[1];

// Color
if (bgType === 'color') {
mdCoverBg = bgValue;
}
// Image
else {
const path = !isAbsolutePath(bgValue)
? getPath(this.router.getBasePath(), bgValue)
: bgValue;
dom.toggleClass(el, 'add', 'has-mask');

Check failure on line 473 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / test-playwright (lts/*)

Property 'toggleClass' does not exist on type 'typeof import("/home/runner/work/docsify/docsify/src/core/util/dom")'.

Check failure on line 473 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / lint (lts/*)

Property 'toggleClass' does not exist on type 'typeof import("/home/runner/work/docsify/docsify/src/core/util/dom")'.

Check failure on line 473 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / test-jest (lts/*, ubuntu-latest)

Property 'toggleClass' does not exist on type 'typeof import("/home/runner/work/docsify/docsify/src/core/util/dom")'.

Check failure on line 473 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / test-jest (lts/*, macos-latest)

Property 'toggleClass' does not exist on type 'typeof import("/Users/runner/work/docsify/docsify/src/core/util/dom")'.

Check failure on line 473 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / test-jest (lts/*, windows-latest)

Property 'toggleClass' does not exist on type 'typeof import("D:/a/docsify/docsify/src/core/util/dom")'.
if (!isAbsolutePath(m[1])) {
path = getPath(this.router.getBasePath(), m[1]);
}

mdCoverBg = `center center / cover url(${path})`;
el.style.backgroundImage = `url(${path})`;

Check failure on line 478 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / test-playwright (lts/*)

Property 'style' does not exist on type 'Element'.

Check failure on line 478 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / lint (lts/*)

Property 'style' does not exist on type 'Element'.

Check failure on line 478 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / test-jest (lts/*, ubuntu-latest)

Property 'style' does not exist on type 'Element'.

Check failure on line 478 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / test-jest (lts/*, macos-latest)

Property 'style' does not exist on type 'Element'.

Check failure on line 478 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / test-jest (lts/*, windows-latest)

Property 'style' does not exist on type 'Element'.
el.style.backgroundSize = 'cover';

Check failure on line 479 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / test-playwright (lts/*)

Property 'style' does not exist on type 'Element'.

Check failure on line 479 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / lint (lts/*)

Property 'style' does not exist on type 'Element'.

Check failure on line 479 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / test-jest (lts/*, ubuntu-latest)

Property 'style' does not exist on type 'Element'.

Check failure on line 479 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / test-jest (lts/*, macos-latest)

Property 'style' does not exist on type 'Element'.

Check failure on line 479 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / test-jest (lts/*, windows-latest)

Property 'style' does not exist on type 'Element'.
el.style.backgroundPosition = 'center center';

Check failure on line 480 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / test-playwright (lts/*)

Property 'style' does not exist on type 'Element'.

Check failure on line 480 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / lint (lts/*)

Property 'style' does not exist on type 'Element'.

Check failure on line 480 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / test-jest (lts/*, ubuntu-latest)

Property 'style' does not exist on type 'Element'.

Check failure on line 480 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / test-jest (lts/*, macos-latest)

Property 'style' does not exist on type 'Element'.

Check failure on line 480 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / test-jest (lts/*, windows-latest)

Property 'style' does not exist on type 'Element'.
}

html = html.replace(bgMatch, '');
html = html.replace(m[0], '');
}
// Gradient background
else {
const degrees = Math.round((Math.random() * 120) / 2);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this gradient background code is removed. Need to restore it. @YiiGuxing would you mind restoring the random gradient?


let hue1 = Math.round(Math.random() * 360);
let hue2 = Math.round(Math.random() * 360);

// Ensure hue1 and hue2 are at least 50 degrees apart
if (Math.abs(hue1 - hue2) < 50) {
const hueShift = Math.round(Math.random() * 25) + 25;

hue1 = Math.max(hue1, hue2) + hueShift;
hue2 = Math.min(hue1, hue2) - hueShift;
}

// OKLCH color
if (window?.CSS?.supports('color', 'oklch(0 0 0 / 1%)')) {
const l = 90; // Lightness
const c = 20; // Chroma

// prettier-ignore
mdCoverBg = `linear-gradient(
${degrees}deg,
oklch(${l}% ${c}% ${hue1}) 0%,
oklch(${l}% ${c}% ${hue2}) 100%
)`.replace(/\s+/g, ' ');
}
// HSL color (Legacy)
else {
const s = 100; // Saturation
const l = 85; // Lightness
const o = 100; // Opacity

// prettier-ignore
mdCoverBg = `linear-gradient(
${degrees}deg,
hsl(${hue1} ${s}% ${l}% / ${o}%) 0%,
hsl(${hue2} ${s}% ${l}% / ${o}%) 100%
)`.replace(/\s+/g, ' ');
}
}
this._renderTo('.cover-main', html);
next();
};

rootElm.style.setProperty('--cover-bg', mdCoverBg);
// TODO: Call the 'beforeEach' and 'afterEach' hooks.
// However, when the cover and the home page are on the same page,
// the 'beforeEach' and 'afterEach' hooks are called multiple times.
// It is difficult to determine the target of the hook within the
// hook functions. We might need to make some changes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@YiiGuxing I think we should update the hooks in a backwards compatible way, such that the hook can somehow see which content it is called for. For example it can see string values like "cover", "sidebar", etc. The default (right now no sort of value to determine the target of the hook) would work the same as before for anyone who hasn't updated their plugin code.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ultimate result should be, that all markdown files are handled the same, regardless where in the app they are rendered (cover, sidebar, nav, etc) but any plugin can easily decide which to handle.

Something like

hook.doneEach((section) => {
  if (section === 'cover') {
    // run logic after cover rendered
  } else if (section === 'sidebar') {
    // run logic after sidebar rendered
  } else if (section === 'nav') {
    // run logic after nav bar rendered
  } else if (section === 'main') {
    // run logic after main content rendered (new way)
  } else {
    // run logic after main content rendered (old way, fallback)
  }
})

or similar.

if (this.coverIsHTML) {
callback(text);
} else {
prerenderEmbed(
{

Check failure on line 499 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / test-playwright (lts/*)

Argument of type '{ compiler: Compiler | undefined; raw: any; }' is not assignable to parameter of type '{ compiler: any; raw?: string | undefined; fetch: any; }'.

Check failure on line 499 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / lint (lts/*)

Argument of type '{ compiler: Compiler | undefined; raw: any; }' is not assignable to parameter of type '{ compiler: any; raw?: string | undefined; fetch: any; }'.

Check failure on line 499 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / test-jest (lts/*, ubuntu-latest)

Argument of type '{ compiler: Compiler | undefined; raw: any; }' is not assignable to parameter of type '{ compiler: any; raw?: string | undefined; fetch: any; }'.

Check failure on line 499 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / test-jest (lts/*, macos-latest)

Argument of type '{ compiler: Compiler | undefined; raw: any; }' is not assignable to parameter of type '{ compiler: any; raw?: string | undefined; fetch: any; }'.

Check failure on line 499 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / test-jest (lts/*, windows-latest)

Argument of type '{ compiler: Compiler | undefined; raw: any; }' is not assignable to parameter of type '{ compiler: any; raw?: string | undefined; fetch: any; }'.
compiler: this.compiler,
raw: text,
},
tokens => callback(this.compiler.cover(tokens)),

Check failure on line 503 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / test-playwright (lts/*)

Object is possibly 'undefined'.

Check failure on line 503 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / lint (lts/*)

Object is possibly 'undefined'.

Check failure on line 503 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / test-jest (lts/*, ubuntu-latest)

Object is possibly 'undefined'.

Check failure on line 503 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / test-jest (lts/*, macos-latest)

Object is possibly 'undefined'.

Check failure on line 503 in src/core/render/index.js

View workflow job for this annotation

GitHub Actions / test-jest (lts/*, windows-latest)

Object is possibly 'undefined'.
);
}

dom.setHTML('.cover-main', html);

// Button styles
dom
.findAll('.cover-main > p:last-of-type > a:not([class])')
.forEach(elm => {
const buttonType = elm.matches(':first-child')
? 'primary'
: 'secondary';

elm.classList.add('button', buttonType);
});
}

_updateRender() {
Expand Down
33 changes: 33 additions & 0 deletions test/e2e/embed-files.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import docsifyInit from '../helpers/docsify-init.js';
import { test, expect } from './fixtures/docsify-init-fixture.js';

test.describe('Embed files', () => {
const routes = {
'fragment.md': '## Fragment',
};

test('embed into homepage', async ({ page }) => {
await docsifyInit({
routes,
markdown: {
homepage: "# Hello World\n\n[fragment](fragment.md ':include')",
},
// _logHTML: {},
});

await expect(page.locator('#main')).toContainText('Fragment');
});

test('embed into cover', async ({ page }) => {
await docsifyInit({
routes,
markdown: {
homepage: '# Hello World',
coverpage: "# Cover\n\n[fragment](fragment.md ':include')",
},
// _logHTML: {},
});

await expect(page.locator('.cover-main')).toContainText('Fragment');
});
});
68 changes: 68 additions & 0 deletions test/e2e/plugins.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,74 @@ test.describe('Plugins', () => {
});
});

test.describe('doneEach()', () => {
test('callback after cover loads', async ({ page }) => {
const consoleMessages = [];

page.on('console', msg => consoleMessages.push(msg.text()));

await docsifyInit({
config: {
plugins: [
function (hook) {
hook.doneEach(() => {
const homepageTitle = document.querySelector('#homepage-title');
const coverTitle = document.querySelector('#cover-title');
console.log(homepageTitle?.textContent);
console.log(coverTitle?.textContent);
});
},
],
},
markdown: {
homepage: '# Hello World :id=homepage-title',
coverpage: () => {
return new Promise(resolve => {
setTimeout(() => resolve('# Cover Page :id=cover-title'), 500);
});
},
},
// _logHTML: {},
});

await expect(consoleMessages).toEqual(['Hello World', 'Cover Page']);
});

test('only cover', async ({ page }) => {
const consoleMessages = [];

page.on('console', msg => consoleMessages.push(msg.text()));

await docsifyInit({
config: {
onlyCover: true,
plugins: [
function (hook) {
hook.doneEach(() => {
const homepageTitle = document.querySelector('#homepage-title');
const coverTitle = document.querySelector('#cover-title');
console.log(homepageTitle?.textContent);
console.log(coverTitle?.textContent);
});
},
],
},
markdown: {
homepage: '# Hello World :id=homepage-title',
coverpage: () => {
return new Promise(resolve => {
setTimeout(() => resolve('# Cover Page :id=cover-title'), 500);
});
},
},
waitForSelector: '.cover-main > *:first-child',
// _logHTML: {},
});

await expect(consoleMessages).toEqual(['undefined', 'Cover Page']);
});
});

test.describe('route data accessible to plugins', () => {
let routeData = null;

Expand Down
Loading
Loading