lupine.press 1.0.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.
@@ -0,0 +1,163 @@
1
+ import {
2
+ VNode,
3
+ CssProps,
4
+ MediaQueryRange,
5
+ bindLinks,
6
+ RefProps,
7
+ MediaQueryMaxWidth,
8
+ isFrontEnd,
9
+ } from 'lupine.components';
10
+ import { pressLoad } from '../services/press-load';
11
+ import { getSidebarScroll, setSidebarScroll } from '../services';
12
+
13
+ export interface PressFrameProps {
14
+ header: VNode<any>;
15
+ sidemenu: VNode<any>;
16
+ sidemenuWidth?: string;
17
+ hideSidemenu?: boolean;
18
+ content: VNode<any>;
19
+ css?: CssProps;
20
+ }
21
+
22
+ export const PressFrame = (props: PressFrameProps) => {
23
+ if (isFrontEnd()) {
24
+ // Global navigation helper requested by user
25
+ (window as any).lpPressLoad = pressLoad;
26
+ }
27
+
28
+ const cssMarkdown: CssProps = {
29
+ // used in built markdown htmls, put under some element to override global.css
30
+ 'h1, h2, h3': {
31
+ borderBottom: '1px solid var(--press-border-color)',
32
+ paddingBottom: '0.3em',
33
+ marginTop: '1.5em',
34
+ position: 'relative',
35
+ scrollMarginTop: '80px',
36
+ '&:first-child': { marginTop: 0 },
37
+ '&:hover .header-anchor': { opacity: 1 },
38
+ },
39
+ '.header-anchor': {
40
+ position: 'absolute',
41
+ left: '-1.5rem',
42
+ width: '1rem',
43
+ opacity: 0,
44
+ textDecoration: 'none',
45
+ color: 'var(--press-brand-color)',
46
+ fontSize: '0.8em',
47
+ transition: 'opacity 0.2s',
48
+ },
49
+ ol: {
50
+ listStyleType: 'decimal',
51
+ },
52
+ 'li, p': {
53
+ margin: '0.5em 0',
54
+ },
55
+ pre: {
56
+ backgroundColor: 'var(--secondary-bg-color)',
57
+ padding: '1rem',
58
+ borderRadius: '8px',
59
+ overflowX: 'auto',
60
+ },
61
+ code: {
62
+ fontFamily: 'var(--font-family-mono, monospace)',
63
+ },
64
+ };
65
+
66
+ const cssContainer: CssProps = {
67
+ display: 'flex',
68
+ flexDirection: 'column',
69
+ width: '100%',
70
+ height: '100%',
71
+ minHeight: '100%',
72
+ position: 'relative',
73
+ ...cssMarkdown,
74
+ ...props.css,
75
+ '.press-frame-box': {
76
+ display: 'flex',
77
+ flex: '1',
78
+ flexDirection: 'column',
79
+ height: '100%',
80
+ width: '100%',
81
+ maxWidth: MediaQueryMaxWidth.DesktopMax,
82
+ margin: 'auto',
83
+ // trick: to put two padding-top properties
84
+ 'padding-top ': 'constant(safe-area-inset-top)',
85
+ 'padding-top': 'env(safe-area-inset-top)',
86
+ },
87
+ '.press-frame-header': {
88
+ display: 'flex',
89
+ flexDirection: 'column',
90
+ alignItems: 'center',
91
+ padding: '4px 16px 4px 0px',
92
+ width: '100%',
93
+ },
94
+ '.press-frame-main': {
95
+ display: 'flex',
96
+ flex: '1',
97
+ flexDirection: 'row',
98
+ overflowY: 'auto',
99
+ scrollbarWidth: 'none',
100
+ borderTop: '1px solid var(--press-border-color)',
101
+ minHeight: '0',
102
+ scrollBehavior: 'smooth',
103
+ '&::-webkit-scrollbar': {
104
+ display: 'none',
105
+ },
106
+ },
107
+ '.press-frame-main .padding-block': {
108
+ padding: '0 16px',
109
+ },
110
+ '.press-frame-content': {
111
+ display: 'flex',
112
+ flex: '1',
113
+ flexDirection: 'column',
114
+ overflowY: 'auto',
115
+ scrollbarWidth: 'none',
116
+ },
117
+ '.press-frame-sidemenu': {
118
+ width: props.sidemenuWidth || '260px',
119
+ display: 'flex',
120
+ borderRight: '1px solid var(--press-border-color)',
121
+ overflowX: 'hidden',
122
+ overflowY: 'auto',
123
+ color: 'var(--sidebar-color)',
124
+ scrollbarWidth: 'none',
125
+ '&::-webkit-scrollbar': {
126
+ display: 'none',
127
+ },
128
+ // '&::-webkit-scrollbar': { width: '4px' },
129
+ // '&::-webkit-scrollbar-thumb': { backgroundColor: 'var(--press-border-color)' },
130
+ // backgroundColor: 'var(--sidebar-bg-color)',
131
+ },
132
+ [MediaQueryRange.TabletBelow]: {
133
+ '.press-frame-sidemenu': {
134
+ display: 'none',
135
+ },
136
+ },
137
+ };
138
+
139
+ const ref: RefProps = {
140
+ onLoad: async (el: Element) => {
141
+ bindLinks(el);
142
+ const sidemenu = el.querySelector('.press-frame-sidemenu');
143
+ if (sidemenu) {
144
+ sidemenu.scrollTop = getSidebarScroll();
145
+ sidemenu.addEventListener('scroll', () => {
146
+ setSidebarScroll(sidemenu.scrollTop);
147
+ });
148
+ }
149
+ },
150
+ };
151
+
152
+ return (
153
+ <div ref={ref} css={cssContainer} class='press-frame'>
154
+ <div class='press-frame-box'>
155
+ <div class='press-frame-header'>{props.header}</div>
156
+ <div class='press-frame-main'>
157
+ {!props.hideSidemenu && <div class='press-frame-sidemenu'>{props.sidemenu}</div>}
158
+ <div class='press-frame-content'>{props.content}</div>
159
+ </div>
160
+ </div>
161
+ </div>
162
+ );
163
+ };
package/src/index.ts ADDED
@@ -0,0 +1,7 @@
1
+ import './styles/global.css';
2
+
3
+ export * from './components';
4
+ export * from './frames';
5
+ export * from './page';
6
+ export * from './services';
7
+ export * from './styles/press-themes';
@@ -0,0 +1 @@
1
+ export * from './press-page';
@@ -0,0 +1,73 @@
1
+ import { isFrontEnd, PageProps } from 'lupine.components';
2
+ import { PressLayout } from '../components';
3
+ import { getPressData } from '../services/cache';
4
+
5
+ export const PressPage = (props: PageProps) => {
6
+ if (!isFrontEnd()) {
7
+ return <div></div>;
8
+ }
9
+
10
+ const markdownConfig = getPressData();
11
+ // Derive language from URL path first
12
+ const pathParts = window.location.pathname.split('/');
13
+ const pathLang = pathParts[1]; // /en/... or /zh/...
14
+
15
+ // Get supported langs from global config
16
+ const globalConfig = markdownConfig['/']?.data || {};
17
+ const supportedLangs = Array.isArray(globalConfig.lang) ? globalConfig.lang.map((l: any) => l.id) : ['en', 'zh'];
18
+
19
+ const langName = supportedLangs.includes(pathLang) ? pathLang : supportedLangs[0] || 'en';
20
+
21
+ // Sync global state with URL language
22
+ import('lupine.components').then((m) => {
23
+ if (m.getCurrentLang().langName !== langName) {
24
+ m.updateLang(langName);
25
+ }
26
+ });
27
+
28
+ // Get current path from router props or window.location
29
+ let currentPath = window.location.pathname;
30
+ if (currentPath === '' || currentPath === '/') {
31
+ currentPath = `/${langName}/index`;
32
+ } else {
33
+ if (currentPath.endsWith('/') && currentPath.length > 1)
34
+ currentPath = currentPath.substring(0, currentPath.length - 1);
35
+ // If it's just lang root like /en, append /index
36
+ if (currentPath === `/${langName}`) currentPath = `/${langName}/index`;
37
+ }
38
+
39
+ const config = markdownConfig[currentPath];
40
+ const content = config ? config.html : `<h1>404 - Page Not Found</h1><p>Path: ${currentPath}</p>`;
41
+
42
+ // Get localized configuration from lang index
43
+ const rootIndex = markdownConfig[`/${langName}/index`]?.data || {};
44
+ const rootNav = rootIndex.nav || [];
45
+ const rootSidebar = rootIndex.sidebar || [];
46
+ const siteTitle = rootIndex.title || 'LupineJS';
47
+
48
+ // Collect langs from all supported language root index files
49
+ const langs = supportedLangs.map((l: string) => {
50
+ const data = markdownConfig[`/${l}/index`]?.data || {};
51
+ const langData = Array.isArray(data.lang) ? data.lang[0] : data.lang || {};
52
+ return {
53
+ text: langData.text || langData.title || langData.label || l.toUpperCase(),
54
+ id: l,
55
+ };
56
+ });
57
+
58
+ return (
59
+ <PressLayout
60
+ title={siteTitle}
61
+ nav={rootNav}
62
+ sidebar={rootSidebar}
63
+ lang={langName}
64
+ langs={langs}
65
+ data={config?.data}
66
+ headings={config?.headings}
67
+ sidemenuWidth={rootIndex['sidemenu-width']}
68
+ github={{ url: rootIndex['github-link'], title: rootIndex['github-title'] }}
69
+ >
70
+ {content}
71
+ </PressLayout>
72
+ );
73
+ };
@@ -0,0 +1,18 @@
1
+ import { HtmlVar, VNode } from 'lupine.components';
2
+
3
+ const _cache: any = {};
4
+ export const bindPressData = (pressData: any) => {
5
+ _cache.data = pressData;
6
+ };
7
+
8
+ export const getPressData = () => {
9
+ return _cache.data;
10
+ };
11
+
12
+ export const getSidebarScroll = () => {
13
+ return _cache.sidebarScroll || 0;
14
+ };
15
+
16
+ export const setSidebarScroll = (val: number) => {
17
+ _cache.sidebarScroll = val;
18
+ };
@@ -0,0 +1,3 @@
1
+ export * from './cache';
2
+ export * from './markdown';
3
+ export * from './press-load';
@@ -0,0 +1,18 @@
1
+ import { marked } from 'marked';
2
+ import matter from 'gray-matter';
3
+
4
+ export interface MarkdownResult {
5
+ content: string;
6
+ data: { [key: string]: any };
7
+ html: string;
8
+ }
9
+
10
+ export const parseMarkdown = (mdContent: string): MarkdownResult => {
11
+ const { data, content } = matter(mdContent);
12
+ const html = marked.parse(content) as string;
13
+ return {
14
+ content,
15
+ data,
16
+ html,
17
+ };
18
+ };
@@ -0,0 +1,17 @@
1
+ import { getCurrentLang, initializePage } from 'lupine.components';
2
+ import { setSidebarScroll } from './cache';
3
+
4
+ export const pressLoad = (url: string) => {
5
+ const sidemenu = document.querySelector('.press-frame-sidemenu');
6
+ if (sidemenu) {
7
+ setSidebarScroll(sidemenu.scrollTop);
8
+ }
9
+
10
+ let target = url;
11
+ if (target.startsWith('/') && !target.startsWith('/en/') && !target.startsWith('/zh/')) {
12
+ const { langName } = getCurrentLang();
13
+ target = `/${langName}${target}`;
14
+ }
15
+ if (target.endsWith('/') && target.length > 1) target = target.substring(0, target.length - 1);
16
+ initializePage(target || '/');
17
+ };
@@ -0,0 +1,3 @@
1
+ <svg viewBox='0 0 24 24' width='20' height='20' fill='currentColor'>
2
+ <path d='M12 2C6.477 2 2 6.477 2 12c0 4.42 2.865 8.164 6.839 9.49.5.092.682-.217.682-.482 0-.237-.008-.866-.013-1.7-2.782.604-3.369-1.34-3.369-1.34-.454-1.156-1.11-1.463-1.11-1.463-.908-.62.069-.608.069-.608 1.003.07 1.531 1.03 1.531 1.03.892 1.529 2.341 1.087 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.11-4.555-4.943 0-1.091.39-1.984 1.029-2.683-.103-.253-.446-1.27.098-2.647 0 0 .84-.269 2.75 1.025A9.578 9.578 0 0112 6.836c.85.004 1.705.115 2.504.337 1.909-1.294 2.747-1.025 2.747-1.025.546 1.377.203 2.394.1 2.647.64.699 1.028 1.592 1.028 2.683 0 3.842-2.339 4.687-4.566 4.935.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12c0-5.523-4.477-10-10-10z' />
3
+ </svg>