优化搜索逻辑,代码和表格样式
This commit is contained in:
parent
e859a1294e
commit
851c3ef386
@ -15,6 +15,7 @@ import compressor from "astro-compressor";
|
|||||||
import vercel from "@astrojs/vercel";
|
import vercel from "@astrojs/vercel";
|
||||||
import { articleIndexerIntegration } from "./src/plugins/build-article-index.js";
|
import { articleIndexerIntegration } from "./src/plugins/build-article-index.js";
|
||||||
import { rehypeCodeBlocks } from "./src/plugins/rehype-code-blocks.js";
|
import { rehypeCodeBlocks } from "./src/plugins/rehype-code-blocks.js";
|
||||||
|
import { rehypeTables } from "./src/plugins/rehype-tables.js";
|
||||||
|
|
||||||
function getArticleDate(articleId) {
|
function getArticleDate(articleId) {
|
||||||
try {
|
try {
|
||||||
@ -129,7 +130,8 @@ export default defineConfig({
|
|||||||
],
|
],
|
||||||
rehypePlugins: [
|
rehypePlugins: [
|
||||||
[rehypeExternalLinks, { target: '_blank', rel: ['nofollow', 'noopener', 'noreferrer'] }],
|
[rehypeExternalLinks, { target: '_blank', rel: ['nofollow', 'noopener', 'noreferrer'] }],
|
||||||
rehypeCodeBlocks
|
rehypeCodeBlocks,
|
||||||
|
rehypeTables
|
||||||
],
|
],
|
||||||
gfm: true,
|
gfm: true,
|
||||||
},
|
},
|
||||||
|
@ -507,9 +507,48 @@ const Search: React.FC<SearchProps> = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 回车键直接执行搜索
|
// 回车键处理逻辑
|
||||||
if (e.key === "Enter") {
|
if (e.key === "Enter") {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
|
// 情况1: 如果有当前选中的内联建议(推荐或纠正)
|
||||||
|
if (inlineSuggestion.visible && inlineSuggestion.text) {
|
||||||
|
const suggestionText = inlineSuggestion.text;
|
||||||
|
|
||||||
|
// 先检查当前搜索结果中是否有完全匹配的结果
|
||||||
|
const exactMatchForSuggestion = allItems.find(item =>
|
||||||
|
item.title.replace(/<\/?mark>/g, '').toLowerCase() === suggestionText.toLowerCase()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (exactMatchForSuggestion) {
|
||||||
|
// 如果有完全匹配的结果,直接导航
|
||||||
|
window.location.href = exactMatchForSuggestion.url;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 没有完全匹配,先补全建议并导航到第一个结果
|
||||||
|
completeInlineSuggestion(true); // 传入true表示需要导航到第一个结果
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 情况2: 如果没有内联建议,但有搜索结果
|
||||||
|
else if (allItems.length > 0 && query.trim()) {
|
||||||
|
// 尝试找到完全匹配当前查询的结果
|
||||||
|
const exactMatch = allItems.find(item =>
|
||||||
|
item.title.replace(/<\/?mark>/g, '').toLowerCase() === query.trim().toLowerCase()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (exactMatch) {
|
||||||
|
// 找到完全匹配,直接导航到该文章
|
||||||
|
window.location.href = exactMatch.url;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有完全匹配,但有搜索结果,进入第一个结果
|
||||||
|
window.location.href = allItems[0].url;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果以上条件都不满足,执行普通搜索
|
||||||
performSearch(query, false);
|
performSearch(query, false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -561,7 +600,7 @@ const Search: React.FC<SearchProps> = ({
|
|||||||
}, [updateCaretPosition]);
|
}, [updateCaretPosition]);
|
||||||
|
|
||||||
// 执行搜索
|
// 执行搜索
|
||||||
const performSearch = async (searchQuery: string, isLoadMore: boolean = false) => {
|
const performSearch = async (searchQuery: string, isLoadMore: boolean = false, shouldNavigateToFirstResult: boolean = false) => {
|
||||||
if (!wasmModule || !isIndexLoaded || !indexData || !searchQuery.trim()) {
|
if (!wasmModule || !isIndexLoaded || !indexData || !searchQuery.trim()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -639,6 +678,11 @@ const Search: React.FC<SearchProps> = ({
|
|||||||
|
|
||||||
// 更新加载状态
|
// 更新加载状态
|
||||||
setLoadingState(prev => ({ ...prev, status: 'success' }));
|
setLoadingState(prev => ({ ...prev, status: 'success' }));
|
||||||
|
|
||||||
|
// 如果需要导航到第一个结果,并且有结果
|
||||||
|
if (shouldNavigateToFirstResult && result.items.length > 0) {
|
||||||
|
window.location.href = result.items[0].url;
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// 检查组件是否仍然挂载
|
// 检查组件是否仍然挂载
|
||||||
if (!isMountedRef.current) return;
|
if (!isMountedRef.current) return;
|
||||||
@ -652,7 +696,7 @@ const Search: React.FC<SearchProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 自动补全内联建议 - 不使用useCallback,避免循环依赖
|
// 自动补全内联建议 - 不使用useCallback,避免循环依赖
|
||||||
const completeInlineSuggestion = () => {
|
const completeInlineSuggestion = (shouldNavigateToFirstResult = false) => {
|
||||||
if (inlineSuggestion.visible && inlineSuggestion.text) {
|
if (inlineSuggestion.visible && inlineSuggestion.text) {
|
||||||
// 保存建议文本
|
// 保存建议文本
|
||||||
const textToComplete = inlineSuggestion.text;
|
const textToComplete = inlineSuggestion.text;
|
||||||
@ -678,7 +722,7 @@ const Search: React.FC<SearchProps> = ({
|
|||||||
setQuery(textToComplete);
|
setQuery(textToComplete);
|
||||||
|
|
||||||
// 立即执行搜索
|
// 立即执行搜索
|
||||||
performSearch(textToComplete, false);
|
performSearch(textToComplete, false, shouldNavigateToFirstResult);
|
||||||
|
|
||||||
// 聚焦输入框并设置光标位置
|
// 聚焦输入框并设置光标位置
|
||||||
if (searchInputRef.current) {
|
if (searchInputRef.current) {
|
||||||
|
@ -37,10 +37,10 @@ export const NAV_STRUCTURE = [
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'others',
|
id: 'other',
|
||||||
text: '其他',
|
text: '其他',
|
||||||
items: [
|
items: [
|
||||||
{ id: 'other', text: '其他', href: '/other' },
|
{ id: 'about', text: '关于', href: '/other' },
|
||||||
{ id: 'projects', text: '项目', href: '/projects' }
|
{ id: 'projects', text: '项目', href: '/projects' }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -200,6 +200,20 @@ export function rehypeCodeBlocks() {
|
|||||||
{ type: 'text', value: ' 复制' }
|
{ type: 'text', value: ' 复制' }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// 计算代码行数,用于生成行号
|
||||||
|
const lineCount = originalCode.split('\n').length;
|
||||||
|
|
||||||
|
// 生成行号元素
|
||||||
|
const lineNumberElements = [];
|
||||||
|
for (let i = 1; i <= lineCount; i++) {
|
||||||
|
lineNumberElements.push({
|
||||||
|
type: 'element',
|
||||||
|
tagName: 'div',
|
||||||
|
properties: { className: ['line-number'] },
|
||||||
|
children: [{ type: 'text', value: String(i) }]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 创建新的代码块容器结构
|
// 创建新的代码块容器结构
|
||||||
const codeBlockContainer = {
|
const codeBlockContainer = {
|
||||||
type: 'element',
|
type: 'element',
|
||||||
@ -235,25 +249,40 @@ export function rehypeCodeBlocks() {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
// 代码内容区域 - 保留原始结构
|
// 代码内容区域 - 修改结构,将代码内容和行号分离
|
||||||
{
|
{
|
||||||
type: 'element',
|
type: 'element',
|
||||||
tagName: 'div',
|
tagName: 'div',
|
||||||
properties: { className: ['code-block-content'] },
|
properties: { className: ['code-block-content'] },
|
||||||
children: [
|
children: [
|
||||||
// 保留原始的 pre 元素及其所有关键属性
|
// 行号容器
|
||||||
{
|
{
|
||||||
type: 'element',
|
type: 'element',
|
||||||
tagName: 'pre',
|
tagName: 'div',
|
||||||
properties: {
|
properties: { className: ['line-numbers-container'] },
|
||||||
style: nodeStyle,
|
children: lineNumberElements
|
||||||
className: nodeClasses,
|
},
|
||||||
// 其他可能的重要属性
|
// 代码内容容器
|
||||||
'data-theme': node.properties['data-theme']
|
{
|
||||||
},
|
type: 'element',
|
||||||
|
tagName: 'div',
|
||||||
|
properties: { className: ['code-content-container'] },
|
||||||
children: [
|
children: [
|
||||||
// 保留原始的code元素,不做修改
|
// 保留原始的 pre 元素及其所有关键属性
|
||||||
codeElement
|
{
|
||||||
|
type: 'element',
|
||||||
|
tagName: 'pre',
|
||||||
|
properties: {
|
||||||
|
style: nodeStyle,
|
||||||
|
className: nodeClasses,
|
||||||
|
// 其他可能的重要属性
|
||||||
|
'data-theme': node.properties['data-theme']
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
// 保留原始的code元素,不做修改
|
||||||
|
codeElement
|
||||||
|
]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
22
src/plugins/rehype-tables.js
Normal file
22
src/plugins/rehype-tables.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { visit } from 'unist-util-visit';
|
||||||
|
|
||||||
|
export function rehypeTables() {
|
||||||
|
return (tree) => {
|
||||||
|
visit(tree, 'element', (node, index, parent) => {
|
||||||
|
if (node.tagName === 'table') {
|
||||||
|
// 创建表格容器
|
||||||
|
const tableContainer = {
|
||||||
|
type: 'element',
|
||||||
|
tagName: 'div',
|
||||||
|
properties: {
|
||||||
|
className: ['table-container']
|
||||||
|
},
|
||||||
|
children: [node]
|
||||||
|
};
|
||||||
|
|
||||||
|
// 替换原始表格节点
|
||||||
|
parent.children[index] = tableContainer;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
@ -57,18 +57,51 @@
|
|||||||
color: #10b981;
|
color: #10b981;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 代码内容容器 - 移除背景 */
|
/* 代码内容容器 - 修改为 flex 布局 */
|
||||||
.code-block-content {
|
.code-block-content {
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow-x: auto;
|
display: flex;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 行号容器 - 固定宽度和位置 */
|
||||||
|
.line-numbers-container {
|
||||||
|
flex: 0 0 2.5rem;
|
||||||
|
background-color: #f1f5f9;
|
||||||
|
border-right: 1px solid #e2e8f0;
|
||||||
|
z-index: 1;
|
||||||
|
position: sticky;
|
||||||
|
left: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.15rem 0;
|
||||||
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 行号元素样式 */
|
||||||
|
.line-number {
|
||||||
|
width: 100%;
|
||||||
|
height: 1.4rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #94a3b8;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 代码内容容器 - 允许水平滚动 */
|
||||||
|
.code-content-container {
|
||||||
|
flex: 1;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
/* 基础代码块样式 - 减小内边距 */
|
/* 基础代码块样式 - 减小内边距 */
|
||||||
pre {
|
pre {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0.15rem 0;
|
padding: 0.15rem 0;
|
||||||
overflow-x: auto;
|
overflow-x: visible; /* 改为 visible,滚动由父容器处理 */
|
||||||
}
|
}
|
||||||
|
|
||||||
pre code {
|
pre code {
|
||||||
@ -85,47 +118,16 @@ pre code {
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 行号背景条 - 减小宽度 */
|
/* 行样式 - 移除左侧 padding */
|
||||||
.line-numbers::before {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
width: 2.5rem;
|
|
||||||
background-color: #f1f5f9;
|
|
||||||
border-right: 1px solid #e2e8f0;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 行样式 - 进一步减小行间距和缩进 */
|
|
||||||
.line-numbers .line {
|
.line-numbers .line {
|
||||||
position: relative;
|
position: relative;
|
||||||
counter-increment: line;
|
counter-increment: line;
|
||||||
padding-left: 3rem;
|
padding-left: 0.5rem; /* 减小左侧内边距 */
|
||||||
padding-right: 0.4rem;
|
padding-right: 0.4rem;
|
||||||
min-height: 1.4rem;
|
min-height: 1.4rem;
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 行号 - 调整位置 */
|
|
||||||
.line-numbers .line::before {
|
|
||||||
content: counter(line);
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
width: 2.5rem;
|
|
||||||
height: 100%;
|
|
||||||
text-align: center;
|
|
||||||
color: #94a3b8;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
user-select: none;
|
|
||||||
z-index: 2;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 暗色模式 */
|
/* 暗色模式 */
|
||||||
[data-theme='dark'] .code-block-container {
|
[data-theme='dark'] .code-block-container {
|
||||||
border-color: #334155;
|
border-color: #334155;
|
||||||
@ -154,17 +156,17 @@ pre code {
|
|||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 暗色模式行号样式 */
|
/* 暗色模式行号容器样式 */
|
||||||
[data-theme='dark'] .line-numbers::before {
|
[data-theme='dark'] .line-numbers-container {
|
||||||
background-color: #1e293b;
|
background-color: #1e293b;
|
||||||
border-right-color: #334155;
|
border-right-color: #334155;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme='dark'] .line-numbers .line::before {
|
/* 暗色模式行号样式 */
|
||||||
|
[data-theme='dark'] .line-number {
|
||||||
color: #64748b;
|
color: #64748b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* 确保所有代码元素没有背景 */
|
/* 确保所有代码元素没有背景 */
|
||||||
code, pre, .code-block-content,
|
code, pre, .code-block-content,
|
||||||
.code-block-content pre.shiki,
|
.code-block-content pre.shiki,
|
||||||
@ -201,11 +203,8 @@ pre.shiki, pre.astro-code,
|
|||||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
color: var(--color-primary-700);
|
color: var(--color-primary-700);
|
||||||
background-color: var(--color-primary-50);
|
|
||||||
padding: 0.2rem 0.4rem;
|
|
||||||
margin: 0 0.2rem;
|
margin: 0 0.2rem;
|
||||||
border-radius: 0.3rem;
|
border-radius: 0.3rem;
|
||||||
border: 1px solid var(--color-primary-100);
|
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
overflow-wrap: break-word;
|
overflow-wrap: break-word;
|
||||||
@ -219,8 +218,6 @@ pre.shiki, pre.astro-code,
|
|||||||
/* 行内代码块黑暗模式样式 */
|
/* 行内代码块黑暗模式样式 */
|
||||||
[data-theme='dark'] :not(pre) > code {
|
[data-theme='dark'] :not(pre) > code {
|
||||||
color: var(--color-primary-300);
|
color: var(--color-primary-300);
|
||||||
background-color: rgba(75, 107, 255, 0.1);
|
|
||||||
border-color: var(--color-primary-700);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 长路径的行内代码块样式特殊处理 */
|
/* 长路径的行内代码块样式特殊处理 */
|
||||||
@ -239,14 +236,10 @@ pre.shiki, pre.astro-code,
|
|||||||
/* 针对文件路径的特殊样式 - 适用于Windows路径 */
|
/* 针对文件路径的特殊样式 - 适用于Windows路径 */
|
||||||
:not(pre) > code.file-path {
|
:not(pre) > code.file-path {
|
||||||
color: var(--color-gray-700);
|
color: var(--color-gray-700);
|
||||||
background-color: var(--color-gray-100);
|
|
||||||
border-color: var(--color-gray-200);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 针对文件路径的特殊样式 - 黑暗模式 */
|
/* 针对文件路径的特殊样式 - 黑暗模式 */
|
||||||
[data-theme='dark'] :not(pre) > code.file-path {
|
[data-theme='dark'] :not(pre) > code.file-path {
|
||||||
color: var(--color-gray-300);
|
color: var(--color-gray-300);
|
||||||
background-color: var(--color-gray-800);
|
|
||||||
border-color: var(--color-gray-700);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
/* 表格样式 - 与全局主题配色协调 */
|
/* 表格样式 - 与全局主题配色协调 */
|
||||||
table {
|
.table-container {
|
||||||
|
container-type: inline-size;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: 1.5rem;
|
|
||||||
border-collapse: separate;
|
|
||||||
border-spacing: 0;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
display: block;
|
border-radius: 0.5rem;
|
||||||
max-width: 100%;
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
display: table;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
thead {
|
thead {
|
||||||
|
Loading…
Reference in New Issue
Block a user