@fsegurai/marked-extended-tabs 17.0.0-beta.2 → 17.0.0-beta.3
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/README.custom.md +443 -190
- package/README.md +443 -190
- package/dist/index.cjs +265 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +265 -6
- package/dist/index.esm.js.map +1 -1
- package/dist/index.umd.js +265 -6
- package/dist/index.umd.js.map +1 -1
- package/dist/types/renderer.d.ts +1 -1
- package/dist/types/types.d.ts +29 -0
- package/dist/types/utils/constants.d.ts +12 -0
- package/package.json +1 -1
package/README.custom.md
CHANGED
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
## 🎯 Overview
|
|
4
4
|
|
|
5
|
-
The **marked-extended-tabs** extension transforms your Markdown into interactive tabbed content sections with support
|
|
5
|
+
The **marked-extended-tabs** extension transforms your Markdown into interactive tabbed content sections with support
|
|
6
|
+
for custom icons, smooth animations, and nested Markdown content. With a CSS-only implementation for excellent
|
|
7
|
+
performance and accessibility, it's perfect for code examples, documentation, API references, tutorials, and organizing
|
|
8
|
+
complex content.
|
|
6
9
|
|
|
7
10
|
### ✨ Key Features
|
|
8
11
|
|
|
@@ -12,8 +15,11 @@ The **marked-extended-tabs** extension transforms your Markdown into interactive
|
|
|
12
15
|
- 🚀 **CSS-Only**: Pure CSS implementation for optimal performance
|
|
13
16
|
- 📝 **Rich Content**: Full Markdown support including code, tables, lists
|
|
14
17
|
- ♿ **Fully Accessible**: ARIA labels, keyboard navigation, semantic HTML
|
|
18
|
+
- ⌨️ **Keyboard Navigation**: Arrow keys, Home/End keys for tab switching
|
|
19
|
+
- 💾 **Persist Selection**: Remember tab choice across sessions (localStorage)
|
|
15
20
|
- 🎯 **Auto-Activation**: Automatically activate first tab
|
|
16
|
-
-
|
|
21
|
+
- 🎪 **Custom Events**: `tab-switched` events for external script integration
|
|
22
|
+
- 🔄 **Callback Hooks**: `onBeforeSwitch` and `onAfterSwitch` callbacks
|
|
17
23
|
- 🎨 **Customizable Styles**: Multiple variants (compact, vertical, pills)
|
|
18
24
|
- 🌈 **Dark Mode Ready**: Theme support included
|
|
19
25
|
- 📱 **Responsive**: Mobile-friendly with scrollable tabs
|
|
@@ -32,6 +38,7 @@ Experience all tab styles and animations: [View Demo](https://fsegurai.github.io
|
|
|
32
38
|
- [Tab Features](#tab-features)
|
|
33
39
|
- [Animation Types](#animation-types)
|
|
34
40
|
- [Configuration Options](#configuration-options)
|
|
41
|
+
- [Enhanced Tab Switching Features](#enhanced-tab-switching-features)
|
|
35
42
|
- [Use Cases](#use-cases)
|
|
36
43
|
- [Aliases](#aliases)
|
|
37
44
|
- [Advanced Examples](#advanced-examples)
|
|
@@ -42,7 +49,8 @@ Experience all tab styles and animations: [View Demo](https://fsegurai.github.io
|
|
|
42
49
|
|
|
43
50
|
### Basic Concept
|
|
44
51
|
|
|
45
|
-
**`tabs` is the identifier for the tabbed container, and `tab` for individual tab items. Each tab can have a label,
|
|
52
|
+
**`tabs` is the identifier for the tabbed container, and `tab` for individual tab items. Each tab can have a label,
|
|
53
|
+
icon, and active state.**
|
|
46
54
|
|
|
47
55
|
<!-- SECTION:CUSTOM_USAGE_EXAMPLE -->
|
|
48
56
|
|
|
@@ -67,7 +75,7 @@ pnpm add @fsegurai/marked-extended-tabs
|
|
|
67
75
|
### Basic Implementation
|
|
68
76
|
|
|
69
77
|
```javascript
|
|
70
|
-
import {
|
|
78
|
+
import {marked} from 'marked';
|
|
71
79
|
import markedExtendedTabs from '@fsegurai/marked-extended-tabs';
|
|
72
80
|
|
|
73
81
|
// Import styles (required for functionality)
|
|
@@ -77,9 +85,9 @@ import '@fsegurai/marked-extended-tabs/styles/tabs-theme.css';
|
|
|
77
85
|
|
|
78
86
|
// Register the extension
|
|
79
87
|
marked.use(markedExtendedTabs({
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
88
|
+
animation: 'fade',
|
|
89
|
+
autoActivate: true,
|
|
90
|
+
persistSelection: false
|
|
83
91
|
}));
|
|
84
92
|
|
|
85
93
|
// Your markdown content with tabs
|
|
@@ -323,6 +331,7 @@ Tabs support full Markdown:
|
|
|
323
331
|
```markdown
|
|
324
332
|
::::tabs
|
|
325
333
|
:::tab{label="Documentation" icon="📖" active="true"}
|
|
334
|
+
|
|
326
335
|
# Getting Started
|
|
327
336
|
|
|
328
337
|
Follow these steps:
|
|
@@ -344,6 +353,7 @@ const client = new Client();
|
|
|
344
353
|
:::tabend
|
|
345
354
|
|
|
346
355
|
:::tab{label="FAQ" icon="❓"}
|
|
356
|
+
|
|
347
357
|
## Frequently Asked Questions
|
|
348
358
|
|
|
349
359
|
### How do I authenticate?
|
|
@@ -355,8 +365,8 @@ Use your API key in the Authorization header.
|
|
|
355
365
|
- Free tier: 100 requests/hour
|
|
356
366
|
- Pro tier: 10,000 requests/hour
|
|
357
367
|
- Enterprise: Custom limits
|
|
358
|
-
:::tabend
|
|
359
|
-
::::tabsend
|
|
368
|
+
:::tabend
|
|
369
|
+
::::tabsend
|
|
360
370
|
```
|
|
361
371
|
|
|
362
372
|
#### Short Aliases
|
|
@@ -382,7 +392,8 @@ system.
|
|
|
382
392
|
|
|
383
393
|
### Styling Your Tabs
|
|
384
394
|
|
|
385
|
-
This extension provides optional CSS/SCSS files located in the `styles/` directory. Import them into your project to
|
|
395
|
+
This extension provides optional CSS/SCSS files located in the `styles/` directory. Import them into your project to
|
|
396
|
+
style the tabs.
|
|
386
397
|
|
|
387
398
|
Usage examples:
|
|
388
399
|
|
|
@@ -398,7 +409,6 @@ import '@fsegurai/marked-extended-tabs/styles/tabs-theme.css';
|
|
|
398
409
|
<link rel="stylesheet" href="node_modules/@fsegurai/marked-extended-tabs/styles/tabs-theme.css">
|
|
399
410
|
```
|
|
400
411
|
|
|
401
|
-
|
|
402
412
|
#### Generated HTML Structure
|
|
403
413
|
|
|
404
414
|
```html
|
|
@@ -702,11 +712,15 @@ Individual tabs can use these aliases:
|
|
|
702
712
|
The marked-extended-tabs extension accepts the following configuration options:
|
|
703
713
|
|
|
704
714
|
- `className`: The base CSS class name for tabs container. Defaults to 'marked-extended-tabs-container'.
|
|
705
|
-
- `persistSelection`: Whether to remember tab selection across page reloads. Defaults to
|
|
715
|
+
- `persistSelection`: Whether to remember tab selection across page reloads. Defaults to **true**.
|
|
706
716
|
- `animation`: Animation type for tab transitions. Defaults to 'fade'. See [Animation Types](#animation-types).
|
|
707
717
|
- `autoActivate`: Automatically activate the first tab if none is explicitly active. Defaults to true.
|
|
708
718
|
- `template`: A custom HTML template for the tabs structure. Defaults to the built-in template.
|
|
709
719
|
- `customizeToken`: A function that allows you to customize the token object before rendering. Defaults to null.
|
|
720
|
+
- `enableKeyboardNavigation`: Enable arrow key navigation between tabs. Defaults to **true**.
|
|
721
|
+
- `enableFocusManagement`: Enable automatic focus management for accessibility. Defaults to **true**.
|
|
722
|
+
- `onBeforeSwitch`: Callback function fired before switching tabs (can prevent switch). Defaults to null.
|
|
723
|
+
- `onAfterSwitch`: Callback function fired after switching tabs. Defaults to null.
|
|
710
724
|
|
|
711
725
|
Tab syntax parameters:
|
|
712
726
|
|
|
@@ -714,6 +728,160 @@ Tab syntax parameters:
|
|
|
714
728
|
- `active`: Whether this tab is active by default ("true" or "false"). Defaults to "false".
|
|
715
729
|
- `icon`: An optional icon (emoji or Unicode) to display in the tab header. No default.
|
|
716
730
|
|
|
731
|
+
### Enhanced Tab Switching Features
|
|
732
|
+
|
|
733
|
+
The marked-extended-tabs extension now includes advanced interactivity features that enhance user experience and
|
|
734
|
+
accessibility:
|
|
735
|
+
|
|
736
|
+
#### 🎯 Keyboard Navigation
|
|
737
|
+
|
|
738
|
+
Users can navigate between tabs using keyboard shortcuts:
|
|
739
|
+
|
|
740
|
+
- **Arrow Right / Arrow Down** - Move to the next tab
|
|
741
|
+
- **Arrow Left / Arrow Up** - Move to the previous tab
|
|
742
|
+
- **Home** - Jump to the first tab
|
|
743
|
+
- **End** - Jump to the last tab
|
|
744
|
+
- **Space / Enter** - Activate the focused tab
|
|
745
|
+
|
|
746
|
+
Keyboard navigation is **enabled by default**. Disable it with:
|
|
747
|
+
|
|
748
|
+
```javascript
|
|
749
|
+
marked.use(markedExtendedTabs({
|
|
750
|
+
enableKeyboardNavigation: false
|
|
751
|
+
}));
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
#### ♿ Focus Management
|
|
755
|
+
|
|
756
|
+
Automatic focus management for keyboard accessibility:
|
|
757
|
+
|
|
758
|
+
- Tabs are properly tabindexed for keyboard navigation
|
|
759
|
+
- Active tab receives focus index 0
|
|
760
|
+
- Other tabs have focus index -1
|
|
761
|
+
- Focus automatically updates when switching tabs
|
|
762
|
+
- Screen reader support with ARIA attributes
|
|
763
|
+
|
|
764
|
+
Enable/disable with:
|
|
765
|
+
|
|
766
|
+
```javascript
|
|
767
|
+
marked.use(markedExtendedTabs({
|
|
768
|
+
enableFocusManagement: true // Default: true
|
|
769
|
+
}));
|
|
770
|
+
```
|
|
771
|
+
|
|
772
|
+
#### 💾 State Persistence
|
|
773
|
+
|
|
774
|
+
Tab selection is automatically persisted to localStorage:
|
|
775
|
+
|
|
776
|
+
- Each tab container has a unique storage key
|
|
777
|
+
- When the page is reloaded, the previously selected tab is restored
|
|
778
|
+
- Multiple tab groups maintain separate states
|
|
779
|
+
- Works across browser sessions
|
|
780
|
+
|
|
781
|
+
Enable/disable with:
|
|
782
|
+
|
|
783
|
+
```javascript
|
|
784
|
+
marked.use(markedExtendedTabs({
|
|
785
|
+
persistSelection: true // Default: true
|
|
786
|
+
}));
|
|
787
|
+
```
|
|
788
|
+
|
|
789
|
+
Storage key format: `marked-extended-tabs-active-{containerId}`
|
|
790
|
+
|
|
791
|
+
#### 🎪 Custom Events
|
|
792
|
+
|
|
793
|
+
When a tab is switched, a custom `tab-switched` event is automatically dispatched:
|
|
794
|
+
|
|
795
|
+
```javascript
|
|
796
|
+
document.addEventListener('tab-switched', (event) => {
|
|
797
|
+
console.log('Tab switched:', event.detail.tabId);
|
|
798
|
+
console.log('Timestamp:', event.detail.timestamp);
|
|
799
|
+
});
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
Event detail structure:
|
|
803
|
+
|
|
804
|
+
```javascript
|
|
805
|
+
{
|
|
806
|
+
tabId: string; // ID of the newly active tab
|
|
807
|
+
timestamp: number; // Timestamp of the switch event (milliseconds)
|
|
808
|
+
}
|
|
809
|
+
```
|
|
810
|
+
|
|
811
|
+
#### 🔄 Callback Hooks
|
|
812
|
+
|
|
813
|
+
Execute custom logic before and after tab switches using callback functions:
|
|
814
|
+
|
|
815
|
+
```javascript
|
|
816
|
+
marked.use(markedExtendedTabs({
|
|
817
|
+
onBeforeSwitch: (event) => {
|
|
818
|
+
console.log('Switching from', event.previousTabId, 'to', event.newTabId);
|
|
819
|
+
// Return false to prevent the switch
|
|
820
|
+
return true;
|
|
821
|
+
},
|
|
822
|
+
onAfterSwitch: (event) => {
|
|
823
|
+
console.log('Switched to tab:', event.newTabId);
|
|
824
|
+
}
|
|
825
|
+
}));
|
|
826
|
+
```
|
|
827
|
+
|
|
828
|
+
**onBeforeSwitch event detail:**
|
|
829
|
+
|
|
830
|
+
```javascript
|
|
831
|
+
{
|
|
832
|
+
previousTabId: string | null; // ID of the currently active tab (null on first)
|
|
833
|
+
newTabId: string; // ID of the tab being switched to
|
|
834
|
+
tabIndex: number; // Index of the new tab
|
|
835
|
+
timestamp: number; // Timestamp of the event
|
|
836
|
+
}
|
|
837
|
+
```
|
|
838
|
+
|
|
839
|
+
Return `false` from `onBeforeSwitch` to prevent the tab switch. Return `true` or any truthy value to allow it.
|
|
840
|
+
|
|
841
|
+
**onAfterSwitch event detail:**
|
|
842
|
+
|
|
843
|
+
```javascript
|
|
844
|
+
{
|
|
845
|
+
previousTabId: string | null; // ID of the previously active tab (null on first)
|
|
846
|
+
newTabId: string; // ID of the newly active tab
|
|
847
|
+
tabIndex: number; // Index of the new tab
|
|
848
|
+
timestamp: number; // Timestamp of the event
|
|
849
|
+
}
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
#### 📋 Complete Configuration Example
|
|
853
|
+
|
|
854
|
+
```javascript
|
|
855
|
+
import {marked} from 'marked';
|
|
856
|
+
import markedExtendedTabs from '@fsegurai/marked-extended-tabs';
|
|
857
|
+
import '@fsegurai/marked-extended-tabs/styles/tabs.css';
|
|
858
|
+
|
|
859
|
+
marked.use(markedExtendedTabs({
|
|
860
|
+
animation: 'fade',
|
|
861
|
+
autoActivate: true,
|
|
862
|
+
persistSelection: true,
|
|
863
|
+
enableKeyboardNavigation: true,
|
|
864
|
+
enableFocusManagement: true,
|
|
865
|
+
|
|
866
|
+
onBeforeSwitch: (event) => {
|
|
867
|
+
// Validate or prevent switches based on your logic
|
|
868
|
+
if (someUnsavedChanges) {
|
|
869
|
+
console.warn('Cannot switch - unsaved changes');
|
|
870
|
+
return false; // Prevent switch
|
|
871
|
+
}
|
|
872
|
+
return true; // Allow switch
|
|
873
|
+
},
|
|
874
|
+
|
|
875
|
+
onAfterSwitch: (event) => {
|
|
876
|
+
// Track analytics or update other UI elements
|
|
877
|
+
analytics.trackEvent('tab_switched', {
|
|
878
|
+
tab_id: event.newTabId,
|
|
879
|
+
tab_index: event.tabIndex
|
|
880
|
+
});
|
|
881
|
+
}
|
|
882
|
+
}));
|
|
883
|
+
```
|
|
884
|
+
|
|
717
885
|
### Animation Types
|
|
718
886
|
|
|
719
887
|
The extension supports three animation types:
|
|
@@ -813,16 +981,16 @@ marked.use(markedExtendedTabs({
|
|
|
813
981
|
|
|
814
982
|
The extension provides comprehensive tab functionality:
|
|
815
983
|
|
|
816
|
-
| Feature
|
|
817
|
-
|
|
818
|
-
| **Labels**
|
|
819
|
-
| **Icons**
|
|
820
|
-
| **Active State**
|
|
821
|
-
| **Animations**
|
|
822
|
-
| **Persist Selection** | Remember choice
|
|
823
|
-
| **Auto-Activate**
|
|
824
|
-
| **Rich Content**
|
|
825
|
-
| **Nested Extensions** | Other extensions
|
|
984
|
+
| Feature | Description | Example | Default |
|
|
985
|
+
|-----------------------|--------------------|--------------------------|-----------------------------|
|
|
986
|
+
| **Labels** | Tab header text | `label="Overview"` | "Tab N" |
|
|
987
|
+
| **Icons** | Emoji or Unicode | `icon="📋"` | None |
|
|
988
|
+
| **Active State** | Default active tab | `active="true"` | First tab (if autoActivate) |
|
|
989
|
+
| **Animations** | Transition effects | `animation="fade"` | "fade" |
|
|
990
|
+
| **Persist Selection** | Remember choice | `persistSelection: true` | false |
|
|
991
|
+
| **Auto-Activate** | First tab active | `autoActivate: true` | true |
|
|
992
|
+
| **Rich Content** | Full Markdown | All Markdown syntax | Supported |
|
|
993
|
+
| **Nested Extensions** | Other extensions | Accordions, tables, etc. | Supported |
|
|
826
994
|
|
|
827
995
|
### Best Practices
|
|
828
996
|
|
|
@@ -880,24 +1048,34 @@ The extension provides comprehensive tab functionality:
|
|
|
880
1048
|
<!-- Good: Similar structure across tabs -->
|
|
881
1049
|
::::tabs
|
|
882
1050
|
:::tab{label="JavaScript" icon="📜"}
|
|
1051
|
+
|
|
883
1052
|
## Installation
|
|
1053
|
+
|
|
884
1054
|
\`\`\`bash
|
|
885
1055
|
npm install
|
|
886
1056
|
\`\`\`
|
|
1057
|
+
|
|
887
1058
|
## Usage
|
|
1059
|
+
|
|
888
1060
|
\`\`\`javascript
|
|
889
1061
|
// code
|
|
890
1062
|
\`\`\`
|
|
891
1063
|
:::tabend
|
|
892
1064
|
|
|
893
1065
|
:::tab{label="Python" icon="🐍"}
|
|
1066
|
+
|
|
894
1067
|
## Installation
|
|
1068
|
+
|
|
895
1069
|
\`\`\`bash
|
|
896
1070
|
pip install
|
|
897
1071
|
\`\`\`
|
|
1072
|
+
|
|
898
1073
|
## Usage
|
|
1074
|
+
|
|
899
1075
|
\`\`\`python
|
|
1076
|
+
|
|
900
1077
|
# code
|
|
1078
|
+
|
|
901
1079
|
\`\`\`
|
|
902
1080
|
:::tabend
|
|
903
1081
|
::::tabsend
|
|
@@ -905,7 +1083,9 @@ pip install
|
|
|
905
1083
|
<!-- Avoid: Inconsistent structure -->
|
|
906
1084
|
::::tabs
|
|
907
1085
|
:::tab{label="Tab 1"}
|
|
1086
|
+
|
|
908
1087
|
# Heading
|
|
1088
|
+
|
|
909
1089
|
Content
|
|
910
1090
|
:::tabend
|
|
911
1091
|
|
|
@@ -951,18 +1131,19 @@ For experienced users
|
|
|
951
1131
|
|
|
952
1132
|
::::tabs
|
|
953
1133
|
:::tab{label="JavaScript" icon="📜" active="true"}
|
|
1134
|
+
|
|
954
1135
|
### Using Fetch API
|
|
955
1136
|
|
|
956
1137
|
\`\`\`javascript
|
|
957
1138
|
const response = await fetch('https://api.example.com/auth/login', {
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1139
|
+
method: 'POST',
|
|
1140
|
+
headers: {
|
|
1141
|
+
'Content-Type': 'application/json'
|
|
1142
|
+
},
|
|
1143
|
+
body: JSON.stringify({
|
|
1144
|
+
username: 'user@example.com',
|
|
1145
|
+
password: 'secure_password'
|
|
1146
|
+
})
|
|
966
1147
|
});
|
|
967
1148
|
|
|
968
1149
|
const data = await response.json();
|
|
@@ -970,9 +1151,9 @@ console.log('Token:', data.access_token);
|
|
|
970
1151
|
|
|
971
1152
|
// Use token in subsequent requests
|
|
972
1153
|
const userData = await fetch('https://api.example.com/user/profile', {
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
1154
|
+
headers: {
|
|
1155
|
+
'Authorization': \`Bearer \${data.access_token}\`
|
|
1156
|
+
}
|
|
976
1157
|
});
|
|
977
1158
|
\`\`\`
|
|
978
1159
|
|
|
@@ -982,8 +1163,8 @@ const userData = await fetch('https://api.example.com/user/profile', {
|
|
|
982
1163
|
import axios from 'axios';
|
|
983
1164
|
|
|
984
1165
|
const { data } = await axios.post('https://api.example.com/auth/login', {
|
|
985
|
-
|
|
986
|
-
|
|
1166
|
+
username: 'user@example.com',
|
|
1167
|
+
password: 'secure_password'
|
|
987
1168
|
});
|
|
988
1169
|
|
|
989
1170
|
console.log('Token:', data.access_token);
|
|
@@ -994,18 +1175,20 @@ axios.defaults.headers.common['Authorization'] = \`Bearer \${data.access_token}\
|
|
|
994
1175
|
:::tabend
|
|
995
1176
|
|
|
996
1177
|
:::tab{label="Python" icon="🐍"}
|
|
1178
|
+
|
|
997
1179
|
### Using Requests
|
|
998
1180
|
|
|
999
1181
|
\`\`\`python
|
|
1000
1182
|
import requests
|
|
1001
1183
|
|
|
1002
1184
|
# Login request
|
|
1185
|
+
|
|
1003
1186
|
response = requests.post(
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1187
|
+
'https://api.example.com/auth/login',
|
|
1188
|
+
json={
|
|
1189
|
+
'username': 'user@example.com',
|
|
1190
|
+
'password': 'secure_password'
|
|
1191
|
+
}
|
|
1009
1192
|
)
|
|
1010
1193
|
|
|
1011
1194
|
data = response.json()
|
|
@@ -1013,9 +1196,10 @@ access_token = data['access_token']
|
|
|
1013
1196
|
print(f'Token: {access_token}')
|
|
1014
1197
|
|
|
1015
1198
|
# Use token for authenticated requests
|
|
1199
|
+
|
|
1016
1200
|
user_response = requests.get(
|
|
1017
|
-
|
|
1018
|
-
|
|
1201
|
+
'https://api.example.com/user/profile',
|
|
1202
|
+
headers={'Authorization': f'Bearer {access_token}'}
|
|
1019
1203
|
)
|
|
1020
1204
|
|
|
1021
1205
|
print(user_response.json())
|
|
@@ -1028,54 +1212,56 @@ import httpx
|
|
|
1028
1212
|
import asyncio
|
|
1029
1213
|
|
|
1030
1214
|
async def authenticate():
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1215
|
+
async with httpx.AsyncClient() as client:
|
|
1216
|
+
# Login
|
|
1217
|
+
response = await client.post(
|
|
1218
|
+
'https://api.example.com/auth/login',
|
|
1219
|
+
json={
|
|
1220
|
+
'username': 'user@example.com',
|
|
1221
|
+
'password': 'secure_password'
|
|
1222
|
+
}
|
|
1223
|
+
)
|
|
1224
|
+
|
|
1041
1225
|
data = response.json()
|
|
1042
1226
|
return data['access_token']
|
|
1043
1227
|
|
|
1044
1228
|
# Run async function
|
|
1229
|
+
|
|
1045
1230
|
token = asyncio.run(authenticate())
|
|
1046
1231
|
\`\`\`
|
|
1047
1232
|
:::tabend
|
|
1048
1233
|
|
|
1049
1234
|
:::tab{label="Go" icon="🔷"}
|
|
1235
|
+
|
|
1050
1236
|
### Using net/http
|
|
1051
1237
|
|
|
1052
1238
|
\`\`\`go
|
|
1053
1239
|
package main
|
|
1054
1240
|
|
|
1055
1241
|
import (
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1242
|
+
"bytes"
|
|
1243
|
+
"encoding/json"
|
|
1244
|
+
"fmt"
|
|
1245
|
+
"io/ioutil"
|
|
1246
|
+
"net/http"
|
|
1061
1247
|
)
|
|
1062
1248
|
|
|
1063
1249
|
type LoginRequest struct {
|
|
1064
|
-
|
|
1065
|
-
|
|
1250
|
+
Username string \`json:"username"\`
|
|
1251
|
+
Password string \`json:"password"\`
|
|
1066
1252
|
}
|
|
1067
1253
|
|
|
1068
1254
|
type LoginResponse struct {
|
|
1069
|
-
|
|
1255
|
+
AccessToken string \`json:"access_token"\`
|
|
1070
1256
|
}
|
|
1071
1257
|
|
|
1072
1258
|
func main() {
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1259
|
+
// Prepare login request
|
|
1260
|
+
loginData := LoginRequest{
|
|
1261
|
+
Username: "user@example.com",
|
|
1262
|
+
Password: "secure_password",
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1079
1265
|
jsonData, _ := json.Marshal(loginData)
|
|
1080
1266
|
|
|
1081
1267
|
// Make request
|
|
@@ -1095,40 +1281,45 @@ func main() {
|
|
|
1095
1281
|
json.Unmarshal(body, &loginResp)
|
|
1096
1282
|
|
|
1097
1283
|
fmt.Println("Token:", loginResp.AccessToken)
|
|
1284
|
+
|
|
1098
1285
|
}
|
|
1099
1286
|
\`\`\`
|
|
1100
1287
|
:::tabend
|
|
1101
1288
|
|
|
1102
1289
|
:::tab{label="cURL" icon="🔧"}
|
|
1290
|
+
|
|
1103
1291
|
### Basic Authentication Request
|
|
1104
1292
|
|
|
1105
1293
|
\`\`\`bash
|
|
1106
1294
|
curl -X POST https://api.example.com/auth/login \\
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1295
|
+
-H "Content-Type: application/json" \\
|
|
1296
|
+
-d '{
|
|
1297
|
+
"username": "user@example.com",
|
|
1298
|
+
"password": "secure_password"
|
|
1299
|
+
}'
|
|
1112
1300
|
\`\`\`
|
|
1113
1301
|
|
|
1114
1302
|
**Response:**
|
|
1115
1303
|
\`\`\`json
|
|
1116
1304
|
{
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1305
|
+
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
|
1306
|
+
"token_type": "Bearer",
|
|
1307
|
+
"expires_in": 3600
|
|
1120
1308
|
}
|
|
1121
1309
|
\`\`\`
|
|
1122
1310
|
|
|
1123
1311
|
### Using the Token
|
|
1124
1312
|
|
|
1125
1313
|
\`\`\`bash
|
|
1314
|
+
|
|
1126
1315
|
# Save token to variable
|
|
1316
|
+
|
|
1127
1317
|
TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
|
1128
1318
|
|
|
1129
1319
|
# Make authenticated request
|
|
1320
|
+
|
|
1130
1321
|
curl -X GET https://api.example.com/user/profile \\
|
|
1131
|
-
|
|
1322
|
+
-H "Authorization: Bearer \$TOKEN"
|
|
1132
1323
|
\`\`\`
|
|
1133
1324
|
:::tabend
|
|
1134
1325
|
::::tabsend
|
|
@@ -1139,11 +1330,11 @@ All endpoints return standard error responses:
|
|
|
1139
1330
|
|
|
1140
1331
|
\`\`\`json
|
|
1141
1332
|
{
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1333
|
+
"error": {
|
|
1334
|
+
"code": "INVALID_CREDENTIALS",
|
|
1335
|
+
"message": "The username or password is incorrect",
|
|
1336
|
+
"timestamp": "2026-02-17T10:30:00Z"
|
|
1337
|
+
}
|
|
1147
1338
|
}
|
|
1148
1339
|
\`\`\`
|
|
1149
1340
|
```
|
|
@@ -1157,11 +1348,12 @@ All endpoints return standard error responses:
|
|
|
1157
1348
|
|
|
1158
1349
|
::::tabs
|
|
1159
1350
|
:::tab{label="Windows" icon="🪟" active="true"}
|
|
1351
|
+
|
|
1160
1352
|
### Using Installer
|
|
1161
1353
|
|
|
1162
1354
|
1. **Download** the Windows installer:
|
|
1163
|
-
|
|
1164
|
-
|
|
1355
|
+
- [Download for Windows (64-bit)](https://example.com/download/windows-x64)
|
|
1356
|
+
- [Download for Windows (32-bit)](https://example.com/download/windows-x86)
|
|
1165
1357
|
|
|
1166
1358
|
2. **Run** the installer and follow the setup wizard
|
|
1167
1359
|
|
|
@@ -1193,12 +1385,14 @@ winget install MyCompany.MyApp
|
|
|
1193
1385
|
|
|
1194
1386
|
**"Command not found" error:**
|
|
1195
1387
|
Add installation directory to PATH:
|
|
1388
|
+
|
|
1196
1389
|
1. Open System Properties → Environment Variables
|
|
1197
1390
|
2. Edit PATH variable
|
|
1198
1391
|
3. Add: \`C:\\Program Files\\MyApp\\bin\`
|
|
1199
|
-
:::tabend
|
|
1392
|
+
:::tabend
|
|
1200
1393
|
|
|
1201
1394
|
:::tab{label="macOS" icon="🍎"}
|
|
1395
|
+
|
|
1202
1396
|
### Using Homebrew (Recommended)
|
|
1203
1397
|
|
|
1204
1398
|
\`\`\`bash
|
|
@@ -1208,8 +1402,8 @@ brew install myapp
|
|
|
1208
1402
|
### Using DMG Installer
|
|
1209
1403
|
|
|
1210
1404
|
1. **Download** the macOS installer:
|
|
1211
|
-
|
|
1212
|
-
|
|
1405
|
+
- [Download for macOS (Apple Silicon)](https://example.com/download/macos-arm64)
|
|
1406
|
+
- [Download for macOS (Intel)](https://example.com/download/macos-x64)
|
|
1213
1407
|
|
|
1214
1408
|
2. **Open** the .dmg file
|
|
1215
1409
|
|
|
@@ -1233,6 +1427,7 @@ Right-click the app → Open → Confirm to bypass Gatekeeper
|
|
|
1233
1427
|
:::tabend
|
|
1234
1428
|
|
|
1235
1429
|
:::tab{label="Linux" icon="🐧"}
|
|
1430
|
+
|
|
1236
1431
|
### Using Package Manager
|
|
1237
1432
|
|
|
1238
1433
|
**Ubuntu/Debian:**
|
|
@@ -1283,6 +1478,7 @@ which myapp
|
|
|
1283
1478
|
:::tabend
|
|
1284
1479
|
|
|
1285
1480
|
:::tab{label="Docker" icon="🐳"}
|
|
1481
|
+
|
|
1286
1482
|
### Using Docker
|
|
1287
1483
|
|
|
1288
1484
|
Pull the official image:
|
|
@@ -1293,10 +1489,10 @@ docker pull mycompany/myapp:latest
|
|
|
1293
1489
|
Run the container:
|
|
1294
1490
|
\`\`\`bash
|
|
1295
1491
|
docker run -d \\
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1492
|
+
--name myapp \\
|
|
1493
|
+
-p 8080:8080 \\
|
|
1494
|
+
-v \$(pwd)/data:/app/data \\
|
|
1495
|
+
mycompany/myapp:latest
|
|
1300
1496
|
\`\`\`
|
|
1301
1497
|
|
|
1302
1498
|
### Using Docker Compose
|
|
@@ -1305,15 +1501,15 @@ Create \`docker-compose.yml\`:
|
|
|
1305
1501
|
\`\`\`yaml
|
|
1306
1502
|
version: '3.8'
|
|
1307
1503
|
services:
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1504
|
+
myapp:
|
|
1505
|
+
image: mycompany/myapp:latest
|
|
1506
|
+
ports:
|
|
1507
|
+
- "8080:8080"
|
|
1508
|
+
volumes:
|
|
1509
|
+
- ./data:/app/data
|
|
1510
|
+
environment:
|
|
1511
|
+
- APP_ENV=production
|
|
1512
|
+
- LOG_LEVEL=info
|
|
1317
1513
|
\`\`\`
|
|
1318
1514
|
|
|
1319
1515
|
Start services:
|
|
@@ -1327,8 +1523,8 @@ docker-compose up -d
|
|
|
1327
1523
|
- \`2.0.5\` - Specific version
|
|
1328
1524
|
- \`alpine\` - Lightweight Alpine-based image
|
|
1329
1525
|
- \`dev\` - Development build
|
|
1330
|
-
:::tabend
|
|
1331
|
-
::::tabsend
|
|
1526
|
+
:::tabend
|
|
1527
|
+
::::tabsend
|
|
1332
1528
|
|
|
1333
1529
|
## Post-Installation
|
|
1334
1530
|
|
|
@@ -1349,11 +1545,13 @@ myapp config set api-key YOUR_KEY
|
|
|
1349
1545
|
|
|
1350
1546
|
::::tabs
|
|
1351
1547
|
:::tab{label="React" icon="⚛️" active="true"}
|
|
1548
|
+
|
|
1352
1549
|
### Overview
|
|
1353
1550
|
|
|
1354
1551
|
React is a JavaScript library for building user interfaces, focusing on component-based architecture.
|
|
1355
1552
|
|
|
1356
1553
|
**Pros:**
|
|
1554
|
+
|
|
1357
1555
|
- ✅ Large ecosystem and community
|
|
1358
1556
|
- ✅ Virtual DOM for performance
|
|
1359
1557
|
- ✅ Flexible and unopinionated
|
|
@@ -1361,6 +1559,7 @@ React is a JavaScript library for building user interfaces, focusing on componen
|
|
|
1361
1559
|
- ✅ Strong TypeScript support
|
|
1362
1560
|
|
|
1363
1561
|
**Cons:**
|
|
1562
|
+
|
|
1364
1563
|
- ❌ Steep learning curve
|
|
1365
1564
|
- ❌ JSX syntax to learn
|
|
1366
1565
|
- ❌ Need additional libraries for routing, state
|
|
@@ -1371,17 +1570,17 @@ React is a JavaScript library for building user interfaces, focusing on componen
|
|
|
1371
1570
|
import React from 'react';
|
|
1372
1571
|
|
|
1373
1572
|
function App() {
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1573
|
+
const [count, setCount] = React.useState(0);
|
|
1574
|
+
|
|
1575
|
+
return (
|
|
1576
|
+
<div>
|
|
1577
|
+
<h1>Hello, React!</h1>
|
|
1578
|
+
<p>Count: {count}</p>
|
|
1579
|
+
<button onClick={() => setCount(count + 1)}>
|
|
1580
|
+
Increment
|
|
1581
|
+
</button>
|
|
1582
|
+
</div>
|
|
1583
|
+
);
|
|
1385
1584
|
}
|
|
1386
1585
|
|
|
1387
1586
|
export default App;
|
|
@@ -1393,14 +1592,16 @@ export default App;
|
|
|
1393
1592
|
- Need flexibility in architecture
|
|
1394
1593
|
- Large team with React expertise
|
|
1395
1594
|
- Mobile app with React Native
|
|
1396
|
-
:::tabend
|
|
1595
|
+
:::tabend
|
|
1397
1596
|
|
|
1398
1597
|
:::tab{label="Vue" icon="💚"}
|
|
1598
|
+
|
|
1399
1599
|
### Overview
|
|
1400
1600
|
|
|
1401
1601
|
Vue is a progressive framework for building user interfaces with a gentle learning curve.
|
|
1402
1602
|
|
|
1403
1603
|
**Pros:**
|
|
1604
|
+
|
|
1404
1605
|
- ✅ Easy to learn
|
|
1405
1606
|
- ✅ Great documentation
|
|
1406
1607
|
- ✅ Two-way data binding
|
|
@@ -1408,6 +1609,7 @@ Vue is a progressive framework for building user interfaces with a gentle learni
|
|
|
1408
1609
|
- ✅ Template syntax familiar to HTML
|
|
1409
1610
|
|
|
1410
1611
|
**Cons:**
|
|
1612
|
+
|
|
1411
1613
|
- ❌ Smaller ecosystem than React
|
|
1412
1614
|
- ❌ Less corporate backing
|
|
1413
1615
|
- ❌ Fewer job opportunities
|
|
@@ -1438,14 +1640,16 @@ const count = ref(0);
|
|
|
1438
1640
|
- Small to medium projects
|
|
1439
1641
|
- Team with HTML/CSS background
|
|
1440
1642
|
- Gradual migration from legacy code
|
|
1441
|
-
:::tabend
|
|
1643
|
+
:::tabend
|
|
1442
1644
|
|
|
1443
1645
|
:::tab{label="Angular" icon="🅰️"}
|
|
1646
|
+
|
|
1444
1647
|
### Overview
|
|
1445
1648
|
|
|
1446
1649
|
Angular is a full-featured framework with batteries included for enterprise applications.
|
|
1447
1650
|
|
|
1448
1651
|
**Pros:**
|
|
1652
|
+
|
|
1449
1653
|
- ✅ Complete solution (routing, HTTP, forms)
|
|
1450
1654
|
- ✅ TypeScript by default
|
|
1451
1655
|
- ✅ Strong corporate backing (Google)
|
|
@@ -1453,6 +1657,7 @@ Angular is a full-featured framework with batteries included for enterprise appl
|
|
|
1453
1657
|
- ✅ CLI tooling
|
|
1454
1658
|
|
|
1455
1659
|
**Cons:**
|
|
1660
|
+
|
|
1456
1661
|
- ❌ Steeper learning curve
|
|
1457
1662
|
- ❌ Larger bundle size
|
|
1458
1663
|
- ❌ More opinionated
|
|
@@ -1464,23 +1669,23 @@ Angular is a full-featured framework with batteries included for enterprise appl
|
|
|
1464
1669
|
import { Component } from '@angular/core';
|
|
1465
1670
|
|
|
1466
1671
|
@Component({
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1672
|
+
selector: 'app-root',
|
|
1673
|
+
template: \`
|
|
1674
|
+
<div>
|
|
1675
|
+
<h1>Hello, Angular!</h1>
|
|
1676
|
+
<p>Count: {{ count }}</p>
|
|
1677
|
+
<button (click)="increment()">
|
|
1678
|
+
Increment
|
|
1679
|
+
</button>
|
|
1680
|
+
</div>
|
|
1681
|
+
\`
|
|
1477
1682
|
})
|
|
1478
1683
|
export class AppComponent {
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1684
|
+
count = 0;
|
|
1685
|
+
|
|
1686
|
+
increment() {
|
|
1687
|
+
this.count++;
|
|
1688
|
+
}
|
|
1484
1689
|
}
|
|
1485
1690
|
\`\`\`
|
|
1486
1691
|
|
|
@@ -1490,9 +1695,10 @@ export class AppComponent {
|
|
|
1490
1695
|
- Large teams
|
|
1491
1696
|
- Need complete framework
|
|
1492
1697
|
- TypeScript-first development
|
|
1493
|
-
:::tabend
|
|
1698
|
+
:::tabend
|
|
1494
1699
|
|
|
1495
1700
|
:::tab{label="Comparison" icon="📊"}
|
|
1701
|
+
|
|
1496
1702
|
### Side-by-Side Comparison
|
|
1497
1703
|
|
|
1498
1704
|
| Feature | React | Vue | Angular |
|
|
@@ -1530,6 +1736,7 @@ export class AppComponent {
|
|
|
1530
1736
|
## Making Your Choice
|
|
1531
1737
|
|
|
1532
1738
|
Consider your:
|
|
1739
|
+
|
|
1533
1740
|
- Team expertise
|
|
1534
1741
|
- Project requirements
|
|
1535
1742
|
- Timeline
|
|
@@ -1545,27 +1752,35 @@ Consider your:
|
|
|
1545
1752
|
|
|
1546
1753
|
::::tabs
|
|
1547
1754
|
:::tab{label="Development" icon="💻" active="true"}
|
|
1755
|
+
|
|
1548
1756
|
### Development Environment Setup
|
|
1549
1757
|
|
|
1550
1758
|
\`\`\`bash
|
|
1759
|
+
|
|
1551
1760
|
# Install dependencies
|
|
1761
|
+
|
|
1552
1762
|
npm install
|
|
1553
1763
|
|
|
1554
1764
|
# Copy environment file
|
|
1765
|
+
|
|
1555
1766
|
cp .env.example .env.development
|
|
1556
1767
|
|
|
1557
1768
|
# Start dev server
|
|
1769
|
+
|
|
1558
1770
|
npm run dev
|
|
1559
1771
|
\`\`\`
|
|
1560
1772
|
|
|
1561
1773
|
### Configuration (\`.env.development\`)
|
|
1562
1774
|
|
|
1563
1775
|
\`\`\`env
|
|
1776
|
+
|
|
1564
1777
|
# API Configuration
|
|
1778
|
+
|
|
1565
1779
|
API_URL=http://localhost:3000
|
|
1566
1780
|
API_KEY=dev_key_1234567890
|
|
1567
1781
|
|
|
1568
1782
|
# Database
|
|
1783
|
+
|
|
1569
1784
|
DB_HOST=localhost
|
|
1570
1785
|
DB_PORT=5432
|
|
1571
1786
|
DB_NAME=myapp_dev
|
|
@@ -1573,11 +1788,13 @@ DB_USER=dev_user
|
|
|
1573
1788
|
DB_PASSWORD=dev_password
|
|
1574
1789
|
|
|
1575
1790
|
# Features
|
|
1791
|
+
|
|
1576
1792
|
ENABLE_DEBUG=true
|
|
1577
1793
|
ENABLE_HOT_RELOAD=true
|
|
1578
1794
|
ENABLE_SOURCE_MAPS=true
|
|
1579
1795
|
|
|
1580
1796
|
# Logging
|
|
1797
|
+
|
|
1581
1798
|
LOG_LEVEL=debug
|
|
1582
1799
|
LOG_TO_FILE=false
|
|
1583
1800
|
\`\`\`
|
|
@@ -1589,27 +1806,34 @@ LOG_TO_FILE=false
|
|
|
1589
1806
|
- ✅ Verbose logging
|
|
1590
1807
|
- ✅ Mock API responses
|
|
1591
1808
|
- ✅ Local database
|
|
1592
|
-
:::tabend
|
|
1809
|
+
:::tabend
|
|
1593
1810
|
|
|
1594
1811
|
:::tab{label="Staging" icon="🧪"}
|
|
1812
|
+
|
|
1595
1813
|
### Staging Environment Setup
|
|
1596
1814
|
|
|
1597
1815
|
\`\`\`bash
|
|
1816
|
+
|
|
1598
1817
|
# Build for staging
|
|
1818
|
+
|
|
1599
1819
|
npm run build:staging
|
|
1600
1820
|
|
|
1601
1821
|
# Deploy to staging
|
|
1822
|
+
|
|
1602
1823
|
npm run deploy:staging
|
|
1603
1824
|
\`\`\`
|
|
1604
1825
|
|
|
1605
1826
|
### Configuration (\`.env.staging\`)
|
|
1606
1827
|
|
|
1607
1828
|
\`\`\`env
|
|
1829
|
+
|
|
1608
1830
|
# API Configuration
|
|
1831
|
+
|
|
1609
1832
|
API_URL=https://staging-api.example.com
|
|
1610
1833
|
API_KEY=staging_key_secure_token
|
|
1611
1834
|
|
|
1612
1835
|
# Database
|
|
1836
|
+
|
|
1613
1837
|
DB_HOST=staging-db.example.com
|
|
1614
1838
|
DB_PORT=5432
|
|
1615
1839
|
DB_NAME=myapp_staging
|
|
@@ -1617,16 +1841,19 @@ DB_USER=staging_user
|
|
|
1617
1841
|
DB_PASSWORD=staging_secure_password
|
|
1618
1842
|
|
|
1619
1843
|
# Features
|
|
1844
|
+
|
|
1620
1845
|
ENABLE_DEBUG=false
|
|
1621
1846
|
ENABLE_HOT_RELOAD=false
|
|
1622
1847
|
ENABLE_SOURCE_MAPS=true
|
|
1623
1848
|
|
|
1624
1849
|
# Logging
|
|
1850
|
+
|
|
1625
1851
|
LOG_LEVEL=info
|
|
1626
1852
|
LOG_TO_FILE=true
|
|
1627
1853
|
LOG_PATH=/var/log/myapp
|
|
1628
1854
|
|
|
1629
1855
|
# Monitoring
|
|
1856
|
+
|
|
1630
1857
|
SENTRY_DSN=https://...@sentry.io/staging
|
|
1631
1858
|
ANALYTICS_ID=staging-analytics-id
|
|
1632
1859
|
\`\`\`
|
|
@@ -1638,27 +1865,34 @@ ANALYTICS_ID=staging-analytics-id
|
|
|
1638
1865
|
- ✅ Error tracking enabled
|
|
1639
1866
|
- ✅ Performance monitoring
|
|
1640
1867
|
- ✅ QA testing environment
|
|
1641
|
-
:::tabend
|
|
1868
|
+
:::tabend
|
|
1642
1869
|
|
|
1643
1870
|
:::tab{label="Production" icon="🚀"}
|
|
1871
|
+
|
|
1644
1872
|
### Production Environment Setup
|
|
1645
1873
|
|
|
1646
1874
|
\`\`\`bash
|
|
1875
|
+
|
|
1647
1876
|
# Build for production
|
|
1877
|
+
|
|
1648
1878
|
npm run build:production
|
|
1649
1879
|
|
|
1650
1880
|
# Deploy to production
|
|
1881
|
+
|
|
1651
1882
|
npm run deploy:production
|
|
1652
1883
|
\`\`\`
|
|
1653
1884
|
|
|
1654
1885
|
### Configuration (\`.env.production\`)
|
|
1655
1886
|
|
|
1656
1887
|
\`\`\`env
|
|
1888
|
+
|
|
1657
1889
|
# API Configuration
|
|
1890
|
+
|
|
1658
1891
|
API_URL=https://api.example.com
|
|
1659
1892
|
API_KEY=prod_key_super_secure_token
|
|
1660
1893
|
|
|
1661
1894
|
# Database
|
|
1895
|
+
|
|
1662
1896
|
DB_HOST=prod-db.example.com
|
|
1663
1897
|
DB_PORT=5432
|
|
1664
1898
|
DB_NAME=myapp_prod
|
|
@@ -1666,26 +1900,31 @@ DB_USER=prod_user
|
|
|
1666
1900
|
DB_PASSWORD=prod_ultra_secure_password
|
|
1667
1901
|
|
|
1668
1902
|
# Features
|
|
1903
|
+
|
|
1669
1904
|
ENABLE_DEBUG=false
|
|
1670
1905
|
ENABLE_HOT_RELOAD=false
|
|
1671
1906
|
ENABLE_SOURCE_MAPS=false
|
|
1672
1907
|
|
|
1673
1908
|
# Logging
|
|
1909
|
+
|
|
1674
1910
|
LOG_LEVEL=error
|
|
1675
1911
|
LOG_TO_FILE=true
|
|
1676
1912
|
LOG_PATH=/var/log/myapp
|
|
1677
1913
|
LOG_ROTATION=daily
|
|
1678
1914
|
|
|
1679
1915
|
# Monitoring
|
|
1916
|
+
|
|
1680
1917
|
SENTRY_DSN=https://...@sentry.io/production
|
|
1681
1918
|
ANALYTICS_ID=prod-analytics-id
|
|
1682
1919
|
|
|
1683
1920
|
# Performance
|
|
1921
|
+
|
|
1684
1922
|
ENABLE_CACHING=true
|
|
1685
1923
|
CACHE_TTL=3600
|
|
1686
1924
|
CDN_URL=https://cdn.example.com
|
|
1687
1925
|
|
|
1688
1926
|
# Security
|
|
1927
|
+
|
|
1689
1928
|
RATE_LIMIT=100
|
|
1690
1929
|
CORS_ORIGIN=https://example.com
|
|
1691
1930
|
SSL_CERT=/path/to/cert.pem
|
|
@@ -1700,9 +1939,10 @@ SSL_CERT=/path/to/cert.pem
|
|
|
1700
1939
|
- ✅ Monitoring and alerts
|
|
1701
1940
|
- ✅ Automated backups
|
|
1702
1941
|
- ✅ Load balancing
|
|
1703
|
-
:::tabend
|
|
1942
|
+
:::tabend
|
|
1704
1943
|
|
|
1705
1944
|
:::tab{label="Docker" icon="🐳"}
|
|
1945
|
+
|
|
1706
1946
|
### Docker Configuration
|
|
1707
1947
|
|
|
1708
1948
|
Create \`docker-compose.yml\` per environment:
|
|
@@ -1711,44 +1951,46 @@ Create \`docker-compose.yml\` per environment:
|
|
|
1711
1951
|
\`\`\`yaml
|
|
1712
1952
|
version: '3.8'
|
|
1713
1953
|
services:
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1954
|
+
app:
|
|
1955
|
+
build:
|
|
1956
|
+
context: .
|
|
1957
|
+
target: development
|
|
1958
|
+
ports:
|
|
1959
|
+
- "3000:3000"
|
|
1960
|
+
volumes:
|
|
1961
|
+
- .:/app
|
|
1962
|
+
- /app/node_modules
|
|
1963
|
+
environment:
|
|
1964
|
+
- NODE_ENV=development
|
|
1965
|
+
command: npm run dev
|
|
1726
1966
|
\`\`\`
|
|
1727
1967
|
|
|
1728
1968
|
**Production:**
|
|
1729
1969
|
\`\`\`yaml
|
|
1730
1970
|
version: '3.8'
|
|
1731
1971
|
services:
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1972
|
+
app:
|
|
1973
|
+
build:
|
|
1974
|
+
context: .
|
|
1975
|
+
target: production
|
|
1976
|
+
ports:
|
|
1977
|
+
- "3000:3000"
|
|
1978
|
+
environment:
|
|
1979
|
+
- NODE_ENV=production
|
|
1980
|
+
restart: always
|
|
1981
|
+
healthcheck:
|
|
1982
|
+
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
|
|
1743
1983
|
interval: 30s
|
|
1744
|
-
|
|
1745
|
-
|
|
1984
|
+
timeout: 10s
|
|
1985
|
+
retries: 3
|
|
1746
1986
|
\`\`\`
|
|
1747
1987
|
|
|
1748
1988
|
### Multi-stage Dockerfile
|
|
1749
1989
|
|
|
1750
1990
|
\`\`\`dockerfile
|
|
1991
|
+
|
|
1751
1992
|
# Development stage
|
|
1993
|
+
|
|
1752
1994
|
FROM node:18-alpine AS development
|
|
1753
1995
|
WORKDIR /app
|
|
1754
1996
|
COPY package*.json ./
|
|
@@ -1757,6 +1999,7 @@ COPY . .
|
|
|
1757
1999
|
CMD ["npm", "run", "dev"]
|
|
1758
2000
|
|
|
1759
2001
|
# Production stage
|
|
2002
|
+
|
|
1760
2003
|
FROM node:18-alpine AS production
|
|
1761
2004
|
WORKDIR /app
|
|
1762
2005
|
COPY package*.json ./
|
|
@@ -1786,19 +2029,22 @@ CMD ["npm", "start"]
|
|
|
1786
2029
|
**Solutions**:
|
|
1787
2030
|
|
|
1788
2031
|
1. **Import CSS:**
|
|
2032
|
+
|
|
1789
2033
|
```javascript
|
|
1790
2034
|
import '@fsegurai/marked-extended-tabs/styles/tabs.css';
|
|
1791
2035
|
```
|
|
1792
2036
|
|
|
1793
2037
|
2. **Check for conflicting styles:**
|
|
2038
|
+
|
|
1794
2039
|
```css
|
|
1795
2040
|
/* Make sure nothing is overriding tab display */
|
|
1796
2041
|
.marked-extended-tabs-content-pane {
|
|
1797
|
-
|
|
2042
|
+
display: none; /* Should be hidden by default */
|
|
1798
2043
|
}
|
|
1799
2044
|
```
|
|
1800
2045
|
|
|
1801
2046
|
3. **Verify tab structure:**
|
|
2047
|
+
|
|
1802
2048
|
```markdown
|
|
1803
2049
|
<!-- Ensure proper nesting -->
|
|
1804
2050
|
::::tabs
|
|
@@ -1815,6 +2061,7 @@ Content
|
|
|
1815
2061
|
**Solutions**:
|
|
1816
2062
|
|
|
1817
2063
|
1. **Use valid Unicode/emoji:**
|
|
2064
|
+
|
|
1818
2065
|
```markdown
|
|
1819
2066
|
<!-- Correct -->
|
|
1820
2067
|
:::tab{label="Code" icon="📜"}
|
|
@@ -1825,10 +2072,11 @@ Content
|
|
|
1825
2072
|
```
|
|
1826
2073
|
|
|
1827
2074
|
2. **Check font support:**
|
|
2075
|
+
|
|
1828
2076
|
```css
|
|
1829
2077
|
/* Ensure emoji font is loaded */
|
|
1830
2078
|
.marked-extended-tabs-icon {
|
|
1831
|
-
|
|
2079
|
+
font-family: "Apple Color Emoji", "Segoe UI Emoji", sans-serif;
|
|
1832
2080
|
}
|
|
1833
2081
|
```
|
|
1834
2082
|
|
|
@@ -1839,13 +2087,15 @@ Content
|
|
|
1839
2087
|
**Solutions**:
|
|
1840
2088
|
|
|
1841
2089
|
1. **Set animation type:**
|
|
2090
|
+
|
|
1842
2091
|
```javascript
|
|
1843
2092
|
marked.use(markedExtendedTabs({
|
|
1844
|
-
|
|
2093
|
+
animation: 'fade' // or 'slide'
|
|
1845
2094
|
}));
|
|
1846
2095
|
```
|
|
1847
2096
|
|
|
1848
2097
|
2. **Import theme CSS:**
|
|
2098
|
+
|
|
1849
2099
|
```javascript
|
|
1850
2100
|
import '@fsegurai/marked-extended-tabs/styles/tabs-theme.css';
|
|
1851
2101
|
```
|
|
@@ -1855,14 +2105,16 @@ import '@fsegurai/marked-extended-tabs/styles/tabs-theme.css';
|
|
|
1855
2105
|
**Problem**: No tab is active by default.
|
|
1856
2106
|
|
|
1857
2107
|
**Solution**:
|
|
2108
|
+
|
|
1858
2109
|
```javascript
|
|
1859
2110
|
// Enable auto-activation
|
|
1860
2111
|
marked.use(markedExtendedTabs({
|
|
1861
|
-
|
|
2112
|
+
autoActivate: true // Default is true
|
|
1862
2113
|
}));
|
|
1863
2114
|
```
|
|
1864
2115
|
|
|
1865
2116
|
Or mark a tab as active:
|
|
2117
|
+
|
|
1866
2118
|
```markdown
|
|
1867
2119
|
:::tab{label="Tab 1" active="true"}
|
|
1868
2120
|
```
|
|
@@ -1873,37 +2125,37 @@ Or mark a tab as active:
|
|
|
1873
2125
|
|
|
1874
2126
|
```tsx
|
|
1875
2127
|
// TabsContent.tsx
|
|
1876
|
-
import {
|
|
2128
|
+
import {marked} from 'marked';
|
|
1877
2129
|
import markedExtendedTabs from '@fsegurai/marked-extended-tabs';
|
|
1878
2130
|
import '@fsegurai/marked-extended-tabs/styles/tabs.css';
|
|
1879
2131
|
import '@fsegurai/marked-extended-tabs/styles/tabs-theme.css';
|
|
1880
|
-
import {
|
|
2132
|
+
import {useEffect, useState} from 'react';
|
|
1881
2133
|
|
|
1882
2134
|
marked.use(markedExtendedTabs({
|
|
1883
|
-
|
|
1884
|
-
|
|
2135
|
+
animation: 'fade',
|
|
2136
|
+
autoActivate: true
|
|
1885
2137
|
}));
|
|
1886
2138
|
|
|
1887
2139
|
interface Props {
|
|
1888
|
-
|
|
1889
|
-
|
|
2140
|
+
content: string;
|
|
2141
|
+
animation?: 'fade' | 'slide' | 'none';
|
|
1890
2142
|
}
|
|
1891
2143
|
|
|
1892
|
-
export function TabsContent({
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
2144
|
+
export function TabsContent({content, animation = 'fade'}: Props) {
|
|
2145
|
+
const [html, setHtml] = useState('');
|
|
2146
|
+
|
|
2147
|
+
useEffect(() => {
|
|
2148
|
+
marked.use(markedExtendedTabs({animation}));
|
|
2149
|
+
const parsed = marked.parse(content);
|
|
2150
|
+
setHtml(parsed);
|
|
2151
|
+
}, [content, animation]);
|
|
2152
|
+
|
|
2153
|
+
return (
|
|
2154
|
+
<div
|
|
2155
|
+
className="tabs-wrapper"
|
|
2156
|
+
dangerouslySetInnerHTML={{__html: html}}
|
|
2157
|
+
/>
|
|
2158
|
+
);
|
|
1907
2159
|
}
|
|
1908
2160
|
|
|
1909
2161
|
// Usage:
|
|
@@ -1948,18 +2200,18 @@ const html = computed(() => {
|
|
|
1948
2200
|
|
|
1949
2201
|
```typescript
|
|
1950
2202
|
// tabs-content.component.ts
|
|
1951
|
-
import {
|
|
1952
|
-
import {
|
|
1953
|
-
import {
|
|
2203
|
+
import {Component, Input, OnChanges, SimpleChanges} from '@angular/core';
|
|
2204
|
+
import {DomSanitizer, SafeHtml} from '@angular/platform-browser';
|
|
2205
|
+
import {marked} from 'marked';
|
|
1954
2206
|
import markedExtendedTabs from '@fsegurai/marked-extended-tabs';
|
|
1955
2207
|
|
|
1956
2208
|
marked.use(markedExtendedTabs({
|
|
1957
|
-
|
|
2209
|
+
animation: 'fade'
|
|
1958
2210
|
}));
|
|
1959
2211
|
|
|
1960
2212
|
@Component({
|
|
1961
|
-
|
|
1962
|
-
|
|
2213
|
+
selector: 'app-tabs-content',
|
|
2214
|
+
template: \`<div [innerHTML]="parsedContent"></div>\`,
|
|
1963
2215
|
styleUrls: [
|
|
1964
2216
|
'../node_modules/@fsegurai/marked-extended-tabs/styles/tabs.css',
|
|
1965
2217
|
'../node_modules/@fsegurai/marked-extended-tabs/styles/tabs-theme.css'
|
|
@@ -1991,13 +2243,14 @@ export class TabsContentComponent implements OnChanges {
|
|
|
1991
2243
|
```javascript
|
|
1992
2244
|
// Disable animations for better performance
|
|
1993
2245
|
marked.use(markedExtendedTabs({
|
|
1994
|
-
|
|
2246
|
+
animation: 'none'
|
|
1995
2247
|
}));
|
|
1996
2248
|
```
|
|
1997
2249
|
|
|
1998
2250
|
### Contributing
|
|
1999
2251
|
|
|
2000
|
-
Found a bug or have a feature request? Please open an issue
|
|
2252
|
+
Found a bug or have a feature request? Please open an issue
|
|
2253
|
+
on [GitHub](https://github.com/fsegurai/marked-extensions/issues).
|
|
2001
2254
|
|
|
2002
2255
|
### Related Resources
|
|
2003
2256
|
|