pdf-smith 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/index.cjs +146 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +33 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +115 -0
- package/dist/index.js.map +1 -0
- package/dist/preview-plugin-QWJEXDW2.js +84 -0
- package/dist/preview-plugin-QWJEXDW2.js.map +1 -0
- package/dist/server.cjs +246 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +33 -0
- package/dist/server.d.ts +33 -0
- package/dist/server.js +104 -0
- package/dist/server.js.map +1 -0
- package/package.json +91 -0
- package/src/constants.ts +11 -0
- package/src/document.tsx +15 -0
- package/src/index.ts +11 -0
- package/src/page.tsx +35 -0
- package/src/preview/app.tsx +52 -0
- package/src/preview/entry.tsx +7 -0
- package/src/preview/navigation.tsx +98 -0
- package/src/preview/preview-plugin.ts +92 -0
- package/src/preview/types.ts +16 -0
- package/src/preview/vite-env.d.ts +3 -0
- package/src/styles.ts +69 -0
- package/src/types.ts +23 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
interface NavigationProps {
|
|
2
|
+
pages: Record<string, React.ComponentType>;
|
|
3
|
+
activePage: string | null;
|
|
4
|
+
onSelectPage: (page: string | null) => void;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const navStyle: React.CSSProperties = {
|
|
8
|
+
position: 'fixed',
|
|
9
|
+
top: 0,
|
|
10
|
+
left: 0,
|
|
11
|
+
width: '240px',
|
|
12
|
+
height: '100vh',
|
|
13
|
+
background: '#1a1a2e',
|
|
14
|
+
color: '#e0e0e0',
|
|
15
|
+
padding: '16px',
|
|
16
|
+
overflowY: 'auto',
|
|
17
|
+
fontFamily: 'system-ui, -apple-system, sans-serif',
|
|
18
|
+
fontSize: '14px',
|
|
19
|
+
zIndex: 1000,
|
|
20
|
+
boxSizing: 'border-box',
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const titleStyle: React.CSSProperties = {
|
|
24
|
+
fontSize: '16px',
|
|
25
|
+
fontWeight: 700,
|
|
26
|
+
marginBottom: '16px',
|
|
27
|
+
color: '#ffffff',
|
|
28
|
+
letterSpacing: '0.5px',
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const buttonBaseStyle: React.CSSProperties = {
|
|
32
|
+
display: 'block',
|
|
33
|
+
width: '100%',
|
|
34
|
+
padding: '8px 12px',
|
|
35
|
+
border: 'none',
|
|
36
|
+
borderRadius: '6px',
|
|
37
|
+
cursor: 'pointer',
|
|
38
|
+
textAlign: 'left',
|
|
39
|
+
fontSize: '13px',
|
|
40
|
+
marginBottom: '4px',
|
|
41
|
+
transition: 'background 0.15s',
|
|
42
|
+
fontFamily: 'inherit',
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
function getButtonStyle(isActive: boolean): React.CSSProperties {
|
|
46
|
+
return {
|
|
47
|
+
...buttonBaseStyle,
|
|
48
|
+
background: isActive ? '#16213e' : 'transparent',
|
|
49
|
+
color: isActive ? '#ffffff' : '#b0b0b0',
|
|
50
|
+
fontWeight: isActive ? 600 : 400,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const separatorStyle: React.CSSProperties = {
|
|
55
|
+
border: 'none',
|
|
56
|
+
borderTop: '1px solid #2a2a4a',
|
|
57
|
+
margin: '12px 0',
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export function Navigation({ pages, activePage, onSelectPage }: NavigationProps) {
|
|
61
|
+
const pageNames = Object.keys(pages);
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<nav data-pdf-smith-nav="" style={navStyle}>
|
|
65
|
+
<div style={titleStyle}>pdf-smith Preview</div>
|
|
66
|
+
<button
|
|
67
|
+
type="button"
|
|
68
|
+
style={getButtonStyle(activePage === null)}
|
|
69
|
+
onClick={() => onSelectPage(null)}
|
|
70
|
+
onMouseEnter={(e) => {
|
|
71
|
+
if (activePage !== null) e.currentTarget.style.background = '#16213e40';
|
|
72
|
+
}}
|
|
73
|
+
onMouseLeave={(e) => {
|
|
74
|
+
if (activePage !== null) e.currentTarget.style.background = 'transparent';
|
|
75
|
+
}}
|
|
76
|
+
>
|
|
77
|
+
All Pages
|
|
78
|
+
</button>
|
|
79
|
+
<hr style={separatorStyle} />
|
|
80
|
+
{pageNames.map((name) => (
|
|
81
|
+
<button
|
|
82
|
+
type="button"
|
|
83
|
+
key={name}
|
|
84
|
+
style={getButtonStyle(activePage === name)}
|
|
85
|
+
onClick={() => onSelectPage(name)}
|
|
86
|
+
onMouseEnter={(e) => {
|
|
87
|
+
if (activePage !== name) e.currentTarget.style.background = '#16213e40';
|
|
88
|
+
}}
|
|
89
|
+
onMouseLeave={(e) => {
|
|
90
|
+
if (activePage !== name) e.currentTarget.style.background = 'transparent';
|
|
91
|
+
}}
|
|
92
|
+
>
|
|
93
|
+
{name}
|
|
94
|
+
</button>
|
|
95
|
+
))}
|
|
96
|
+
</nav>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { Plugin } from 'vite';
|
|
2
|
+
|
|
3
|
+
const VIRTUAL_PAGES_ID = 'virtual:pdf-smith-pages';
|
|
4
|
+
const RESOLVED_VIRTUAL_PAGES_ID = `\0${VIRTUAL_PAGES_ID}`;
|
|
5
|
+
|
|
6
|
+
interface PreviewPluginOptions {
|
|
7
|
+
pkgSrcDir: string;
|
|
8
|
+
pagesGlob: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function pdfSmithPreviewPlugin({ pkgSrcDir, pagesGlob }: PreviewPluginOptions): Plugin {
|
|
12
|
+
return {
|
|
13
|
+
name: 'pdf-smith-preview',
|
|
14
|
+
|
|
15
|
+
configureServer(server) {
|
|
16
|
+
return () => {
|
|
17
|
+
server.middlewares.use((req, res, next) => {
|
|
18
|
+
if (req.url !== '/' && req.url !== '/index.html') {
|
|
19
|
+
next();
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const html = `<!DOCTYPE html>
|
|
24
|
+
<html lang="en">
|
|
25
|
+
<head>
|
|
26
|
+
<script type="module">
|
|
27
|
+
import RefreshRuntime from "/@react-refresh"
|
|
28
|
+
RefreshRuntime.injectIntoGlobalHook(window)
|
|
29
|
+
window.$RefreshReg$ = () => {}
|
|
30
|
+
window.$RefreshSig$ = () => (type) => type
|
|
31
|
+
window.__vite_plugin_react_preamble_installed__ = true
|
|
32
|
+
</script>
|
|
33
|
+
<script type="module" src="/@vite/client"></script>
|
|
34
|
+
<meta charset="UTF-8" />
|
|
35
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
36
|
+
<title>pdf-smith Preview</title>
|
|
37
|
+
<style>
|
|
38
|
+
@media print {
|
|
39
|
+
[data-pdf-smith-nav] { display: none !important; }
|
|
40
|
+
[data-pdf-smith-container] {
|
|
41
|
+
margin-left: 0 !important;
|
|
42
|
+
padding: 0 !important;
|
|
43
|
+
background: none !important;
|
|
44
|
+
}
|
|
45
|
+
[data-pdf-smith-container] > [data-pdf-smith-document] > div {
|
|
46
|
+
margin-bottom: 0 !important;
|
|
47
|
+
}
|
|
48
|
+
[data-pdf-smith-container] > [data-pdf-smith-document] > div > div:first-child {
|
|
49
|
+
display: none !important;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
</style>
|
|
53
|
+
</head>
|
|
54
|
+
<body>
|
|
55
|
+
<div id="root"></div>
|
|
56
|
+
<script type="module" src="/@fs/${pkgSrcDir}/preview/entry.tsx"></script>
|
|
57
|
+
</body>
|
|
58
|
+
</html>`;
|
|
59
|
+
|
|
60
|
+
res.setHeader('Content-Type', 'text/html');
|
|
61
|
+
res.statusCode = 200;
|
|
62
|
+
res.end(html);
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
resolveId(id) {
|
|
68
|
+
if (id === VIRTUAL_PAGES_ID) {
|
|
69
|
+
return RESOLVED_VIRTUAL_PAGES_ID;
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
load(id) {
|
|
74
|
+
if (id === RESOLVED_VIRTUAL_PAGES_ID) {
|
|
75
|
+
return `
|
|
76
|
+
const modules = import.meta.glob('${pagesGlob}', { eager: true });
|
|
77
|
+
|
|
78
|
+
export function getPages() {
|
|
79
|
+
const pages = {};
|
|
80
|
+
for (const [path, mod] of Object.entries(modules)) {
|
|
81
|
+
const name = path
|
|
82
|
+
.replace(/^\\/pages\\//, '')
|
|
83
|
+
.replace(/\\.[^.]+$/, '');
|
|
84
|
+
pages[name] = mod.default;
|
|
85
|
+
}
|
|
86
|
+
return pages;
|
|
87
|
+
}
|
|
88
|
+
`;
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface PreviewOptions {
|
|
2
|
+
/** User's project root (absolute path) */
|
|
3
|
+
root: string;
|
|
4
|
+
/** Dev server port (default: 3000) */
|
|
5
|
+
port?: number;
|
|
6
|
+
// Glob pattern for page discovery (default: '/pages/**/*.tsx')
|
|
7
|
+
pages?: string;
|
|
8
|
+
/** Auto-open browser (default: false) */
|
|
9
|
+
open?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface PreviewServer {
|
|
13
|
+
close(): Promise<void>;
|
|
14
|
+
port: number;
|
|
15
|
+
url: string;
|
|
16
|
+
}
|
package/src/styles.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { PageDimensions, PagePadding } from './types';
|
|
2
|
+
|
|
3
|
+
interface ResolvedPadding {
|
|
4
|
+
top: number;
|
|
5
|
+
right: number;
|
|
6
|
+
bottom: number;
|
|
7
|
+
left: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function resolvePadding(padding: PagePadding): ResolvedPadding {
|
|
11
|
+
if (typeof padding === 'number') {
|
|
12
|
+
return { top: padding, right: padding, bottom: padding, left: padding };
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
top: padding.top ?? 0,
|
|
16
|
+
right: padding.right ?? 0,
|
|
17
|
+
bottom: padding.bottom ?? 0,
|
|
18
|
+
left: padding.left ?? 0,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function getPageStyle(
|
|
23
|
+
dimensions: PageDimensions,
|
|
24
|
+
padding: ResolvedPadding,
|
|
25
|
+
): React.CSSProperties {
|
|
26
|
+
return {
|
|
27
|
+
width: `${dimensions.width}mm`,
|
|
28
|
+
height: `${dimensions.height}mm`,
|
|
29
|
+
paddingTop: `${padding.top}mm`,
|
|
30
|
+
paddingRight: `${padding.right}mm`,
|
|
31
|
+
paddingBottom: `${padding.bottom}mm`,
|
|
32
|
+
paddingLeft: `${padding.left}mm`,
|
|
33
|
+
display: 'block',
|
|
34
|
+
overflow: 'hidden',
|
|
35
|
+
background: 'white',
|
|
36
|
+
boxSizing: 'border-box',
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function getDocumentStyleSheet(dimensions: PageDimensions): string {
|
|
41
|
+
return `
|
|
42
|
+
@page {
|
|
43
|
+
size: ${dimensions.width}mm ${dimensions.height}mm;
|
|
44
|
+
margin: 0;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
* {
|
|
48
|
+
print-color-adjust: exact;
|
|
49
|
+
-webkit-print-color-adjust: exact;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@media screen {
|
|
53
|
+
[data-pdf-smith-page] {
|
|
54
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
|
|
55
|
+
margin-left: auto;
|
|
56
|
+
margin-right: auto;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@media print {
|
|
61
|
+
[data-pdf-smith-page] {
|
|
62
|
+
box-shadow: none;
|
|
63
|
+
break-after: page;
|
|
64
|
+
overflow: visible;
|
|
65
|
+
height: auto;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
`.trim();
|
|
69
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type PageSizePreset = 'A4' | 'Letter' | 'A3';
|
|
2
|
+
|
|
3
|
+
export interface PageDimensions {
|
|
4
|
+
width: number;
|
|
5
|
+
height: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type PageSize = PageSizePreset | PageDimensions;
|
|
9
|
+
|
|
10
|
+
export type PagePadding = number | { top?: number; right?: number; bottom?: number; left?: number };
|
|
11
|
+
|
|
12
|
+
export interface PageProps {
|
|
13
|
+
children?: React.ReactNode;
|
|
14
|
+
className?: string;
|
|
15
|
+
size?: PageSize;
|
|
16
|
+
padding?: PagePadding;
|
|
17
|
+
id?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface DocumentProps {
|
|
21
|
+
children?: React.ReactNode;
|
|
22
|
+
className?: string;
|
|
23
|
+
}
|