@@ -17,49 +17,122 @@ namespace Sisk.JsonRPC.Documentation;
17
17
/// </summary>
18
18
public class JsonRpcHtmlExport : IJsonRpcDocumentationExporter {
19
19
20
+ /// <summary>
21
+ /// Gets or sets an boolean indicating if an summary should be exported in the HTML.
22
+ /// </summary>
23
+ public bool ExportSummary { get ; set ; } = true ;
24
+
25
+ /// <summary>
26
+ /// Gets or sets an optional object to append to the header of the
27
+ /// exported HTML.
28
+ /// </summary>
29
+ public object ? Header { get ; set ; }
30
+
20
31
/// <summary>
21
32
/// Gets or sets the CSS styles used in the HTML export.
22
33
/// </summary>
23
34
public string ? Style { get ; set ; } = """
24
- * { box-sizing: border-box; }
25
- html, body { margin: 0; background-color: #f4f9ff; font-family: Arial }
35
+ * { box-sizing: border-box; }
26
36
p, li { line-height: 1.6 }
27
- li + li { margin-top: 1em; }
28
- .monospaced { font-family: monospace; }
37
+
38
+ html, body {
39
+ margin: 0;
40
+ background-color: white;
41
+ font-size: 16px;
42
+ font-family: -apple-system,BlinkMacSystemFont,"Segoe UI","Noto Sans",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"
43
+ }
29
44
30
45
main {
31
46
background: white;
32
- max-width: 800px;
47
+ max-width: 900px;
48
+ width: 90vw;
33
49
margin: 30px auto 0 auto;
34
50
padding: 20px 40px;
35
51
border-radius: 14px;
36
- border: 1px solid #cbd3da;
52
+ border: 1px solid #d1d9e0;
53
+ }
54
+
55
+ h1, h2, h3 {
56
+ margin-top: 2.5rem;
57
+ margin-bottom: 1rem;
58
+ font-weight: 600;
59
+ line-height: 1.25;
60
+ }
61
+
62
+ h1, h2 {
63
+ padding-bottom: .3em;
64
+ border-bottom: 2px solid #d1d9e0b3;
37
65
}
38
66
39
- h1 {
40
- padding-bottom: .25em;
41
- border-bottom: 2px solid #aacae7;
42
- font-size: 1.8em;
43
- font-weight: medium;
67
+ h1 {
68
+ font-size: 2em;
44
69
}
45
70
46
71
h2 {
47
- padding: 1em 0 .25em 0;
48
- border-bottom: 1px solid #aacae7;
49
- font-size: 1.4em;
50
- font-weight: normal;
72
+ font-size: 1.5em;
73
+ }
74
+
75
+ h1 a,
76
+ h2 a {
77
+ opacity: 0;
78
+ color: #000;
79
+ text-decoration: none !important;
80
+ user-select: none;
81
+ }
82
+
83
+ h1:hover a,
84
+ h2:hover a {
85
+ opacity: 0.3;
86
+ }
87
+
88
+ h1 a:hover,
89
+ h2 a:hover {
90
+ opacity: 1;
91
+ }
92
+
93
+ .paramlist {
94
+ padding-left: 0;
95
+ list-style-type: none;
96
+ }
97
+
98
+ .paramtitle {
99
+ display: flex;
100
+ gap: 15px;
101
+ margin-bottom: 5px;
102
+ }
103
+
104
+ .paramtitle b {
105
+ padding: .2em .4em;
106
+ margin: 0;
107
+ font-family: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;
108
+ font-size: 14px;
109
+ font-weight: 600;
110
+ white-space: break-spaces;
111
+ background-color: #eff1f3;
112
+ border-radius: 6px;
113
+ }
114
+
115
+ .paramlist li + li {
116
+ margin-top: 1em;
117
+ border-top: 1px solid #d8dee4;
118
+ padding-top: 1.25em;
119
+ }
120
+
121
+ .muted {
122
+ color: #656d76;
123
+ }
124
+
125
+ .at {
126
+ color: #9a6700;
51
127
}
52
128
53
- .details > span {
54
- display: inline-block;
55
- background-color: #e3e8ed;
56
- padding: 1px 6px;
57
- font-size: 0.9em;
58
- font-weight: bold;
129
+ a {
130
+ color: #0969da;
131
+ text-decoration: none;
59
132
}
60
133
61
- .details > span:not(:first-child) {
62
- margin-left: 5px;
134
+ a:hover {
135
+ text-decoration: underline;
63
136
}
64
137
""" ;
65
138
@@ -70,6 +143,10 @@ public class JsonRpcHtmlExport : IJsonRpcDocumentationExporter {
70
143
protected string EncodeDocumentationHtml ( JsonRpcDocumentation documentation ) {
71
144
HtmlElement html = new HtmlElement ( "html" ) ;
72
145
146
+ string GetMethodIdName ( string name ) {
147
+ return new string ( name . Where ( char . IsLetterOrDigit ) . ToArray ( ) ) ;
148
+ }
149
+
73
150
html += new HtmlElement ( "head" , head => {
74
151
head += new HtmlElement ( "title" , "JSON-RPC 2.0 Application Documentation" ) ;
75
152
if ( this . Style != null ) {
@@ -83,24 +160,62 @@ protected string EncodeDocumentationHtml ( JsonRpcDocumentation documentation )
83
160
. GroupBy ( g => g . Category ) ;
84
161
85
162
body += new HtmlElement ( "main" , main => {
163
+
164
+ if ( this . Header is not null ) {
165
+ main += this . Header ;
166
+ }
167
+
168
+ if ( this . ExportSummary ) {
169
+ main += new HtmlElement ( "h1" , "Summary" ) ;
170
+
171
+ foreach ( var category in methodsGrouped ) {
172
+ main += new HtmlElement ( "p" , ( category . Key ?? "Methods" ) + ":" ) ;
173
+ main += new HtmlElement ( "ul" , ul => {
174
+ foreach ( var method in documentation . Methods ) {
175
+ ul += new HtmlElement ( "li" , li => {
176
+ li += new HtmlElement ( "a" , method . MethodName )
177
+ . WithAttribute ( "href" , $ "#{ GetMethodIdName ( method . MethodName ) } " ) ;
178
+ if ( ! string . IsNullOrEmpty ( method . Description ) ) {
179
+ li += new HtmlElement ( "span" , $ " - { method . Description } " )
180
+ . WithClass ( "muted" ) ;
181
+ }
182
+ } ) ;
183
+ }
184
+ } ) ;
185
+ }
186
+ }
187
+
86
188
foreach ( var category in methodsGrouped ) {
87
189
main += new HtmlElement ( "h1" , category . Key ?? "Methods" ) ;
88
190
foreach ( var method in category ) {
89
191
main += new HtmlElement ( "section" , section => {
90
- section += new HtmlElement ( "h2" , method . MethodName ) ;
192
+
193
+ section . Id = GetMethodIdName ( method . MethodName ) ;
194
+
195
+ section += new HtmlElement ( "h2" , h2 => {
196
+ h2 += method . MethodName ;
197
+ h2 += new HtmlElement ( "a" , "🔗" )
198
+ . WithAttribute ( "href" , $ "#{ section . Id } " ) ;
199
+ } ) ;
91
200
section += new HtmlElement ( "p" , method . Description ?? "" ) ;
92
201
section += new HtmlElement ( "p" , $ "Returns: { method . ReturnType . FullName } " ) ;
93
202
section += new HtmlElement ( "ul" , ulParams => {
203
+ ulParams . ClassList . Add ( "paramlist" ) ;
94
204
foreach ( var param in method . Parameters ) {
95
205
ulParams += new HtmlElement ( "li" , li => {
96
- li += new HtmlElement ( "b" , param . ParameterName )
97
- . WithClass ( "monospaced" ) ;
98
- li += new HtmlElement ( "div" , param . Description ?? "(no description)" ) ;
99
- li += new HtmlElement ( "div" , paramDetails => {
100
- paramDetails . WithClass ( "details" ) ;
101
- paramDetails += new HtmlElement ( "span" , param . IsOptional ? "Optional" : "Required" ) ;
102
- paramDetails += new HtmlElement ( "span" , param . ParameterType . FullName ) ;
206
+ li += new HtmlElement ( "div" , div => {
207
+ div . ClassList . Add ( "paramtitle" ) ;
208
+
209
+ div += new HtmlElement ( "b" , param . ParameterName ) ;
210
+
211
+ div += new HtmlElement ( "span" , param . ParameterType . FullName )
212
+ . WithClass ( "muted" ) ;
213
+
214
+ if ( ! param . IsOptional )
215
+ div += new HtmlElement ( "span" , "Required" ) . WithClass ( "at" ) ;
216
+
103
217
} ) ;
218
+ li += new HtmlElement ( "div" , param . Description ?? "" ) ;
104
219
} ) ;
105
220
}
106
221
} ) ;
0 commit comments