@x-wave/blog 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.
@@ -0,0 +1,156 @@
1
+ import i18n from 'i18next'
2
+ import LanguageDetector from 'i18next-browser-languagedetector'
3
+ import { initReactI18next } from 'react-i18next'
4
+
5
+ const resources = {
6
+ en: {
7
+ translation: {
8
+ language: 'English',
9
+ languageCode: 'en',
10
+ nav: {
11
+ gettingStarted: 'Getting Started',
12
+ helpResources: 'Help Resources',
13
+ guides: 'Guides',
14
+ reference: 'Reference',
15
+ },
16
+ docs: {
17
+ helloWorld: 'Welcome!',
18
+ gettingStarted: 'Getting Started',
19
+ glossary: 'Glossary',
20
+ faq: 'FAQ',
21
+ },
22
+ ui: {
23
+ simple: 'Simple',
24
+ advanced: 'Advanced',
25
+ switchLanguage: 'Switch Language',
26
+ tableOfContents: 'Table of Contents',
27
+ onThisPage: 'On this page',
28
+ launchApp: 'Launch App',
29
+ support: 'Support',
30
+ discord: 'Discord',
31
+ email: 'Email',
32
+ theme: 'Theme',
33
+ light: 'Light',
34
+ dark: 'Dark',
35
+ system: 'System',
36
+ search: 'Search',
37
+ searchPlaceholder: 'Search documentation...',
38
+ searchNavigate: 'to navigate',
39
+ searchSelect: 'to select',
40
+ searchClose: 'to close',
41
+ noSearchResults: 'No results found',
42
+ tags: 'Tags',
43
+ filterByTag: 'Filter by tag',
44
+ tagResults: 'Articles tagged with',
45
+ noTagResults: 'No articles found with this tag',
46
+ lastEdited: 'Last edited',
47
+ },
48
+ },
49
+ },
50
+ zh: {
51
+ translation: {
52
+ language: '中文',
53
+ languageCode: 'zh',
54
+ nav: {
55
+ gettingStarted: '开始使用',
56
+ helpResources: '帮助资源',
57
+ guides: '指南',
58
+ reference: '参考',
59
+ },
60
+ docs: {
61
+ helloWorld: '欢迎!',
62
+ gettingStarted: '开始使用',
63
+ glossary: '术语表',
64
+ faq: '常见问题',
65
+ },
66
+ ui: {
67
+ simple: '简单',
68
+ advanced: '高级',
69
+ switchLanguage: '切换语言',
70
+ tableOfContents: '目录',
71
+ onThisPage: '本页内容',
72
+ launchApp: '启动应用',
73
+ support: '支持',
74
+ discord: 'Discord',
75
+ email: '电子邮件',
76
+ theme: '主题',
77
+ light: '浅色',
78
+ dark: '深色',
79
+ system: '系统',
80
+ search: '搜索',
81
+ searchPlaceholder: '搜索文档...',
82
+ searchNavigate: '导航',
83
+ searchSelect: '选择',
84
+ searchClose: '关闭',
85
+ noSearchResults: '未找到结果',
86
+ tags: '标签',
87
+ filterByTag: '按标签筛选',
88
+ tagResults: '带有标签的文章',
89
+ noTagResults: '未找到带有此标签的文章',
90
+ lastEdited: '最后编辑',
91
+ },
92
+ },
93
+ },
94
+ es: {
95
+ translation: {
96
+ language: 'Español',
97
+ languageCode: 'es',
98
+ nav: {
99
+ gettingStarted: 'Primeros Pasos',
100
+ helpResources: 'Recursos de Ayuda',
101
+ guides: 'Guías',
102
+ reference: 'Referencia',
103
+ },
104
+ docs: {
105
+ helloWorld: '¡Bienvenido!',
106
+ gettingStarted: 'Primeros Pasos',
107
+ glossary: 'Glosario',
108
+ faq: 'Preguntas Frecuentes',
109
+ },
110
+ ui: {
111
+ simple: 'Simple',
112
+ advanced: 'Avanzado',
113
+ switchLanguage: 'Cambiar Idioma',
114
+ tableOfContents: 'Tabla de Contenidos',
115
+ onThisPage: 'En esta página',
116
+ launchApp: 'Iniciar App',
117
+ support: 'Soporte',
118
+ discord: 'Discord',
119
+ email: 'Correo',
120
+ theme: 'Tema',
121
+ light: 'Claro',
122
+ dark: 'Oscuro',
123
+ system: 'Sistema',
124
+ search: 'Buscar',
125
+ searchPlaceholder: 'Buscar documentación...',
126
+ searchNavigate: 'para navegar',
127
+ searchSelect: 'para seleccionar',
128
+ searchClose: 'para cerrar',
129
+ noSearchResults: 'No se encontraron resultados',
130
+ tags: 'Etiquetas',
131
+ filterByTag: 'Filtrar por etiqueta',
132
+ tagResults: 'Artículos etiquetados con',
133
+ noTagResults: 'No se encontraron artículos con esta etiqueta',
134
+ lastEdited: 'Última edición',
135
+ },
136
+ },
137
+ },
138
+ }
139
+
140
+ i18n
141
+ .use(LanguageDetector)
142
+ .use(initReactI18next)
143
+ .init({
144
+ resources,
145
+ fallbackLng: 'en',
146
+ debug: false,
147
+ interpolation: {
148
+ escapeValue: false,
149
+ },
150
+ detection: {
151
+ order: ['path', 'localStorage', 'navigator'],
152
+ lookupFromPathIndex: 0,
153
+ },
154
+ })
155
+
156
+ export default i18n
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@x-wave/blog",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": {
7
+ "import": "./index.js",
8
+ "types": "./index.d.ts"
9
+ },
10
+ "./locales": {
11
+ "import": "./locales/index.js",
12
+ "types": "./locales/index.d.ts"
13
+ },
14
+ "./consts": {
15
+ "import": "./consts/index.js",
16
+ "types": "./consts/index.d.ts"
17
+ },
18
+ "./types": {
19
+ "import": "./types/index.js",
20
+ "types": "./types/index.d.ts"
21
+ },
22
+ "./styles": {
23
+ "import": "./styles/main.scss"
24
+ }
25
+ },
26
+ "scripts": {
27
+ "build": "vite build",
28
+ "check": "biome check .",
29
+ "lint": "biome check --write .",
30
+ "clear": "rm -rf dist tsconfig.tsbuildinfo node_modules"
31
+ },
32
+ "dependencies": {
33
+ "@phosphor-icons/react": "^2.1.7",
34
+ "i18next": "^25.8.8",
35
+ "react-markdown": "^10.1.0",
36
+ "remark-gfm": "^4.0.1",
37
+ "styles": "workspace:*",
38
+ "types": "workspace:*"
39
+ },
40
+ "peerDependencies": {
41
+ "react": "^19.0.0",
42
+ "react-dom": "^19.0.0",
43
+ "react-i18next": "^15.7.3",
44
+ "react-router-dom": "^7.9.1"
45
+ },
46
+ "devDependencies": {
47
+ "@vitejs/plugin-react-swc": "^3.10.1",
48
+ "sass": "^1.97.3",
49
+ "vite": "^6.3.5",
50
+ "vite-tsconfig-paths": "^5.1.4"
51
+ }
52
+ }
@@ -0,0 +1,60 @@
1
+ // Color Variables - Using CSS custom properties for theme support
2
+ $color-background: var(--background);
3
+ $color-foreground: var(--foreground);
4
+ $color-primary: var(--primary);
5
+ $color-card: var(--card);
6
+ $color-card-foreground: var(--card-foreground);
7
+ $color-popover: var(--popover);
8
+ $color-popover-foreground: var(--popover-foreground);
9
+ $color-muted: var(--muted);
10
+ $color-muted-foreground: var(--muted-foreground);
11
+ $color-accent: var(--accent);
12
+ $color-accent-foreground: var(--accent-foreground);
13
+ $color-destructive: var(--destructive);
14
+ $color-border: var(--border);
15
+ $color-input: var(--input);
16
+ $color-ring: var(--ring);
17
+ $color-secondary: var(--secondary);
18
+ $color-secondary-foreground: var(--secondary-foreground);
19
+ $color-link: var(--link);
20
+ $color-sidebar: var(--sidebar);
21
+ $color-sidebar-foreground: var(--sidebar-foreground);
22
+
23
+ // Border Radius
24
+ $radius-base: 0.5rem;
25
+ $radius-sm: calc($radius-base * 0.5);
26
+ $radius-md: $radius-base;
27
+ $radius-lg: calc($radius-base * 1.5);
28
+ $radius-full: 9999px;
29
+
30
+ // Typography
31
+ $font-family-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
32
+ $font-family-mono: ui-monospace, 'SFMono-Regular', 'Consolas', 'Liberation Mono', 'Menlo', monospace;
33
+
34
+ // Spacing
35
+ $spacing-xs: 0.25rem;
36
+ $spacing-sm: 0.5rem;
37
+ $spacing-md: 1rem;
38
+ $spacing-lg: 1.5rem;
39
+ $spacing-xl: 2rem;
40
+
41
+ // Breakpoints
42
+ $breakpoint-md: 768px;
43
+ $breakpoint-lg: 1024px;
44
+ $breakpoint-xl: 1280px;
45
+
46
+ // Z-index
47
+ $z-index-dropdown: 1000;
48
+ $z-index-sticky: 1020;
49
+ $z-index-mobile-menu-backdrop: 1040;
50
+ $z-index-mobile-menu: 1050;
51
+ $z-index-search-overlay: 2000;
52
+ $z-index-search-modal: 2200;
53
+
54
+ // Shadows
55
+ $shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
56
+ $shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
57
+ $shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
58
+
59
+ // Transitions
60
+ $transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
@@ -0,0 +1,189 @@
1
+ @use './variables' as *;
2
+
3
+ // Global styles
4
+ *,
5
+ *::before,
6
+ *::after {
7
+ box-sizing: border-box;
8
+ }
9
+
10
+ * {
11
+ margin: 0;
12
+ padding: 0;
13
+ }
14
+
15
+ html,
16
+ body {
17
+ height: 100%;
18
+ }
19
+
20
+ body {
21
+ line-height: 1.5;
22
+ -webkit-font-smoothing: antialiased;
23
+ font-family: $font-family-sans;
24
+ background-color: $color-background;
25
+ color: $color-foreground;
26
+ }
27
+
28
+ img,
29
+ picture,
30
+ video,
31
+ canvas,
32
+ svg {
33
+ display: block;
34
+ max-width: 100%;
35
+ }
36
+
37
+ input,
38
+ button,
39
+ textarea,
40
+ select {
41
+ font: inherit;
42
+ }
43
+
44
+ p,
45
+ h1,
46
+ h2,
47
+ h3,
48
+ h4,
49
+ h5,
50
+ h6 {
51
+ overflow-wrap: break-word;
52
+ }
53
+
54
+ #root {
55
+ isolation: isolate;
56
+ height: 100%;
57
+ }
58
+
59
+ // Typography
60
+ h1 {
61
+ font-size: 1.875rem;
62
+ font-weight: 600;
63
+ line-height: 1.2;
64
+ letter-spacing: -0.025em;
65
+ margin-bottom: 1.5rem;
66
+ }
67
+
68
+ h2 {
69
+ font-size: 1.5rem;
70
+ font-weight: 500;
71
+ line-height: 1.3;
72
+ letter-spacing: -0.02em;
73
+ margin-bottom: 1rem;
74
+ margin-top: 2.5rem;
75
+ }
76
+
77
+ h3 {
78
+ font-size: 1.25rem;
79
+ font-weight: 500;
80
+ line-height: 1.4;
81
+ letter-spacing: -0.015em;
82
+ margin-bottom: 0.75rem;
83
+ margin-top: 2rem;
84
+ }
85
+
86
+ h4 {
87
+ font-size: 1.125rem;
88
+ font-weight: 500;
89
+ line-height: 1.4;
90
+ letter-spacing: -0.01em;
91
+ margin-bottom: 0.5rem;
92
+ margin-top: 1.5rem;
93
+ }
94
+
95
+ h5 {
96
+ font-size: 1rem;
97
+ font-weight: 500;
98
+ line-height: 1.5;
99
+ margin-bottom: 0.5rem;
100
+ margin-top: 1.25rem;
101
+ }
102
+
103
+ h6 {
104
+ font-size: 0.875rem;
105
+ font-weight: 500;
106
+ line-height: 1.5;
107
+ margin-bottom: 0.5rem;
108
+ margin-top: 1rem;
109
+ }
110
+
111
+ p {
112
+ font-size: 1rem;
113
+ line-height: 1.65;
114
+ margin-bottom: 1rem;
115
+ }
116
+
117
+ code {
118
+ font-family: $font-family-mono;
119
+ font-size: 0.875rem;
120
+ background-color: $color-muted;
121
+ padding: 0.1875rem 0.4375rem;
122
+ border-radius: $radius-sm;
123
+ font-weight: 500;
124
+ }
125
+
126
+ pre {
127
+ font-family: $font-family-mono;
128
+ background-color: $color-muted;
129
+ padding: 1.25rem;
130
+ border-radius: $radius-lg;
131
+ overflow-x: auto;
132
+ margin: 1.5rem 0;
133
+ border: 1px solid $color-border;
134
+
135
+ code {
136
+ background-color: transparent;
137
+ padding: 0;
138
+ font-weight: 400;
139
+ }
140
+ }
141
+
142
+ // Focus styles
143
+ :focus-visible {
144
+ outline: 2px solid $color-ring;
145
+ outline-offset: 2px;
146
+ }
147
+
148
+ // Selection styles
149
+ ::selection {
150
+ background-color: $color-accent;
151
+ color: $color-accent-foreground;
152
+ }
153
+
154
+ // Utility classes
155
+ .sr-only {
156
+ position: absolute !important;
157
+ width: 1px !important;
158
+ height: 1px !important;
159
+ padding: 0 !important;
160
+ margin: -1px !important;
161
+ overflow: hidden !important;
162
+ clip: rect(0, 0, 0, 0) !important;
163
+ white-space: nowrap !important;
164
+ border: 0 !important;
165
+ }
166
+
167
+ .text-muted {
168
+ color: $color-muted-foreground;
169
+ }
170
+
171
+ .text-destructive {
172
+ color: $color-destructive;
173
+ }
174
+
175
+ .bg-muted {
176
+ background-color: $color-muted;
177
+ }
178
+
179
+ .border {
180
+ border: 1px solid $color-border;
181
+ }
182
+
183
+ .rounded {
184
+ border-radius: $radius-md;
185
+ }
186
+
187
+ .shadow {
188
+ box-shadow: $shadow-md;
189
+ }
@@ -0,0 +1,92 @@
1
+ /*
2
+ Main styles entry point - imports all required stylesheets
3
+ */
4
+ @use './global.scss';
5
+
6
+ :root {
7
+ --radius: 0.625rem;
8
+ --background: oklch(1 0 0);
9
+ --foreground: oklch(0.145 0 0);
10
+ --card: oklch(1 0 0);
11
+ --card-foreground: oklch(0.145 0 0);
12
+ --popover: oklch(1 0 0);
13
+ --popover-foreground: oklch(0.145 0 0);
14
+ --primary: oklch(0.205 0 0);
15
+ --primary-foreground: oklch(0.985 0 0);
16
+ --secondary: oklch(0.97 0 0);
17
+ --secondary-foreground: oklch(0.205 0 0);
18
+ --muted: oklch(0.97 0 0);
19
+ --muted-foreground: oklch(0.556 0 0);
20
+ --accent: oklch(0.97 0 0);
21
+ --accent-foreground: oklch(0.205 0 0);
22
+ --destructive: oklch(0.577 0.245 27.325);
23
+ --border: oklch(0.922 0 0);
24
+ --input: oklch(0.922 0 0);
25
+ --ring: oklch(0.708 0 0);
26
+ --link: rgb(211, 48, 121);
27
+ --chart-1: oklch(0.646 0.222 41.116);
28
+ --chart-2: oklch(0.6 0.118 184.704);
29
+ --chart-3: oklch(0.398 0.07 227.392);
30
+ --chart-4: oklch(0.828 0.189 84.429);
31
+ --chart-5: oklch(0.769 0.188 70.08);
32
+ --sidebar: oklch(0.97 0 0);
33
+ --sidebar-foreground: oklch(0.145 0 0);
34
+ --sidebar-primary: oklch(0.205 0 0);
35
+ --sidebar-primary-foreground: oklch(0.985 0 0);
36
+ --sidebar-accent: oklch(0.97 0 0);
37
+ --sidebar-accent-foreground: oklch(0.205 0 0);
38
+ --sidebar-border: oklch(0.922 0 0);
39
+ --sidebar-ring: oklch(0.708 0 0);
40
+ }
41
+
42
+ /*
43
+ ---break---
44
+ */
45
+ .dark {
46
+ --background: oklch(0.145 0 0);
47
+ --foreground: oklch(0.985 0 0);
48
+ --card: oklch(0.205 0 0);
49
+ --card-foreground: oklch(0.985 0 0);
50
+ --popover: oklch(0.205 0 0);
51
+ --popover-foreground: oklch(0.985 0 0);
52
+ --primary: oklch(0.922 0 0);
53
+ --primary-foreground: oklch(0.205 0 0);
54
+ --secondary: oklch(0.269 0 0);
55
+ --secondary-foreground: oklch(0.985 0 0);
56
+ --muted: oklch(0.269 0 0);
57
+ --muted-foreground: oklch(0.708 0 0);
58
+ --accent: oklch(0.269 0 0);
59
+ --accent-foreground: oklch(0.985 0 0);
60
+ --destructive: oklch(0.704 0.191 22.216);
61
+ --border: oklch(1 0 0 / 10%);
62
+ --input: oklch(1 0 0 / 15%);
63
+ --ring: oklch(0.556 0 0);
64
+ --link: rgb(211, 48, 121);
65
+ --chart-1: oklch(0.488 0.243 264.376);
66
+ --chart-2: oklch(0.696 0.17 162.48);
67
+ --chart-3: oklch(0.769 0.188 70.08);
68
+ --chart-4: oklch(0.627 0.265 303.9);
69
+ --chart-5: oklch(0.645 0.246 16.439);
70
+ --sidebar: oklch(0.205 0 0);
71
+ --sidebar-foreground: oklch(0.985 0 0);
72
+ --sidebar-primary: oklch(0.488 0.243 264.376);
73
+ --sidebar-primary-foreground: oklch(0.985 0 0);
74
+ --sidebar-accent: oklch(0.269 0 0);
75
+ --sidebar-accent-foreground: oklch(0.985 0 0);
76
+ --sidebar-border: oklch(1 0 0 / 10%);
77
+ --sidebar-ring: oklch(0.556 0 0);
78
+ }
79
+
80
+ /* Basic reset and body styles */
81
+ * {
82
+ box-sizing: border-box;
83
+ margin: 0;
84
+ padding: 0;
85
+ }
86
+
87
+ body {
88
+ background-color: var(--background);
89
+ color: var(--foreground);
90
+ font-family: Inter, sans-serif;
91
+ line-height: 1.6;
92
+ }
@@ -0,0 +1,129 @@
1
+ @use './variables' as *;
2
+
3
+ // Mixins for common patterns
4
+ @mixin button-base {
5
+ display: inline-flex;
6
+ align-items: center;
7
+ justify-content: center;
8
+ white-space: nowrap;
9
+ border-radius: $radius-md;
10
+ font-size: 0.875rem;
11
+ font-weight: 500;
12
+ transition: $transition-fast;
13
+ cursor: pointer;
14
+ border: none;
15
+ outline: none;
16
+
17
+ &:focus-visible {
18
+ outline: 2px solid $color-ring;
19
+ outline-offset: 2px;
20
+ }
21
+
22
+ &:disabled {
23
+ pointer-events: none;
24
+ opacity: 0.5;
25
+ }
26
+ }
27
+
28
+ @mixin button-variant($bg, $color, $hover-bg: null) {
29
+ background-color: $bg;
30
+ color: $color;
31
+
32
+ @if $hover-bg {
33
+ &:hover {
34
+ background-color: $hover-bg;
35
+ }
36
+ } @else {
37
+ &:hover {
38
+ opacity: 0.9;
39
+ }
40
+ }
41
+ }
42
+
43
+ @mixin input-base {
44
+ display: flex;
45
+ width: 100%;
46
+ border-radius: $radius-md;
47
+ border: 1px solid $color-input;
48
+ background-color: $color-background;
49
+ padding: $spacing-sm $spacing-md;
50
+ font-size: 0.875rem;
51
+ transition: $transition-fast;
52
+
53
+ &:focus {
54
+ outline: none;
55
+ box-shadow: 0 0 0 2px $color-ring;
56
+ }
57
+
58
+ &:disabled {
59
+ cursor: not-allowed;
60
+ opacity: 0.5;
61
+ }
62
+
63
+ &::placeholder {
64
+ color: $color-muted-foreground;
65
+ }
66
+ }
67
+
68
+ @mixin card {
69
+ border-radius: $radius-lg;
70
+ border: 1px solid $color-border;
71
+ background-color: $color-card;
72
+ color: $color-card-foreground;
73
+ box-shadow: $shadow-sm;
74
+ }
75
+
76
+ @mixin flex-center {
77
+ display: flex;
78
+ align-items: center;
79
+ justify-content: center;
80
+ }
81
+
82
+ @mixin flex-between {
83
+ display: flex;
84
+ align-items: center;
85
+ justify-content: space-between;
86
+ }
87
+
88
+ @mixin text-truncate {
89
+ overflow: hidden;
90
+ text-overflow: ellipsis;
91
+ white-space: nowrap;
92
+ }
93
+
94
+ @mixin visually-hidden {
95
+ position: absolute !important;
96
+ width: 1px !important;
97
+ height: 1px !important;
98
+ padding: 0 !important;
99
+ margin: -1px !important;
100
+ overflow: hidden !important;
101
+ clip: rect(0, 0, 0, 0) !important;
102
+ white-space: nowrap !important;
103
+ border: 0 !important;
104
+ }
105
+
106
+ // Responsive mixins
107
+ @mixin mobile {
108
+ @media (max-width: #{$breakpoint-md - 1px}) {
109
+ @content;
110
+ }
111
+ }
112
+
113
+ @mixin tablet {
114
+ @media (min-width: #{$breakpoint-md}) and (max-width: #{$breakpoint-lg - 1px}) {
115
+ @content;
116
+ }
117
+ }
118
+
119
+ @mixin desktop {
120
+ @media (min-width: #{$breakpoint-lg}) {
121
+ @content;
122
+ }
123
+ }
124
+
125
+ @mixin large-desktop {
126
+ @media (min-width: #{$breakpoint-xl}) {
127
+ @content;
128
+ }
129
+ }
package/types/index.ts ADDED
@@ -0,0 +1,27 @@
1
+ export interface NavigationItem {
2
+ title: string
3
+ slug: string
4
+ hasAdvanced?: boolean
5
+ showTableOfContents?: boolean
6
+ }
7
+
8
+ export interface NavigationSection {
9
+ title: string
10
+ items: NavigationItem[]
11
+ defaultOpen?: boolean
12
+ }
13
+
14
+ export type NavigationEntry = NavigationSection | NavigationItem
15
+
16
+ export interface LoadedContent {
17
+ content: string
18
+ frontmatter: {
19
+ hasAdvanced?: boolean
20
+ tags?: string[]
21
+ date?: string
22
+ author?: string
23
+ [key: string]: unknown
24
+ }
25
+ }
26
+
27
+ export type SupportedLanguage = string