@fecommunity/reactpress-theme-starter 1.0.0-beta.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.
Files changed (163) hide show
  1. package/.env.example +22 -0
  2. package/LICENSE +21 -0
  3. package/README.md +286 -0
  4. package/app/api/[[...path]]/route.ts +69 -0
  5. package/app/archives/page.tsx +28 -0
  6. package/app/article/[id]/page.tsx +92 -0
  7. package/app/category/[category]/page.tsx +60 -0
  8. package/app/globals.css +4969 -0
  9. package/app/knowledge/[pId]/[id]/page.tsx +54 -0
  10. package/app/knowledge/[pId]/page.tsx +47 -0
  11. package/app/knowledge/page.tsx +29 -0
  12. package/app/layout.tsx +76 -0
  13. package/app/llms.txt/route.ts +26 -0
  14. package/app/login/page.tsx +17 -0
  15. package/app/nav/[id]/page.tsx +44 -0
  16. package/app/nav/page.tsx +5 -0
  17. package/app/not-found.tsx +21 -0
  18. package/app/page/[id]/page.tsx +57 -0
  19. package/app/page.tsx +61 -0
  20. package/app/register/page.tsx +17 -0
  21. package/app/robots.ts +14 -0
  22. package/app/rss/route.ts +82 -0
  23. package/app/search/page.tsx +46 -0
  24. package/app/sitemap.ts +38 -0
  25. package/app/suggestions/page.tsx +47 -0
  26. package/app/tag/[tag]/page.tsx +58 -0
  27. package/app/tags/page.tsx +18 -0
  28. package/app/template.tsx +6 -0
  29. package/components/article/ArchiveBanner.tsx +40 -0
  30. package/components/article/ArticleActionsBar.tsx +53 -0
  31. package/components/article/ArticleCarousel.tsx +237 -0
  32. package/components/article/ArticleCopyrightBar.tsx +128 -0
  33. package/components/article/ArticleEndBar.tsx +19 -0
  34. package/components/article/ArticleFeedSection.tsx +59 -0
  35. package/components/article/ArticleList.tsx +170 -0
  36. package/components/article/ArticleReadingChrome.tsx +104 -0
  37. package/components/article/ArticleReadingProgress.tsx +75 -0
  38. package/components/article/ArticleRecommend.tsx +362 -0
  39. package/components/article/ArticleTocPanel.tsx +19 -0
  40. package/components/article/ArticleViewClient.tsx +221 -0
  41. package/components/article/CategoryMenu.tsx +67 -0
  42. package/components/article/CmsPageClient.tsx +92 -0
  43. package/components/article/LoadMore.tsx +59 -0
  44. package/components/article/ReadingContent.tsx +51 -0
  45. package/components/auth/AuthPageClient.tsx +314 -0
  46. package/components/comment/CommentEditor.tsx +290 -0
  47. package/components/comment/CommentEmoji.tsx +110 -0
  48. package/components/comment/CommentIconButton.tsx +37 -0
  49. package/components/comment/CommentItem.tsx +135 -0
  50. package/components/comment/CommentSection.tsx +123 -0
  51. package/components/comment/emojis.ts +152 -0
  52. package/components/knowledge/KnowledgeList.tsx +70 -0
  53. package/components/layout/BackToTop.tsx +27 -0
  54. package/components/layout/ConditionalSiteFooter.tsx +29 -0
  55. package/components/layout/ContentStylesLoader.tsx +9 -0
  56. package/components/layout/DoubleColumnLayout.tsx +171 -0
  57. package/components/layout/HeaderSearch.tsx +23 -0
  58. package/components/layout/LocaleSwitcher.tsx +24 -0
  59. package/components/layout/PageContainer.tsx +11 -0
  60. package/components/layout/PageLoadProgress.tsx +169 -0
  61. package/components/layout/SiteFooter.tsx +75 -0
  62. package/components/layout/SiteHeader.tsx +312 -0
  63. package/components/layout/SystemNotification.tsx +176 -0
  64. package/components/layout/ThemeSwitch.tsx +19 -0
  65. package/components/layout/UserAuth.tsx +130 -0
  66. package/components/search/SearchHero.tsx +323 -0
  67. package/components/search/SearchQuickLinks.tsx +285 -0
  68. package/components/shared/DevChunkRecovery.tsx +8 -0
  69. package/components/shared/Link.tsx +30 -0
  70. package/components/shared/SectionHeading.tsx +38 -0
  71. package/components/shared/ThemeImage.tsx +13 -0
  72. package/components/shared/logo.svg +7 -0
  73. package/components/views/ArchivesClient.tsx +137 -0
  74. package/components/views/CategoryClient.tsx +87 -0
  75. package/components/views/HomeClient.tsx +79 -0
  76. package/components/views/KnowledgeBookClient.tsx +127 -0
  77. package/components/views/KnowledgeChapterClient.tsx +175 -0
  78. package/components/views/KnowledgeClient.tsx +69 -0
  79. package/components/views/LoginClient.tsx +80 -0
  80. package/components/views/NavDetailClient.tsx +177 -0
  81. package/components/views/SearchClient.tsx +100 -0
  82. package/components/views/TagArchiveClient.tsx +105 -0
  83. package/components/views/TagsClient.tsx +46 -0
  84. package/components/widgets/AboutUs.tsx +180 -0
  85. package/components/widgets/ArticleActionButton.tsx +95 -0
  86. package/components/widgets/BookmarkWidget.tsx +61 -0
  87. package/components/widgets/CategoriesWidget.tsx +47 -0
  88. package/components/widgets/HomeSidebar.tsx +40 -0
  89. package/components/widgets/LikesWidget.tsx +77 -0
  90. package/components/widgets/ShareWidget.tsx +120 -0
  91. package/components/widgets/TagCloud.tsx +94 -0
  92. package/components/widgets/TagsWidget.tsx +160 -0
  93. package/eslint.config.mjs +80 -0
  94. package/lib/auth/githubOAuth.ts +26 -0
  95. package/lib/auth/sessionUser.ts +41 -0
  96. package/lib/mock-api/auth.ts +129 -0
  97. package/lib/mock-api/config.ts +11 -0
  98. package/lib/mock-api/cors.ts +53 -0
  99. package/lib/mock-api/data.ts +998 -0
  100. package/lib/mock-api/match-route.ts +202 -0
  101. package/lib/mock-api/proxy.ts +28 -0
  102. package/lib/providers/client.ts +58 -0
  103. package/lib/providers/materializeProvider.ts +9 -0
  104. package/lib/providers/server.ts +8 -0
  105. package/lib/reactpress/appearance.ts +69 -0
  106. package/lib/reactpress/bootstrap.ts +48 -0
  107. package/lib/reactpress/coercePublishedPage.ts +12 -0
  108. package/lib/reactpress/contentSeo.ts +123 -0
  109. package/lib/reactpress/env.ts +31 -0
  110. package/lib/reactpress/feedFooterPlacement.ts +16 -0
  111. package/lib/reactpress/fetchSetting.ts +19 -0
  112. package/lib/reactpress/homeFooter.tsx +27 -0
  113. package/lib/reactpress/layout-shell.tsx +19 -0
  114. package/lib/reactpress/materializeBootstrapCatalog.ts +31 -0
  115. package/lib/reactpress/normalizeApiResponse.ts +25 -0
  116. package/lib/reactpress/providers.tsx +199 -0
  117. package/lib/reactpress/resolveSiteNav.ts +56 -0
  118. package/lib/reactpress/seoMetadata.ts +82 -0
  119. package/lib/reactpress/serverLocale.ts +43 -0
  120. package/lib/reactpress/siteMetadata.ts +151 -0
  121. package/lib/reactpress/staticParams.ts +46 -0
  122. package/lib/reactpress/suggestionsContent.ts +48 -0
  123. package/lib/reactpress/useFeedFooterPlacement.ts +22 -0
  124. package/lib/reactpress/useSiteNavConfig.ts +39 -0
  125. package/lib/utils/archiveBanner.ts +8 -0
  126. package/lib/utils/colors.ts +119 -0
  127. package/lib/utils/comment.ts +46 -0
  128. package/lib/utils/headerPosition.ts +55 -0
  129. package/lib/utils/icons.tsx +238 -0
  130. package/lib/utils/reading.ts +16 -0
  131. package/lib/utils/scroll.ts +8 -0
  132. package/lib/utils/tagCloud.ts +222 -0
  133. package/next.config.js +103 -0
  134. package/package.json +98 -0
  135. package/postcss.config.js +5 -0
  136. package/prettier.config.js +10 -0
  137. package/public/apple-touch-icon.png +0 -0
  138. package/public/cli.png +0 -0
  139. package/public/favicon-32.png +0 -0
  140. package/public/favicon.ico +0 -0
  141. package/public/home-dark.png +0 -0
  142. package/public/home-light.png +0 -0
  143. package/public/icon-192.png +0 -0
  144. package/public/icon-512.png +0 -0
  145. package/public/logo-400.png +0 -0
  146. package/public/logo.png +0 -0
  147. package/public/mobile.png +0 -0
  148. package/public/pad.png +0 -0
  149. package/public/site.webmanifest +19 -0
  150. package/scripts/build-mock.mjs +139 -0
  151. package/scripts/dev.mjs +221 -0
  152. package/scripts/ensure-env.mjs +100 -0
  153. package/scripts/remote-dev.mjs +143 -0
  154. package/scripts/resolve-api-env.mjs +114 -0
  155. package/scripts/set-publish-version.mjs +31 -0
  156. package/scripts/smoke-pages.mjs +113 -0
  157. package/scripts/start-mock.mjs +79 -0
  158. package/scripts/verify-toolkit.mjs +37 -0
  159. package/svg.d.ts +6 -0
  160. package/theme.json +123 -0
  161. package/theme.manifest.schema.json +265 -0
  162. package/tsconfig.json +41 -0
  163. package/vercel.json +12 -0
