feat: 支持后端Argon代码块运行时
This commit is contained in:
parent
d9aa7d9cc5
commit
52cbef7d65
@ -1,4 +1,4 @@
|
|||||||
import { expect, test, type Page, type TestInfo } from '@playwright/test';
|
import { expect, test, type Page, type Route, type TestInfo } from '@playwright/test';
|
||||||
import { readFile } from 'node:fs/promises';
|
import { readFile } from 'node:fs/promises';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -9,6 +9,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
captureAllRouteStates,
|
captureAllRouteStates,
|
||||||
gotoLocalRoute,
|
gotoLocalRoute,
|
||||||
|
waitForArgonPage,
|
||||||
writeArgonArtifact,
|
writeArgonArtifact,
|
||||||
} from './argonParityHelpers';
|
} from './argonParityHelpers';
|
||||||
|
|
||||||
@ -16,6 +17,28 @@ interface PageFixture {
|
|||||||
page: Page;
|
page: Page;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const BACKEND_BASE_CODEBLOCK_ARTICLE = {
|
||||||
|
authorName: 'KwiTsukasa',
|
||||||
|
categoriesResolved: [
|
||||||
|
{
|
||||||
|
name: 'public',
|
||||||
|
slug: 'public',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
contentHtml: `<h2 class="wp-block-heading" id="header-id-1">后端代码块运行时</h2>
|
||||||
|
<pre class="wp-block-code hljs-codeblock"><code class="hljs typescript">const a = 1;
|
||||||
|
const b = a + 1;</code></pre>`,
|
||||||
|
date: '2026-07-01T16:40:00.000Z',
|
||||||
|
excerptText: '后端只生成基础 Argon 代码块,Blog Web 运行时补齐行号和控制条。',
|
||||||
|
id: 909001,
|
||||||
|
modified: '2026-07-01T16:40:00.000Z',
|
||||||
|
slug: 'backend-codeblock-runtime',
|
||||||
|
tagsResolved: [],
|
||||||
|
title: {
|
||||||
|
rendered: '后端代码块运行时',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers representative Argon interaction checks against the local Vue implementation.
|
* Registers representative Argon interaction checks against the local Vue implementation.
|
||||||
*/
|
*/
|
||||||
@ -33,6 +56,7 @@ function registerLocalInteractionSuite() {
|
|||||||
test('local post page exposes share, comments, and reading progress', createPostInteractionTest());
|
test('local post page exposes share, comments, and reading progress', createPostInteractionTest());
|
||||||
test('local post codeblocks match Argon hljs layout', createPostCodeblockSurfaceTest());
|
test('local post codeblocks match Argon hljs layout', createPostCodeblockSurfaceTest());
|
||||||
test('local post codeblock controls match Argon runtime toggles', createPostCodeblockControlBehaviorTest());
|
test('local post codeblock controls match Argon runtime toggles', createPostCodeblockControlBehaviorTest());
|
||||||
|
test('local post upgrades backend-generated base Argon codeblocks at runtime', createBackendBaseCodeblockRuntimeTest());
|
||||||
test('local post codeblock copy exposes Argon toast feedback', createPostCodeblockCopyFeedbackTest());
|
test('local post codeblock copy exposes Argon toast feedback', createPostCodeblockCopyFeedbackTest());
|
||||||
test('local post links match live WordPress Argon inline semantics', createPostInlineLinkSurfaceTest());
|
test('local post links match live WordPress Argon inline semantics', createPostInlineLinkSurfaceTest());
|
||||||
test('local post fancybox wrappers open live Argon lightbox shell', createPostFancyboxRuntimeTest());
|
test('local post fancybox wrappers open live Argon lightbox shell', createPostFancyboxRuntimeTest());
|
||||||
@ -764,6 +788,40 @@ function createPostCodeblockControlBehaviorTest() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns Playwright test body that verifies backend-generated base codeblocks are upgraded on page bind.
|
||||||
|
*/
|
||||||
|
function createBackendBaseCodeblockRuntimeTest() {
|
||||||
|
/**
|
||||||
|
* @param fixtures Playwright page fixture used to intercept the public article API and render a base codeblock.
|
||||||
|
*/
|
||||||
|
return async function runBackendBaseCodeblockRuntimeTest({ page }: PageFixture) {
|
||||||
|
await page.route('**/api/blog/article/public/list**', fulfillBackendBaseCodeblockArticleList);
|
||||||
|
await page.route('**/api/blog/article/public/detail**', fulfillBackendBaseCodeblockArticleDetail);
|
||||||
|
await page.goto('/#/post/backend-codeblock-runtime', { waitUntil: 'domcontentloaded' });
|
||||||
|
await waitForArgonPage(page);
|
||||||
|
|
||||||
|
const pre = page.locator('#post_content pre.hljs-codeblock, #post_content pre.wp-block-code.hljs-codeblock').first();
|
||||||
|
const lineCodes = pre.locator('.hljs-ln-code');
|
||||||
|
|
||||||
|
await expect(page.getByText('后端代码块运行时').first()).toBeVisible();
|
||||||
|
await expect(pre).toBeVisible();
|
||||||
|
await expect(pre.locator('table.hljs-ln')).toHaveCount(1);
|
||||||
|
await expect(pre.locator('table.hljs-ln > tbody > tr')).toHaveCount(2);
|
||||||
|
await expect(pre.locator(':scope > .hljs-control')).toHaveCount(1);
|
||||||
|
await expect(pre.locator('.hljs-control-btn')).toHaveCount(4);
|
||||||
|
await expect(lineCodes.nth(0)).toHaveText('const a = 1;');
|
||||||
|
await expect(lineCodes.nth(0)).toHaveAttribute('data-line-number', '1');
|
||||||
|
await expect(lineCodes.nth(1)).toHaveText('const b = a + 1;');
|
||||||
|
await expect(lineCodes.nth(1)).toHaveAttribute('data-line-number', '2');
|
||||||
|
|
||||||
|
await pre.locator('.hljs-control-toggle-break-line').click();
|
||||||
|
await expect(pre).toHaveClass(/hljs-break-line/);
|
||||||
|
await pre.locator('.hljs-control-toggle-linenumber').click();
|
||||||
|
await expect(pre).toHaveClass(/hljs-hide-linenumber/);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns Playwright test body that verifies code copy produces Argon's toast-style user feedback.
|
* @returns Playwright test body that verifies code copy produces Argon's toast-style user feedback.
|
||||||
*/
|
*/
|
||||||
@ -790,6 +848,37 @@ function createPostCodeblockCopyFeedbackTest() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param route Intercepted Blog article list request from the local Vue app.
|
||||||
|
*/
|
||||||
|
async function fulfillBackendBaseCodeblockArticleList(route: Route) {
|
||||||
|
await route.fulfill({
|
||||||
|
contentType: 'application/json',
|
||||||
|
json: {
|
||||||
|
code: 0,
|
||||||
|
data: {
|
||||||
|
list: [BACKEND_BASE_CODEBLOCK_ARTICLE],
|
||||||
|
total: 1,
|
||||||
|
},
|
||||||
|
msg: 'ok',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param route Intercepted Blog article detail request from the local Vue app.
|
||||||
|
*/
|
||||||
|
async function fulfillBackendBaseCodeblockArticleDetail(route: Route) {
|
||||||
|
await route.fulfill({
|
||||||
|
contentType: 'application/json',
|
||||||
|
json: {
|
||||||
|
code: 0,
|
||||||
|
data: BACKEND_BASE_CODEBLOCK_ARTICLE,
|
||||||
|
msg: 'ok',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns Playwright test body that verifies inline WordPress links inherit Argon's live color and hover underline.
|
* @returns Playwright test body that verifies inline WordPress links inherit Argon's live color and hover underline.
|
||||||
*/
|
*/
|
||||||
|
|||||||
117
src/__tests__/argonCodeblockFactory.spec.ts
Normal file
117
src/__tests__/argonCodeblockFactory.spec.ts
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
import { describe, expect, it, afterEach } from 'vitest';
|
||||||
|
|
||||||
|
import { upgradeArgonCodeblocks } from '@/factories/argonCodeblockFactory';
|
||||||
|
|
||||||
|
describe('upgradeArgonCodeblocks', registerArgonCodeblockFactoryTests);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers Argon codeblock runtime upgrade tests against jsdom-rendered post content.
|
||||||
|
*/
|
||||||
|
function registerArgonCodeblockFactoryTests() {
|
||||||
|
afterEach(resetDocumentBody);
|
||||||
|
|
||||||
|
it('upgrades backend-generated base codeblocks into Argon line tables and controls', runBaseCodeblockUpgradeTest);
|
||||||
|
it('ignores the serializer newline appended to markdown fenced code blocks', runTrailingNewlineCodeblockTest);
|
||||||
|
it('keeps existing WordPress runtime codeblocks idempotent', runExistingRuntimeCodeblockIdempotenceTest);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears article markup between tests because the factory mutates the supplied DOM root in place.
|
||||||
|
*/
|
||||||
|
function resetDocumentBody() {
|
||||||
|
document.body.innerHTML = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies plain backend code text becomes the line-number table and control shell expected by Argon.
|
||||||
|
*/
|
||||||
|
function runBaseCodeblockUpgradeTest() {
|
||||||
|
document.body.innerHTML = `
|
||||||
|
<article id="post_content">
|
||||||
|
<pre class="wp-block-code hljs-codeblock"><code class="hljs typescript">const a = 1;
|
||||||
|
const b = a + 1;</code></pre>
|
||||||
|
</article>
|
||||||
|
`;
|
||||||
|
const root = document.querySelector<HTMLElement>('#post_content')!;
|
||||||
|
|
||||||
|
upgradeArgonCodeblocks(root);
|
||||||
|
|
||||||
|
const pre = root.querySelector<HTMLElement>('pre.wp-block-code.hljs-codeblock')!;
|
||||||
|
const rows = pre.querySelectorAll('table.hljs-ln > tbody > tr');
|
||||||
|
const lineNumbers = pre.querySelectorAll<HTMLElement>('.hljs-ln-numbers');
|
||||||
|
const lineNumberInners = pre.querySelectorAll<HTMLElement>('.hljs-ln-n');
|
||||||
|
const lineCodes = pre.querySelectorAll<HTMLElement>('.hljs-ln-code');
|
||||||
|
|
||||||
|
expect(rows).toHaveLength(2);
|
||||||
|
expect(lineNumbers).toHaveLength(2);
|
||||||
|
expect(lineNumberInners).toHaveLength(2);
|
||||||
|
expect(lineCodes).toHaveLength(2);
|
||||||
|
expect(lineNumbers[0]?.dataset.lineNumber).toBe('1');
|
||||||
|
expect(lineNumberInners[0]?.dataset.lineNumber).toBe('1');
|
||||||
|
expect(lineCodes[0]?.dataset.lineNumber).toBe('1');
|
||||||
|
expect(lineCodes[0]?.textContent).toBe('const a = 1;');
|
||||||
|
expect(lineNumbers[1]?.dataset.lineNumber).toBe('2');
|
||||||
|
expect(lineNumberInners[1]?.dataset.lineNumber).toBe('2');
|
||||||
|
expect(lineCodes[1]?.dataset.lineNumber).toBe('2');
|
||||||
|
expect(lineCodes[1]?.textContent).toBe('const b = a + 1;');
|
||||||
|
expect(pre.querySelectorAll(':scope > .hljs-control')).toHaveLength(1);
|
||||||
|
expect(pre.querySelector('.hljs-control-toggle-linenumber')).not.toBeNull();
|
||||||
|
expect(pre.querySelector('.hljs-control-toggle-break-line')).not.toBeNull();
|
||||||
|
expect(pre.querySelector('.hljs-control-copy')).not.toBeNull();
|
||||||
|
expect(pre.querySelector('.hljs-control-fullscreen')).not.toBeNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies API-rendered fenced code blocks do not show an extra blank Argon line for serializer-only newline.
|
||||||
|
*/
|
||||||
|
function runTrailingNewlineCodeblockTest() {
|
||||||
|
document.body.innerHTML = `
|
||||||
|
<article id="post_content">
|
||||||
|
<pre class="wp-block-code hljs-codeblock"><code class="hljs typescript">const a = 1;
|
||||||
|
const b = a + 1;
|
||||||
|
</code></pre>
|
||||||
|
</article>
|
||||||
|
`;
|
||||||
|
const root = document.querySelector<HTMLElement>('#post_content')!;
|
||||||
|
|
||||||
|
upgradeArgonCodeblocks(root);
|
||||||
|
|
||||||
|
const pre = root.querySelector<HTMLElement>('pre.wp-block-code.hljs-codeblock')!;
|
||||||
|
const lineCodes = pre.querySelectorAll<HTMLElement>('.hljs-ln-code');
|
||||||
|
|
||||||
|
expect(pre.querySelectorAll('table.hljs-ln > tbody > tr')).toHaveLength(2);
|
||||||
|
expect(lineCodes[0]?.textContent).toBe('const a = 1;');
|
||||||
|
expect(lineCodes[1]?.textContent).toBe('const b = a + 1;');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies saved WordPress runtime DOM is detected and not wrapped with duplicate tables or controls.
|
||||||
|
*/
|
||||||
|
function runExistingRuntimeCodeblockIdempotenceTest() {
|
||||||
|
document.body.innerHTML = `
|
||||||
|
<article id="post_content">
|
||||||
|
<pre class="wp-block-code hljs-codeblock">
|
||||||
|
<code class="hljs typescript" hljs-codeblock-inner="">
|
||||||
|
<table class="hljs-ln"><tbody><tr>
|
||||||
|
<td class="hljs-ln-line hljs-ln-numbers hljs"><div class="hljs-ln-n"></div></td>
|
||||||
|
<td class="hljs-ln-line hljs-ln-code">const a = 1;</td>
|
||||||
|
</tr></tbody></table>
|
||||||
|
</code>
|
||||||
|
<div class="hljs-control hljs hljs-title">
|
||||||
|
<div class="hljs-control-btn hljs-control-toggle-linenumber"><i class="fa fa-list"></i></div>
|
||||||
|
</div>
|
||||||
|
</pre>
|
||||||
|
</article>
|
||||||
|
`;
|
||||||
|
const root = document.querySelector<HTMLElement>('#post_content')!;
|
||||||
|
|
||||||
|
upgradeArgonCodeblocks(root);
|
||||||
|
upgradeArgonCodeblocks(root);
|
||||||
|
|
||||||
|
const pre = root.querySelector<HTMLElement>('pre.wp-block-code.hljs-codeblock')!;
|
||||||
|
|
||||||
|
expect(pre.querySelectorAll('table.hljs-ln')).toHaveLength(1);
|
||||||
|
expect(pre.querySelectorAll(':scope > .hljs-control')).toHaveLength(1);
|
||||||
|
expect(pre.querySelectorAll('table.hljs-ln > tbody > tr')).toHaveLength(1);
|
||||||
|
expect(pre.querySelector('.hljs-ln-code')?.textContent).toBe('const a = 1;');
|
||||||
|
}
|
||||||
206
src/factories/argonCodeblockFactory.ts
Normal file
206
src/factories/argonCodeblockFactory.ts
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
const ARGON_CODEBLOCK_SELECTOR = 'pre.hljs-codeblock, pre.wp-block-code.hljs-codeblock';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param root Rendered post content root that may contain backend-generated base Highlight.js blocks.
|
||||||
|
*
|
||||||
|
* Upgrades plain `<pre><code>` Argon codeblocks into the line-number table and control shell that the
|
||||||
|
* live WordPress Argon runtime normally creates, while preserving existing runtime DOM idempotently.
|
||||||
|
*/
|
||||||
|
export function upgradeArgonCodeblocks(root: HTMLElement) {
|
||||||
|
root.querySelectorAll<HTMLElement>(ARGON_CODEBLOCK_SELECTOR).forEach(upgradeArgonCodeblock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param codeBlock Highlight.js `<pre>` block to upgrade in place.
|
||||||
|
*/
|
||||||
|
function upgradeArgonCodeblock(codeBlock: HTMLElement) {
|
||||||
|
const codeElement = getDirectCodeElement(codeBlock);
|
||||||
|
if (!codeElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasDirectLineNumberTable(codeElement)) {
|
||||||
|
const lines = splitCodeText(codeElement.textContent ?? '');
|
||||||
|
codeElement.replaceChildren(createArgonLineTable(lines));
|
||||||
|
codeElement.setAttribute('hljs-codeblock-inner', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!getDirectControl(codeBlock)) {
|
||||||
|
codeBlock.append(createArgonControl());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param codeBlock Highlight.js `<pre>` block whose immediate `<code>` child owns code content.
|
||||||
|
* @returns Direct `<code>` child, or null when the block is malformed.
|
||||||
|
*/
|
||||||
|
function getDirectCodeElement(codeBlock: HTMLElement) {
|
||||||
|
for (const child of codeBlock.children) {
|
||||||
|
if (child.tagName.toLowerCase() === 'code') {
|
||||||
|
return child as HTMLElement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param codeElement Direct code element that may already contain Argon's line-number table.
|
||||||
|
* @returns Whether the direct code child already owns a runtime `table.hljs-ln`.
|
||||||
|
*/
|
||||||
|
function hasDirectLineNumberTable(codeElement: HTMLElement) {
|
||||||
|
for (const child of codeElement.children) {
|
||||||
|
if (child.matches('table.hljs-ln')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param codeBlock Highlight.js `<pre>` block that may already own Argon's control shell.
|
||||||
|
* @returns Direct `.hljs-control` child, or null when controls still need to be created.
|
||||||
|
*/
|
||||||
|
function getDirectControl(codeBlock: HTMLElement) {
|
||||||
|
for (const child of codeBlock.children) {
|
||||||
|
if (child.matches('.hljs-control')) {
|
||||||
|
return child as HTMLElement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param codeText Plain code text from backend-generated HTML.
|
||||||
|
* @returns Code lines preserving author-written empty lines while dropping one serializer-only trailing newline.
|
||||||
|
*/
|
||||||
|
function splitCodeText(codeText: string) {
|
||||||
|
const lines = codeText.replace(/\r\n?/g, '\n').split('\n');
|
||||||
|
if (lines.length > 1 && lines[lines.length - 1] === '') {
|
||||||
|
return lines.slice(0, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param lines Plain code lines to render into Highlight.js line-number rows.
|
||||||
|
* @returns Argon-compatible line-number table.
|
||||||
|
*/
|
||||||
|
function createArgonLineTable(lines: string[]) {
|
||||||
|
const table = document.createElement('table');
|
||||||
|
const tbody = document.createElement('tbody');
|
||||||
|
table.className = 'hljs-ln';
|
||||||
|
|
||||||
|
for (let index = 0; index < lines.length; index += 1) {
|
||||||
|
tbody.append(createArgonLineRow(lines[index] ?? '', index + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
table.append(tbody);
|
||||||
|
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param line Plain code text for one rendered row.
|
||||||
|
* @param lineNumber One-based line number mirrored into Argon's `data-line-number` attributes.
|
||||||
|
* @returns Table row containing the line-number gutter and code cell.
|
||||||
|
*/
|
||||||
|
function createArgonLineRow(line: string, lineNumber: number) {
|
||||||
|
const row = document.createElement('tr');
|
||||||
|
row.append(createArgonLineNumberCell(lineNumber), createArgonLineCodeCell(line, lineNumber));
|
||||||
|
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param lineNumber One-based line number for the gutter cell.
|
||||||
|
* @returns Argon-compatible line-number cell with inner `.hljs-ln-n` marker.
|
||||||
|
*/
|
||||||
|
function createArgonLineNumberCell(lineNumber: number) {
|
||||||
|
const lineNumberText = String(lineNumber);
|
||||||
|
const cell = document.createElement('td');
|
||||||
|
const inner = document.createElement('div');
|
||||||
|
|
||||||
|
cell.className = 'hljs-ln-line hljs-ln-numbers hljs';
|
||||||
|
cell.dataset.lineNumber = lineNumberText;
|
||||||
|
inner.className = 'hljs-ln-n';
|
||||||
|
inner.dataset.lineNumber = lineNumberText;
|
||||||
|
cell.append(inner);
|
||||||
|
|
||||||
|
return cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param line Plain code text for one rendered row.
|
||||||
|
* @param lineNumber One-based line number mirrored for selection/copy metadata.
|
||||||
|
* @returns Argon-compatible code cell containing only text nodes from backend source text.
|
||||||
|
*/
|
||||||
|
function createArgonLineCodeCell(line: string, lineNumber: number) {
|
||||||
|
const cell = document.createElement('td');
|
||||||
|
|
||||||
|
cell.className = 'hljs-ln-line hljs-ln-code';
|
||||||
|
cell.dataset.lineNumber = String(lineNumber);
|
||||||
|
cell.textContent = line;
|
||||||
|
|
||||||
|
return cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns Argon-compatible codeblock control shell used by delegated post content effects.
|
||||||
|
*/
|
||||||
|
function createArgonControl() {
|
||||||
|
const control = document.createElement('div');
|
||||||
|
control.className = 'hljs-control hljs hljs-title';
|
||||||
|
control.append(
|
||||||
|
createArgonControlButton(
|
||||||
|
'hljs-control-toggle-linenumber',
|
||||||
|
'fa fa-list',
|
||||||
|
[
|
||||||
|
['tooltip-hide-linenumber', '隐藏行号'],
|
||||||
|
['tooltip-show-linenumber', '显示行号'],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
createArgonControlButton(
|
||||||
|
'hljs-control-toggle-break-line',
|
||||||
|
'fa fa-align-left',
|
||||||
|
[
|
||||||
|
['tooltip-enable-breakline', '开启折行'],
|
||||||
|
['tooltip-disable-breakline', '关闭折行'],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
createArgonControlButton('hljs-control-copy', 'fa fa-clipboard', [['tooltip', '复制']]),
|
||||||
|
createArgonControlButton(
|
||||||
|
'hljs-control-fullscreen',
|
||||||
|
'fa fa-arrows-alt',
|
||||||
|
[
|
||||||
|
['tooltip-fullscreen', '全屏'],
|
||||||
|
['tooltip-exit-fullscreen', '退出全屏'],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return control;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param modifier Argon control modifier class appended to the shared `.hljs-control-btn`.
|
||||||
|
* @param iconClass Font Awesome class used by the existing Argon codeblock styles.
|
||||||
|
* @param attributes Tooltip attributes consumed by CSS pseudo-elements.
|
||||||
|
* @returns One icon-only codeblock control button.
|
||||||
|
*/
|
||||||
|
function createArgonControlButton(modifier: string, iconClass: string, attributes: [string, string][]) {
|
||||||
|
const button = document.createElement('div');
|
||||||
|
const icon = document.createElement('i');
|
||||||
|
|
||||||
|
button.className = `hljs-control-btn ${modifier}`;
|
||||||
|
icon.className = iconClass;
|
||||||
|
for (const [name, value] of attributes) {
|
||||||
|
button.setAttribute(name, value);
|
||||||
|
}
|
||||||
|
button.append(icon);
|
||||||
|
|
||||||
|
return button;
|
||||||
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
import { BLOG_ANIMATION_TIMING_MS, runAfterBlogDelay } from '@/factories/blogAnimationFactory';
|
import { BLOG_ANIMATION_TIMING_MS, runAfterBlogDelay } from '@/factories/blogAnimationFactory';
|
||||||
|
import { upgradeArgonCodeblocks } from '@/factories/argonCodeblockFactory';
|
||||||
|
|
||||||
interface ArgonPostContentRuntimeState {
|
interface ArgonPostContentRuntimeState {
|
||||||
closeFancybox?: () => void;
|
closeFancybox?: () => void;
|
||||||
@ -11,6 +12,7 @@ interface ArgonPostContentRuntimeState {
|
|||||||
export function bindArgonPostContentEffects(root: HTMLElement) {
|
export function bindArgonPostContentEffects(root: HTMLElement) {
|
||||||
const runtimeState: ArgonPostContentRuntimeState = {};
|
const runtimeState: ArgonPostContentRuntimeState = {};
|
||||||
|
|
||||||
|
upgradeArgonCodeblocks(root);
|
||||||
normalizeArgonCodeblockLineNumbers(root);
|
normalizeArgonCodeblockLineNumbers(root);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user