chore: 初始化项目骨架
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta
|
||||
name="description"
|
||||
content="可配置、可主题化的 Markdown PDF 导出工具"
|
||||
/>
|
||||
<title>Markdown PDF 导出器</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "@md-to-pdf/web",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc -b && vite build",
|
||||
"typecheck": "tsc -b --pretty false"
|
||||
},
|
||||
"dependencies": {
|
||||
"@md-to-pdf/core": "0.1.0",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^19.2.7",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@vitejs/plugin-react": "^5.1.1",
|
||||
"vite": "^7.2.4"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import { defaultExportConfig, paperFormatLabels } from "@md-to-pdf/core";
|
||||
|
||||
const capabilities = [
|
||||
"Markdown 本地预览",
|
||||
"Chromium PDF 下载",
|
||||
"纸张与页边距",
|
||||
"页眉、页脚与页码",
|
||||
"可扩展 CSS 主题"
|
||||
];
|
||||
|
||||
export function App() {
|
||||
return (
|
||||
<main className="shell">
|
||||
<section className="intro" aria-labelledby="page-title">
|
||||
<p className="eyebrow">内网文档工具</p>
|
||||
<h1 id="page-title">Markdown PDF 导出器</h1>
|
||||
<p className="summary">
|
||||
使用同一份 HTML 与主题 CSS 完成网页预览和 Chromium PDF 渲染。
|
||||
</p>
|
||||
<div className="status">
|
||||
<span className="status-dot" aria-hidden="true" />
|
||||
项目骨架已就绪
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="panel" aria-labelledby="config-title">
|
||||
<div>
|
||||
<p className="panel-label">默认导出预设</p>
|
||||
<h2 id="config-title">{defaultExportConfig.name}</h2>
|
||||
</div>
|
||||
<dl className="facts">
|
||||
<div>
|
||||
<dt>纸张</dt>
|
||||
<dd>{paperFormatLabels[defaultExportConfig.paper.format]}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>方向</dt>
|
||||
<dd>
|
||||
{defaultExportConfig.paper.orientation === "portrait"
|
||||
? "纵向"
|
||||
: "横向"}
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>主题</dt>
|
||||
<dd>{defaultExportConfig.themeId}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
<section className="capabilities" aria-label="规划能力">
|
||||
{capabilities.map((item, index) => (
|
||||
<article key={item}>
|
||||
<span>{String(index + 1).padStart(2, "0")}</span>
|
||||
<p>{item}</p>
|
||||
</article>
|
||||
))}
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { StrictMode } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { App } from "./App";
|
||||
import "./styles.css";
|
||||
|
||||
const rootElement = document.getElementById("root");
|
||||
|
||||
if (!rootElement) {
|
||||
throw new Error("未找到应用挂载节点");
|
||||
}
|
||||
|
||||
createRoot(rootElement).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>
|
||||
);
|
||||
@@ -0,0 +1,194 @@
|
||||
:root {
|
||||
color: #1d2522;
|
||||
background:
|
||||
radial-gradient(circle at 12% 10%, rgb(197 225 210 / 45%), transparent 30rem),
|
||||
#f2f0e9;
|
||||
font-family:
|
||||
Inter, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
min-width: 320px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
.shell {
|
||||
width: min(1120px, calc(100% - 40px));
|
||||
margin: 0 auto;
|
||||
padding: 88px 0;
|
||||
}
|
||||
|
||||
.intro {
|
||||
max-width: 760px;
|
||||
}
|
||||
|
||||
.eyebrow,
|
||||
.panel-label {
|
||||
margin: 0 0 14px;
|
||||
color: #34705a;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.16em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
p {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
max-width: 700px;
|
||||
margin-bottom: 22px;
|
||||
font-family: Georgia, "Songti SC", serif;
|
||||
font-size: clamp(3rem, 8vw, 6.4rem);
|
||||
font-weight: 500;
|
||||
letter-spacing: -0.055em;
|
||||
line-height: 0.94;
|
||||
}
|
||||
|
||||
.summary {
|
||||
max-width: 610px;
|
||||
margin-bottom: 28px;
|
||||
color: #56615d;
|
||||
font-size: 1.15rem;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.status {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
color: #315a4b;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 650;
|
||||
}
|
||||
|
||||
.status-dot {
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
border-radius: 50%;
|
||||
background: #3da073;
|
||||
box-shadow: 0 0 0 5px rgb(61 160 115 / 13%);
|
||||
}
|
||||
|
||||
.panel {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(420px, 1.25fr);
|
||||
gap: 48px;
|
||||
align-items: end;
|
||||
margin-top: 80px;
|
||||
padding: 34px;
|
||||
border: 1px solid rgb(41 64 55 / 14%);
|
||||
border-radius: 20px;
|
||||
background: rgb(255 255 255 / 58%);
|
||||
box-shadow: 0 24px 60px rgb(49 67 59 / 8%);
|
||||
backdrop-filter: blur(18px);
|
||||
}
|
||||
|
||||
.panel h2 {
|
||||
margin-bottom: 0;
|
||||
font-family: Georgia, "Songti SC", serif;
|
||||
font-size: clamp(1.8rem, 4vw, 3rem);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.facts {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 12px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.facts div {
|
||||
padding: 18px;
|
||||
border-radius: 13px;
|
||||
background: #e6ebe6;
|
||||
}
|
||||
|
||||
.facts dt {
|
||||
margin-bottom: 7px;
|
||||
color: #6b756f;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.facts dd {
|
||||
margin: 0;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.capabilities {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
margin-top: 20px;
|
||||
border-top: 1px solid rgb(41 64 55 / 16%);
|
||||
}
|
||||
|
||||
.capabilities article {
|
||||
min-height: 132px;
|
||||
padding: 20px 16px;
|
||||
border-right: 1px solid rgb(41 64 55 / 16%);
|
||||
}
|
||||
|
||||
.capabilities article:last-child {
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
.capabilities span {
|
||||
color: #7e8983;
|
||||
font-family: Georgia, serif;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.capabilities p {
|
||||
margin: 42px 0 0;
|
||||
font-size: 0.92rem;
|
||||
font-weight: 650;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
@media (max-width: 760px) {
|
||||
.shell {
|
||||
width: min(100% - 28px, 1120px);
|
||||
padding: 48px 0;
|
||||
}
|
||||
|
||||
.panel {
|
||||
grid-template-columns: 1fr;
|
||||
margin-top: 56px;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.facts {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.capabilities {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.capabilities article {
|
||||
min-height: auto;
|
||||
border-right: 0;
|
||||
border-bottom: 1px solid rgb(41 64 55 / 16%);
|
||||
}
|
||||
|
||||
.capabilities p {
|
||||
margin-top: 16px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": [
|
||||
"ES2022",
|
||||
"DOM",
|
||||
"DOM.Iterable"
|
||||
],
|
||||
"allowJs": false,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
"jsx": "react-jsx",
|
||||
"noEmit": true
|
||||
},
|
||||
"include": [
|
||||
"src",
|
||||
"vite.config.ts"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
server: {
|
||||
port: 5173,
|
||||
proxy: {
|
||||
"/api": "http://localhost:3001"
|
||||
}
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user