Compare commits

..

2 Commits

Author SHA1 Message Date
hypercross fbc67175ae fix: scrolling 2026-03-22 01:18:53 +08:00
hypercross 4839985154 fix: csv 2026-03-22 00:27:59 +08:00
3 changed files with 36 additions and 5 deletions

View File

@ -42,9 +42,12 @@ const App: Component = () => {
<h1 class="text-2xl font-bold text-gray-900">TTRPG Tools</h1>
</div>
</header>
<main class="max-w-4xl mx-auto px-4 py-8 pt-20 md:ml-64 2xl:ml-auto flex justify-center items-center">
<Article class="prose-sm max-w-full flex-1" src={currentPath()} />
</main>
{/* fill th rest of the space*/}
<div class="fixed top-16 left-0 right-0 bottom-0 overflow-auto">
<main class="max-w-4xl mx-auto px-4 py-8 pt-4 md:ml-64 2xl:ml-auto flex justify-center items-center">
<Article class="prose-sm max-w-full flex-1" src={currentPath()} />
</main>
</div>
</div>
);
};

View File

@ -1,9 +1,10 @@
import { Component, createSignal, createEffect, onCleanup, Show, createResource, createMemo } from 'solid-js';
import { Component, createEffect, onCleanup, Show, createResource, createMemo } from 'solid-js';
import { parseMarkdown } from '../markdown';
import { extractSection } from '../data-loader';
import mermaid from 'mermaid';
import {getIndexedData} from "../data-loader/file-index";
import { resolvePath } from './utils/path';
import { useLocation } from '@solidjs/router';
export interface ArticleProps {
src: string;
@ -12,6 +13,7 @@ export interface ArticleProps {
onLoaded?: () => void;
onError?: (error: Error) => void;
class?: string; // 额外的 class 用于样式控制
scrollToHash?: boolean; // 是否自动滚动到 hash
}
async function fetchArticleContent(params: { src: string; section?: string }): Promise<string> {
@ -20,11 +22,34 @@ async function fetchArticleContent(params: { src: string; section?: string }): P
return params.section ? extractSection(text, params.section) : text;
}
/**
* hash
*/
function scrollToHash(hash: string) {
if (!hash) return;
// 移除 # 前缀
const id = hash.startsWith('#') ? hash.slice(1) : hash;
if (!id) return;
// 使用 decodeURIComponent 解码 ID处理中文等特殊字符
const decodedId = decodeURIComponent(id);
// 尝试查找元素
const element = document.getElementById(decodedId);
if (element) {
// 使用 scrollIntoView 滚动到元素
element.scrollIntoView({ behavior: 'instant', block: 'start' });
return true;
}
return false;
}
/**
* Article
* src md markdown
*/
export const Article: Component<ArticleProps> = (props) => {
const location = useLocation();
const [content, { refetch }] = createResource(
() => ({ src: props.src, section: props.section }),
fetchArticleContent
@ -42,6 +67,9 @@ export const Article: Component<ArticleProps> = (props) => {
props.onLoaded?.();
// 内容加载完成后,渲染 mermaid 图表
void mermaid.run();
// 内容渲染后检查 hash 并滚动
scrollToHash(location.hash);
}
});

View File

@ -9,7 +9,7 @@ import type { MarkedExtension, Tokens } from "marked";
function tableToCSV(headers: string[], rows: string[][]): string {
const escapeCell = (cell: string) => {
// 如果单元格包含逗号、换行或引号,需要转义
if (cell.includes(',') || cell.includes('\n') || cell.includes('"')) {
if (cell.includes(',') || cell.includes('\n') || cell.includes('"') || cell.includes("#")) {
return `"${cell.replace(/"/g, '""')}"`;
}
return cell;