@@ -0,0 +1,129 @@
1
+ type MockUser = {
2
+ id: string
3
+ name: string
4
+ password: string
5
+ email: string
6
+ avatar: string
7
+ role: string
8
+ status: string
9
+ type: string
10
+ createAt: string
11
+ updateAt: string
12
+ }
13
+
14
+ const now = () => new Date().toISOString()
15
+
16
+ const mockUsers: MockUser[] = [
17
+ {
18
+ id: 'mock-user-demo',
19
+ name: 'demo',
20
+ password: 'demo123',
21
+ email: 'demo@example.com',
22
+ avatar: '',
23
+ role: 'visitor',
24
+ status: 'active',
25
+ type: 'local',
26
+ createAt: now(),
27
+ updateAt: now(),
28
+ },
29
+ ]
30
+
31
+ function findUserByName(name: string) {
32
+ return mockUsers.find((user) => user.name === name.trim())
33
+ }
34
+
35
+ function toPublicUser(user: MockUser, token: string) {
36
+ const { password: _password, ...rest } = user
37
+ return { ...rest, token: `mock-token-${token}` }
38
+ }
39
+
40
+ function jsonError(message: string, status = 400): Response {
41
+ return Response.json({ success: false, msg: message, statusCode: status }, { status })
42
+ }
43
+
44
+ export function mockAuthLogin(body: unknown): Response {
45
+ const payload = body && typeof body === 'object' ? (body as Record<string, unknown>) : {}
46
+ const name = typeof payload.name === 'string' ? payload.name.trim() : ''
47
+ const password = typeof payload.password === 'string' ? payload.password : ''
48
+
49
+ if (!name || !password) {
50
+ return jsonError('Please enter username and password')
51
+ }
52
+
53
+ const user = findUserByName(name)
54
+ if (!user || user.password !== password) {
55
+ return jsonError('Invalid username or password')
56
+ }
57
+ if (user.status === 'locked') {
58
+ return jsonError('Account is locked')
59
+ }
60
+
61
+ return Response.json({ success: true, data: toPublicUser(user, user.name) })
62
+ }
63
+
64
+ export function mockAuthRegister(body: unknown): Response {
65
+ const payload = body && typeof body === 'object' ? (body as Record<string, unknown>) : {}
66
+ const name = typeof payload.name === 'string' ? payload.name.trim() : ''
67
+ const password = typeof payload.password === 'string' ? payload.password : ''
68
+ const email = typeof payload.email === 'string' ? payload.email.trim() : ''
69
+
70
+ if (!name || !password) {
71
+ return jsonError('Please enter username and password')
72
+ }
73
+ if (findUserByName(name)) {
74
+ return jsonError('Username already exists')
75
+ }
76
+
77
+ const user: MockUser = {
78
+ id: `mock-user-${Date.now()}`,
79
+ name,
80
+ password,
81
+ email,
82
+ avatar: '',
83
+ role: 'visitor',
84
+ status: 'active',
85
+ type: 'local',
86
+ createAt: now(),
87
+ updateAt: now(),
88
+ }
89
+ mockUsers.push(user)
90
+
91
+ const { password: _password, ...rest } = user
92
+ return Response.json({ success: true, data: rest }, { status: 201 })
93
+ }
94
+
95
+ export function mockAuthGithub(body: unknown): Response {
96
+ const code =
97
+ body && typeof body === 'object'
98
+ ? typeof (body as Record<string, unknown>).code === 'string'
99
+ ? (body as Record<string, string>).code
100
+ : ''
101
+ : typeof body === 'string'
102
+ ? body
103
+ : ''
104
+
105
+ if (!code.trim()) {
106
+ return jsonError('Please enter a GitHub authorization code')
107
+ }
108
+
109
+ const user: MockUser = {
110
+ id: 'mock-user-github',
111
+ name: 'Github User',
112
+ password: '',
113
+ email: 'github@example.com',
114
+ avatar: 'https://avatars.githubusercontent.com/u/9919?s=96&v=4',
115
+ role: 'visitor',
116
+ status: 'active',
117
+ type: 'github',
118
+ createAt: now(),
119
+ updateAt: now(),
120
+ }
121
+
122
+ const existing = findUserByName(user.name)
123
+ const resolved = existing ?? user
124
+ if (!existing) {
125
+ mockUsers.push(user)
126
+ }
127
+
128
+ return Response.json({ success: true, data: toPublicUser(resolved, resolved.name) })
129
+ }
@@ -0,0 +1,11 @@
1
+ export function isMockApiEnabled(): boolean {
2
+ return process.env.REACTPRESS_MOCK_API === '1'
3
+ }
4
+
5
+ export function resolveUpstreamApiBase(): string {
6
+ return (
7
+ process.env.REACTPRESS_API_URL?.trim() ||
8
+ process.env.SERVER_API_URL?.trim() ||
9
+ 'http://localhost:3002/api'
10
+ ).replace(/\/$/, '')
11
+ }
@@ -0,0 +1,53 @@
1
+ const ALLOWED_METHODS = 'GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD'
2
+
3
+ function resolveAllowedOrigin(request: Request): string | null {
4
+ const origin = request.headers.get('Origin')?.trim()
5
+ if (!origin) return null
6
+
7
+ try {
8
+ const url = new URL(origin)
9
+ if (url.hostname === 'localhost' || url.hostname === '127.0.0.1') {
10
+ return origin
11
+ }
12
+ } catch {
13
+ // ignore invalid Origin
14
+ }
15
+
16
+ return null
17
+ }
18
+
19
+ export function corsPreflightResponse(request: Request): Response {
20
+ const origin = resolveAllowedOrigin(request)
21
+ const headers = new Headers({
22
+ 'Access-Control-Allow-Methods': ALLOWED_METHODS,
23
+ 'Access-Control-Allow-Headers':
24
+ request.headers.get('Access-Control-Request-Headers') ??
25
+ 'Authorization, Content-Type, Accept',
26
+ 'Access-Control-Max-Age': '86400',
27
+ Vary: 'Origin',
28
+ })
29
+
30
+ if (origin) {
31
+ headers.set('Access-Control-Allow-Origin', origin)
32
+ headers.set('Access-Control-Allow-Credentials', 'true')
33
+ } else {
34
+ headers.set('Access-Control-Allow-Origin', '*')
35
+ }
36
+
37
+ return new Response(null, { status: 204, headers })
38
+ }
39
+
40
+ export function withCors(request: Request, response: Response): Response {
41
+ const origin = resolveAllowedOrigin(request)
42
+ if (!origin) return response
43
+
44
+ const headers = new Headers(response.headers)
45
+ headers.set('Access-Control-Allow-Origin', origin)
46
+ headers.set('Access-Control-Allow-Credentials', 'true')
47
+ headers.set('Vary', 'Origin')
48
+ return new Response(response.body, {
49
+ status: response.status,
50
+ statusText: response.statusText,
51
+ headers,
52
+ })
53
+ }