quackage 1.2.2 → 1.2.4
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/docs/_brand.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Hash": "quackage",
|
|
3
|
+
"Name": "Quackage",
|
|
4
|
+
"Tagline": "Standardized build tool for browser bundles, transpilation, testing, and packaging via Gulp and Browserify",
|
|
5
|
+
"Palette": "mix",
|
|
6
|
+
"Icon": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 96 96\" width=\"96\" height=\"96\">\n\t\t<defs>\n\t\t\t<clipPath id=\"frame-quackage-filled-light\">\n\t\t\t\t<path d=\"M 2 48\n\t\t\tA 46 46 0 1 0 94 48\n\t\t\tA 46 46 0 1 0 2 48 Z\"/>\n\t\t\t</clipPath>\n\t\t</defs>\n\t\t<path d=\"M 2 48\n\t\t\tA 46 46 0 1 0 94 48\n\t\t\tA 46 46 0 1 0 2 48 Z\" fill=\"#1c93d7\"/>\n\t\t<g clip-path=\"url(#frame-quackage-filled-light)\"><rect x=\"20\" y=\"20\" width=\"56\" height=\"56\" rx=\"8\" fill=\"rgba(255,255,255,0.18)\"/>\n\t\t\t\t\t<path d=\"M 48 30 L 70 48 L 48 66 L 26 48 Z\" fill=\"#e76142\"/></g>\n\t\t<text x=\"48\" y=\"50\" text-anchor=\"middle\" dominant-baseline=\"central\"\n\t\t\tfont-family=\"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif\"\n\t\t\tfont-size=\"56\" font-weight=\"700\"\n\t\t\tfill=\"#ffffff\" letter-spacing=\"-1\">Q</text>\n\t</svg>",
|
|
7
|
+
"IconType": "svg",
|
|
8
|
+
"Favicon": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 96 96\" width=\"96\" height=\"96\">\n\t\t<defs>\n\t\t\t<clipPath id=\"fav-quackage-light\">\n\t\t\t\t<path d=\"M 2 48\n\t\t\tA 46 46 0 1 0 94 48\n\t\t\tA 46 46 0 1 0 2 48 Z\"/>\n\t\t\t</clipPath>\n\t\t</defs>\n\t\t<path d=\"M 2 48\n\t\t\tA 46 46 0 1 0 94 48\n\t\t\tA 46 46 0 1 0 2 48 Z\" fill=\"#1c93d7\"/>\n\t\t<g clip-path=\"url(#fav-quackage-light)\"><rect x=\"16\" y=\"16\" width=\"64\" height=\"64\" rx=\"10\" fill=\"rgba(255,255,255,0.22)\"/></g>\n\t\t<text x=\"48\" y=\"50\" text-anchor=\"middle\" dominant-baseline=\"central\"\n\t\t\tfont-family=\"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif\"\n\t\t\tfont-size=\"60\" font-weight=\"800\"\n\t\t\tfill=\"#ffffff\" letter-spacing=\"-1\">Q</text>\n\t</svg>",
|
|
9
|
+
"FaviconDark": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 96 96\" width=\"96\" height=\"96\">\n\t\t<defs>\n\t\t\t<clipPath id=\"fav-quackage-dark\">\n\t\t\t\t<path d=\"M 2 48\n\t\t\tA 46 46 0 1 0 94 48\n\t\t\tA 46 46 0 1 0 2 48 Z\"/>\n\t\t\t</clipPath>\n\t\t</defs>\n\t\t<path d=\"M 2 48\n\t\t\tA 46 46 0 1 0 94 48\n\t\t\tA 46 46 0 1 0 2 48 Z\" fill=\"#69b8e6\"/>\n\t\t<g clip-path=\"url(#fav-quackage-dark)\"><rect x=\"16\" y=\"16\" width=\"64\" height=\"64\" rx=\"10\" fill=\"rgba(255,255,255,0.22)\"/></g>\n\t\t<text x=\"48\" y=\"50\" text-anchor=\"middle\" dominant-baseline=\"central\"\n\t\t\tfont-family=\"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif\"\n\t\t\tfont-size=\"60\" font-weight=\"800\"\n\t\t\tfill=\"#101418\" letter-spacing=\"-1\">Q</text>\n\t</svg>",
|
|
10
|
+
"Colors": {
|
|
11
|
+
"Primary": "#1c93d7",
|
|
12
|
+
"Secondary": "#e76142",
|
|
13
|
+
"PrimaryLight": "#1c93d7",
|
|
14
|
+
"PrimaryDark": "#69b8e6",
|
|
15
|
+
"SecondaryLight": "#e76142",
|
|
16
|
+
"SecondaryDark": "#eea897"
|
|
17
|
+
}
|
|
18
|
+
}
|
package/docs/index.html
CHANGED
|
@@ -8,8 +8,6 @@
|
|
|
8
8
|
|
|
9
9
|
<title>Quackage v1.1.0 Documentation</title>
|
|
10
10
|
|
|
11
|
-
<!-- Application Stylesheet -->
|
|
12
|
-
<link href="css/docuserve.css" rel="stylesheet">
|
|
13
11
|
<!-- KaTeX stylesheet for LaTeX equation rendering -->
|
|
14
12
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.css">
|
|
15
13
|
<!-- PICT Dynamic View CSS Container -->
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "quackage",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.4",
|
|
4
4
|
"description": "Building. Testing. Quacking. Reloading.",
|
|
5
5
|
"main": "source/Quackage-CLIProgram.js",
|
|
6
6
|
"scripts": {
|
|
@@ -60,14 +60,14 @@
|
|
|
60
60
|
"gulp-env": "^0.4.0",
|
|
61
61
|
"gulp-sourcemaps": "^3.0.0",
|
|
62
62
|
"gulp-terser": "^2.1.0",
|
|
63
|
-
"indoctrinate": "^1.0.
|
|
63
|
+
"indoctrinate": "^1.0.12",
|
|
64
64
|
"jsdoc": "^4.0.5",
|
|
65
65
|
"mocha": "10.4.0",
|
|
66
66
|
"npm-check-updates": "^18.0.1",
|
|
67
67
|
"nyc": "^15.1.0",
|
|
68
|
-
"pict-provider-theme": "^
|
|
68
|
+
"pict-provider-theme": "^1.0.1",
|
|
69
69
|
"pict-service-commandlineutility": "^1.0.19",
|
|
70
|
-
"retold-harness": "^1.1.
|
|
70
|
+
"retold-harness": "^1.1.12",
|
|
71
71
|
"vinyl-buffer": "^1.0.1",
|
|
72
72
|
"vinyl-source-stream": "^2.0.0"
|
|
73
73
|
},
|
|
@@ -77,6 +77,6 @@
|
|
|
77
77
|
}
|
|
78
78
|
},
|
|
79
79
|
"devDependencies": {
|
|
80
|
-
"pict-docuserve": "^
|
|
80
|
+
"pict-docuserve": "^1.3.2"
|
|
81
81
|
}
|
|
82
82
|
}
|
|
@@ -18,6 +18,7 @@ class QuackageCommandPrepareDocs extends libCommandLineCommand
|
|
|
18
18
|
this.options.CommandOptions.push({ Name: '-b, --branch [branch]', Description: 'Git branch for GitHub raw URLs (defaults to master).', Default: 'master' });
|
|
19
19
|
this.options.CommandOptions.push({ Name: '-g, --github_org [github_org]', Description: 'GitHub organization for raw URLs (defaults to stevenvelozo).', Default: 'stevenvelozo' });
|
|
20
20
|
this.options.CommandOptions.push({ Name: '-x, --excluded_modules [excluded_modules]', Description: 'Comma-separated list of module names to exclude from the catalog and keyword index. Merged with any ExcludedModules list in indoctrinate\'s loaded config file (e.g. .indoctrinate.config.json).', Default: '' });
|
|
21
|
+
this.options.CommandOptions.push({ Name: '--docs_mode [docs_mode]', Description: 'Documentation scan mode: "module" (one module\'s docs/) or "ecosystem" (a folder of <group>/<module> repos). Auto-detected when omitted — "module" when the scan root has a package.json, else "ecosystem".', Default: '' });
|
|
21
22
|
|
|
22
23
|
this.options.Aliases.push('docs');
|
|
23
24
|
this.options.Aliases.push('prep-docs');
|
|
@@ -77,13 +78,54 @@ class QuackageCommandPrepareDocs extends libCommandLineCommand
|
|
|
77
78
|
tmpExtraScanArgs = ['-e', tmpDocsContentRoot];
|
|
78
79
|
}
|
|
79
80
|
|
|
81
|
+
// Documentation mode: "module" (a single module's docs/) vs
|
|
82
|
+
// "ecosystem" (a folder of <group>/<module> repos). Auto-detected
|
|
83
|
+
// from whether the scan root is itself a package (has package.json);
|
|
84
|
+
// the --docs_mode option overrides.
|
|
85
|
+
let tmpDocsMode = (this.CommandOptions.docs_mode === 'module' || this.CommandOptions.docs_mode === 'ecosystem')
|
|
86
|
+
? this.CommandOptions.docs_mode
|
|
87
|
+
: (libFS.existsSync(libPath.join(tmpResolvedDirectoryRoot, 'package.json')) ? 'module' : 'ecosystem');
|
|
88
|
+
this.log.info(`Documentation mode: [${tmpDocsMode}] (scan root [${tmpResolvedDirectoryRoot}]).`);
|
|
89
|
+
|
|
90
|
+
// In single-module mode the catalog + keyword index describe one
|
|
91
|
+
// module — pass --single_module to indoctrinate. The docs folder is
|
|
92
|
+
// already inside the scanned module root, so the -e extra scan is
|
|
93
|
+
// redundant there (and its content would lack the docs/ path segment).
|
|
94
|
+
let tmpSingleModuleArgs = [];
|
|
95
|
+
if (tmpDocsMode === 'module')
|
|
96
|
+
{
|
|
97
|
+
tmpSingleModuleArgs = ['-s'];
|
|
98
|
+
tmpExtraScanArgs = [];
|
|
99
|
+
}
|
|
100
|
+
|
|
80
101
|
let tmpAnticipate = this.fable.newAnticipate();
|
|
81
102
|
|
|
82
|
-
// Step 1:
|
|
103
|
+
// Step 1: Build and stage flagged example applications into the docs
|
|
104
|
+
// folder. Runs first so any generated example index / quick-links
|
|
105
|
+
// markdown is on disk before the keyword index scans it into search.
|
|
106
|
+
// This is a clean no-op for modules with no flagged example
|
|
107
|
+
// applications, so it is safe to run everywhere prepare-docs runs.
|
|
108
|
+
tmpAnticipate.anticipate(
|
|
109
|
+
function (fNext)
|
|
110
|
+
{
|
|
111
|
+
this.log.info(`###############################[ STEP 1: STAGE EXAMPLE APPLICATIONS ]###############################`);
|
|
112
|
+
this.fable.QuackageProcess.execute(
|
|
113
|
+
tmpDocuserveLocation,
|
|
114
|
+
[
|
|
115
|
+
'stage-examples',
|
|
116
|
+
tmpDocsFolder,
|
|
117
|
+
'-m', tmpDirectoryRoot
|
|
118
|
+
],
|
|
119
|
+
{ cwd: this.fable.AppData.CWD },
|
|
120
|
+
fNext
|
|
121
|
+
);
|
|
122
|
+
}.bind(this));
|
|
123
|
+
|
|
124
|
+
// Step 2: Generate the documentation catalog
|
|
83
125
|
tmpAnticipate.anticipate(
|
|
84
126
|
function (fNext)
|
|
85
127
|
{
|
|
86
|
-
this.log.info(`###############################[ STEP
|
|
128
|
+
this.log.info(`###############################[ STEP 2: INDOCTRINATE CATALOG ]###############################`);
|
|
87
129
|
this.fable.QuackageProcess.execute(
|
|
88
130
|
tmpIndoctrinateLocation,
|
|
89
131
|
[
|
|
@@ -92,34 +134,34 @@ class QuackageCommandPrepareDocs extends libCommandLineCommand
|
|
|
92
134
|
'-o', tmpCatalogFile,
|
|
93
135
|
'-b', tmpBranch,
|
|
94
136
|
'-g', tmpGitHubOrg
|
|
95
|
-
].concat(tmpExcludedModulesArgs),
|
|
137
|
+
].concat(tmpSingleModuleArgs).concat(tmpExcludedModulesArgs),
|
|
96
138
|
{ cwd: this.fable.AppData.CWD },
|
|
97
139
|
fNext
|
|
98
140
|
);
|
|
99
141
|
}.bind(this));
|
|
100
142
|
|
|
101
|
-
// Step
|
|
143
|
+
// Step 3: Generate the keyword search index
|
|
102
144
|
tmpAnticipate.anticipate(
|
|
103
145
|
function (fNext)
|
|
104
146
|
{
|
|
105
|
-
this.log.info(`###############################[ STEP
|
|
147
|
+
this.log.info(`###############################[ STEP 3: KEYWORD INDEX ]###############################`);
|
|
106
148
|
this.fable.QuackageProcess.execute(
|
|
107
149
|
tmpIndoctrinateLocation,
|
|
108
150
|
[
|
|
109
151
|
'generate_keyword_index',
|
|
110
152
|
'-d', tmpDirectoryRoot,
|
|
111
153
|
'-o', tmpKeywordIndexFile
|
|
112
|
-
].concat(tmpExtraScanArgs).concat(tmpExcludedModulesArgs),
|
|
154
|
+
].concat(tmpSingleModuleArgs).concat(tmpExtraScanArgs).concat(tmpExcludedModulesArgs),
|
|
113
155
|
{ cwd: this.fable.AppData.CWD },
|
|
114
156
|
fNext
|
|
115
157
|
);
|
|
116
158
|
}.bind(this));
|
|
117
159
|
|
|
118
|
-
// Step
|
|
160
|
+
// Step 4: Write _version.json version placard sidecar
|
|
119
161
|
tmpAnticipate.anticipate(
|
|
120
162
|
function (fNext)
|
|
121
163
|
{
|
|
122
|
-
this.log.info(`###############################[ STEP
|
|
164
|
+
this.log.info(`###############################[ STEP 4: VERSION PLACARD ]###############################`);
|
|
123
165
|
try
|
|
124
166
|
{
|
|
125
167
|
let tmpPackageJsonPath = libPath.join(tmpDirectoryRoot, 'package.json');
|
|
@@ -163,11 +205,11 @@ class QuackageCommandPrepareDocs extends libCommandLineCommand
|
|
|
163
205
|
return fNext();
|
|
164
206
|
}.bind(this));
|
|
165
207
|
|
|
166
|
-
// Step
|
|
208
|
+
// Step 5: Inject pict-docuserve assets
|
|
167
209
|
tmpAnticipate.anticipate(
|
|
168
210
|
function (fNext)
|
|
169
211
|
{
|
|
170
|
-
this.log.info(`###############################[ STEP
|
|
212
|
+
this.log.info(`###############################[ STEP 5: DOCUSERVE INJECT ]###############################`);
|
|
171
213
|
this.fable.QuackageProcess.execute(
|
|
172
214
|
tmpDocuserveLocation,
|
|
173
215
|
[
|
|
@@ -179,14 +221,14 @@ class QuackageCommandPrepareDocs extends libCommandLineCommand
|
|
|
179
221
|
);
|
|
180
222
|
}.bind(this));
|
|
181
223
|
|
|
182
|
-
// Step
|
|
224
|
+
// Step 6: Stamp meaningful <title> and <meta name="description">
|
|
183
225
|
// into the freshly-injected index.html so social-card scrapers
|
|
184
226
|
// (Slack, etc.) read the module name + version instead of the
|
|
185
227
|
// generic "powered by pict-docuserve" boilerplate.
|
|
186
228
|
tmpAnticipate.anticipate(
|
|
187
229
|
function (fNext)
|
|
188
230
|
{
|
|
189
|
-
this.log.info(`###############################[ STEP
|
|
231
|
+
this.log.info(`###############################[ STEP 6: STAMP HTML METADATA ]###############################`);
|
|
190
232
|
try
|
|
191
233
|
{
|
|
192
234
|
let tmpIndexPath = libPath.join(tmpDocsFolder, 'index.html');
|
|
@@ -2,6 +2,8 @@ const libPict = require('pict');
|
|
|
2
2
|
const libFS = require('fs');
|
|
3
3
|
const libPath = require('path');
|
|
4
4
|
const libHTTP = require('http');
|
|
5
|
+
const libHTTPS = require('https');
|
|
6
|
+
const libURL = require('url');
|
|
5
7
|
|
|
6
8
|
const libFable = require('fable');
|
|
7
9
|
const libBookstoreSchema = require('retold-harness/source/schemas/Retold-Harness-Service-Schema-Bookstore.js');
|
|
@@ -207,8 +209,8 @@ class QuackageExampleService extends libPict.ServiceProviderBase
|
|
|
207
209
|
|
|
208
210
|
/* --- Header Bar --- */
|
|
209
211
|
.pict-example-header { display: flex; align-items: stretch; background: #264653; border-bottom: 3px solid #E76F51; }
|
|
210
|
-
.pict-example-badge { background: #E76F51; color: #fff; padding: 0.6rem 1rem; font-size: 0.7rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.1em; display: flex; align-items: center; gap: 0.5rem; }
|
|
211
|
-
.pict-example-badge svg { width: 14px; height: 14px; fill: #fff; flex-shrink: 0; }
|
|
212
|
+
.pict-example-badge { background: #E76F51; color: var(--theme-color-background-panel, #fff); padding: 0.6rem 1rem; font-size: 0.7rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.1em; display: flex; align-items: center; gap: 0.5rem; }
|
|
213
|
+
.pict-example-badge svg { width: 14px; height: 14px; fill: var(--theme-color-background-panel, #fff); flex-shrink: 0; }
|
|
212
214
|
.pict-example-app-name { padding: 0.6rem 1rem; color: #FAEDCD; font-size: 1.1rem; font-weight: 600; display: flex; align-items: center; }
|
|
213
215
|
.pict-example-module { margin-left: auto; padding: 0.6rem 1rem; color: #D4A373; font-size: 0.75rem; display: flex; align-items: center; letter-spacing: 0.03em; }
|
|
214
216
|
|
|
@@ -219,7 +221,7 @@ class QuackageExampleService extends libPict.ServiceProviderBase
|
|
|
219
221
|
|
|
220
222
|
/* --- Example List --- */
|
|
221
223
|
.example-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 0.5rem; }
|
|
222
|
-
.example-list li { background: #fff; border: 1px solid #D4A373; border-left: 4px solid #E76F51; border-radius: 4px; transition: border-color 0.15s, box-shadow 0.15s; }
|
|
224
|
+
.example-list li { background: var(--theme-color-background-panel, #fff); border: 1px solid #D4A373; border-left: 4px solid #E76F51; border-radius: 4px; transition: border-color 0.15s, box-shadow 0.15s; }
|
|
223
225
|
.example-list li:hover { border-left-color: #264653; box-shadow: 0 2px 8px rgba(38,70,83,0.1); }
|
|
224
226
|
.example-list a { display: block; padding: 0.75rem 1rem; text-decoration: none; color: #264653; font-weight: 500; font-size: 0.95rem; }
|
|
225
227
|
.example-list a:hover { color: #E76F51; }
|
|
@@ -533,6 +535,235 @@ ${tmpExampleListItems} </ul>
|
|
|
533
535
|
});
|
|
534
536
|
}
|
|
535
537
|
|
|
538
|
+
// --- Serve static + reverse-proxy a configured upstream ---
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* Serve example files out of pExamplesFolder, but reverse-proxy any request
|
|
542
|
+
* whose path starts with one of pProxyConfig.paths to pProxyConfig.upstream.
|
|
543
|
+
*
|
|
544
|
+
* Used by mode='proxy' so cookie-credentialed APIs (Headlight, etc.) appear
|
|
545
|
+
* same-origin to the browser and CORS doesn't get in the way.
|
|
546
|
+
*/
|
|
547
|
+
serveStaticWithProxy(pExamplesFolder, pIndexHTML, pPort, pExamples, pProjectName, pProxyConfig, fCallback)
|
|
548
|
+
{
|
|
549
|
+
const tmpUpstreamParsed = libURL.parse(pProxyConfig.upstream);
|
|
550
|
+
const tmpUpstreamClient = (tmpUpstreamParsed.protocol === 'https:') ? libHTTPS : libHTTP;
|
|
551
|
+
const tmpUpstreamHost = tmpUpstreamParsed.host;
|
|
552
|
+
const tmpUpstreamOrigin = `${tmpUpstreamParsed.protocol}//${tmpUpstreamHost}`;
|
|
553
|
+
|
|
554
|
+
const tmpShouldProxy = (pRequestPath) =>
|
|
555
|
+
{
|
|
556
|
+
for (let i = 0; i < pProxyConfig.paths.length; i++)
|
|
557
|
+
{
|
|
558
|
+
if (pRequestPath === pProxyConfig.paths[i] || pRequestPath.indexOf(pProxyConfig.paths[i]) === 0)
|
|
559
|
+
{
|
|
560
|
+
return true;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
return false;
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
const tmpRewriteSetCookie = (pCookies) =>
|
|
567
|
+
{
|
|
568
|
+
if (!pCookies) { return pCookies; }
|
|
569
|
+
const tmpArray = Array.isArray(pCookies) ? pCookies : [pCookies];
|
|
570
|
+
return tmpArray.map((pCookie) => String(pCookie)
|
|
571
|
+
.replace(/;\s*Domain=[^;]+/i, '')
|
|
572
|
+
.replace(/;\s*Secure/i, ''));
|
|
573
|
+
};
|
|
574
|
+
|
|
575
|
+
const tmpProxyRequest = (pRequest, pResponse) =>
|
|
576
|
+
{
|
|
577
|
+
const tmpProxyHeaders = Object.assign({}, pRequest.headers);
|
|
578
|
+
if (pProxyConfig.rewriteOrigin)
|
|
579
|
+
{
|
|
580
|
+
tmpProxyHeaders.host = tmpUpstreamHost;
|
|
581
|
+
if (tmpProxyHeaders.origin) { tmpProxyHeaders.origin = tmpUpstreamOrigin; }
|
|
582
|
+
if (tmpProxyHeaders.referer) { tmpProxyHeaders.referer = tmpUpstreamOrigin + '/'; }
|
|
583
|
+
}
|
|
584
|
+
delete tmpProxyHeaders['accept-encoding'];
|
|
585
|
+
|
|
586
|
+
const tmpProxyOptions =
|
|
587
|
+
{
|
|
588
|
+
protocol: tmpUpstreamParsed.protocol,
|
|
589
|
+
hostname: tmpUpstreamParsed.hostname,
|
|
590
|
+
port: tmpUpstreamParsed.port || (tmpUpstreamParsed.protocol === 'https:' ? 443 : 80),
|
|
591
|
+
method: pRequest.method,
|
|
592
|
+
path: pRequest.url,
|
|
593
|
+
headers: tmpProxyHeaders
|
|
594
|
+
};
|
|
595
|
+
|
|
596
|
+
const tmpProxyReq = tmpUpstreamClient.request(tmpProxyOptions, (pProxyRes) =>
|
|
597
|
+
{
|
|
598
|
+
const tmpResponseHeaders = Object.assign({}, pProxyRes.headers);
|
|
599
|
+
if (pProxyConfig.rewriteCookies && tmpResponseHeaders['set-cookie'])
|
|
600
|
+
{
|
|
601
|
+
tmpResponseHeaders['set-cookie'] = tmpRewriteSetCookie(tmpResponseHeaders['set-cookie']);
|
|
602
|
+
}
|
|
603
|
+
pResponse.writeHead(pProxyRes.statusCode || 502, tmpResponseHeaders);
|
|
604
|
+
pProxyRes.pipe(pResponse);
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
tmpProxyReq.on('error', (pError) =>
|
|
608
|
+
{
|
|
609
|
+
this.log.error(`Proxy upstream error for ${pRequest.method} ${pRequest.url}: ${pError.message}`);
|
|
610
|
+
if (!pResponse.headersSent)
|
|
611
|
+
{
|
|
612
|
+
pResponse.writeHead(502, { 'Content-Type': 'text/plain' });
|
|
613
|
+
}
|
|
614
|
+
pResponse.end(`Bad gateway: ${pError.message}`);
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
pRequest.pipe(tmpProxyReq);
|
|
618
|
+
};
|
|
619
|
+
|
|
620
|
+
const tmpServeStaticFile = (pRequest, pResponse) =>
|
|
621
|
+
{
|
|
622
|
+
let tmpRequestURL = pRequest.url.split('?')[0];
|
|
623
|
+
|
|
624
|
+
if (tmpRequestURL === '/' || tmpRequestURL === '/index.html')
|
|
625
|
+
{
|
|
626
|
+
pResponse.writeHead(200, { 'Content-Type': 'text/html' });
|
|
627
|
+
pResponse.end(pIndexHTML);
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
let tmpFilePath = libPath.join(pExamplesFolder, decodeURIComponent(tmpRequestURL));
|
|
632
|
+
if (!tmpFilePath.startsWith(pExamplesFolder))
|
|
633
|
+
{
|
|
634
|
+
pResponse.writeHead(403);
|
|
635
|
+
pResponse.end('Forbidden');
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
if (libFS.existsSync(tmpFilePath) && libFS.statSync(tmpFilePath).isDirectory())
|
|
639
|
+
{
|
|
640
|
+
tmpFilePath = libPath.join(tmpFilePath, 'index.html');
|
|
641
|
+
}
|
|
642
|
+
if (!libFS.existsSync(tmpFilePath))
|
|
643
|
+
{
|
|
644
|
+
pResponse.writeHead(404);
|
|
645
|
+
pResponse.end('Not Found');
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
let tmpExtension = libPath.extname(tmpFilePath).toLowerCase();
|
|
650
|
+
let tmpMimeType = this.getMimeType(tmpExtension);
|
|
651
|
+
try
|
|
652
|
+
{
|
|
653
|
+
let tmpContent = libFS.readFileSync(tmpFilePath);
|
|
654
|
+
pResponse.writeHead(200, { 'Content-Type': tmpMimeType });
|
|
655
|
+
pResponse.end(tmpContent);
|
|
656
|
+
}
|
|
657
|
+
catch (pReadError)
|
|
658
|
+
{
|
|
659
|
+
pResponse.writeHead(500);
|
|
660
|
+
pResponse.end('Internal Server Error');
|
|
661
|
+
}
|
|
662
|
+
};
|
|
663
|
+
|
|
664
|
+
const tmpServer = libHTTP.createServer((pRequest, pResponse) =>
|
|
665
|
+
{
|
|
666
|
+
const tmpPath = pRequest.url.split('?')[0];
|
|
667
|
+
if (tmpShouldProxy(tmpPath))
|
|
668
|
+
{
|
|
669
|
+
tmpProxyRequest(pRequest, pResponse);
|
|
670
|
+
}
|
|
671
|
+
else
|
|
672
|
+
{
|
|
673
|
+
tmpServeStaticFile(pRequest, pResponse);
|
|
674
|
+
}
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
tmpServer.listen(pPort, () =>
|
|
678
|
+
{
|
|
679
|
+
this.log.info(`##############################################`);
|
|
680
|
+
this.log.info(` Example server running at http://localhost:${pPort}/`);
|
|
681
|
+
this.log.info(` Project: ${pProjectName}`);
|
|
682
|
+
this.log.info(` Proxy upstream: ${pProxyConfig.upstream}`);
|
|
683
|
+
this.log.info(` Proxied paths: ${pProxyConfig.paths.join(', ')}`);
|
|
684
|
+
this.log.info(` Serving ${pExamples.length} example(s):`);
|
|
685
|
+
for (let i = 0; i < pExamples.length; i++)
|
|
686
|
+
{
|
|
687
|
+
this.log.info(` - ${pExamples[i].DisplayName}: http://localhost:${pPort}/${pExamples[i].RelativePath}`);
|
|
688
|
+
}
|
|
689
|
+
this.log.info(`##############################################`);
|
|
690
|
+
this.log.info(`Press Ctrl+C to stop.`);
|
|
691
|
+
});
|
|
692
|
+
|
|
693
|
+
tmpServer.on('error', (pError) =>
|
|
694
|
+
{
|
|
695
|
+
if (pError.code === 'EADDRINUSE')
|
|
696
|
+
{
|
|
697
|
+
this.log.error(`Port ${pPort} is already in use. Try specifying a different port with -p.`);
|
|
698
|
+
}
|
|
699
|
+
else
|
|
700
|
+
{
|
|
701
|
+
this.log.error(`Server error: ${pError.message}`);
|
|
702
|
+
}
|
|
703
|
+
return fCallback(pError);
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// --- Per-project examples configuration ---
|
|
708
|
+
|
|
709
|
+
/**
|
|
710
|
+
* Resolve the host project's examples configuration from its package.json.
|
|
711
|
+
* Reads `quackageExamples` (preferred) or `quack.examples` (alias).
|
|
712
|
+
*
|
|
713
|
+
* Recognised shapes:
|
|
714
|
+
*
|
|
715
|
+
* "quackageExamples": { "mode": "bookstore" } // default — Meadow + SQLite + bookstore data
|
|
716
|
+
* "quackageExamples": { "mode": "static" } // serve files only, no API
|
|
717
|
+
* "quackageExamples": {
|
|
718
|
+
* "mode": "proxy",
|
|
719
|
+
* "proxy": {
|
|
720
|
+
* "upstream": "https://fieldbook.qa.headlight.com",
|
|
721
|
+
* "paths": ["/1.0/", "/CheckSession", "/Authenticate", "/Report/"],
|
|
722
|
+
* "rewriteCookies": true,
|
|
723
|
+
* "rewriteOrigin": true
|
|
724
|
+
* }
|
|
725
|
+
* }
|
|
726
|
+
*
|
|
727
|
+
* Defaults (mode='proxy'):
|
|
728
|
+
* paths = ["/1.0/"]
|
|
729
|
+
* rewriteCookies = true (strip Domain= and Secure so localhost keeps the session)
|
|
730
|
+
* rewriteOrigin = true (rewrite Host/Origin/Referer to the upstream)
|
|
731
|
+
*/
|
|
732
|
+
resolveExamplesConfig()
|
|
733
|
+
{
|
|
734
|
+
const tmpPackage = (this.fable && this.fable.AppData && this.fable.AppData.Package) || {};
|
|
735
|
+
const tmpRaw = tmpPackage.quackageExamples
|
|
736
|
+
|| (tmpPackage.quack && tmpPackage.quack.examples)
|
|
737
|
+
|| {};
|
|
738
|
+
|
|
739
|
+
const tmpMode = (typeof tmpRaw.mode === 'string') ? tmpRaw.mode.toLowerCase() : 'bookstore';
|
|
740
|
+
const tmpResolved = { mode: tmpMode };
|
|
741
|
+
|
|
742
|
+
if (tmpMode === 'proxy')
|
|
743
|
+
{
|
|
744
|
+
const tmpProxy = tmpRaw.proxy || {};
|
|
745
|
+
tmpResolved.proxy =
|
|
746
|
+
{
|
|
747
|
+
upstream: (typeof tmpProxy.upstream === 'string' && tmpProxy.upstream) ? tmpProxy.upstream.replace(/\/+$/, '') : null,
|
|
748
|
+
paths: (Array.isArray(tmpProxy.paths) && tmpProxy.paths.length) ? tmpProxy.paths.slice() : ['/1.0/'],
|
|
749
|
+
rewriteCookies: (tmpProxy.rewriteCookies !== false),
|
|
750
|
+
rewriteOrigin: (tmpProxy.rewriteOrigin !== false)
|
|
751
|
+
};
|
|
752
|
+
// Allow env-var override for the upstream so devs can switch envs without
|
|
753
|
+
// editing package.json: `UPSTREAM=https://fieldbook.headlight.com npx quack examples`.
|
|
754
|
+
if (process.env.QUACKAGE_PROXY_UPSTREAM)
|
|
755
|
+
{
|
|
756
|
+
tmpResolved.proxy.upstream = process.env.QUACKAGE_PROXY_UPSTREAM.replace(/\/+$/, '');
|
|
757
|
+
}
|
|
758
|
+
else if (process.env.UPSTREAM)
|
|
759
|
+
{
|
|
760
|
+
tmpResolved.proxy.upstream = process.env.UPSTREAM.replace(/\/+$/, '');
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
return tmpResolved;
|
|
765
|
+
}
|
|
766
|
+
|
|
536
767
|
// --- Serve examples ---
|
|
537
768
|
|
|
538
769
|
serveExamples(pExamplesFolder, pPort, fCallback)
|
|
@@ -555,7 +786,28 @@ ${tmpExampleListItems} </ul>
|
|
|
555
786
|
|
|
556
787
|
let tmpIndexHTML = this.generateIndexHTML(tmpProjectName, tmpExamples, tmpPort);
|
|
557
788
|
|
|
558
|
-
|
|
789
|
+
const tmpConfig = this.resolveExamplesConfig();
|
|
790
|
+
|
|
791
|
+
if (tmpConfig.mode === 'proxy')
|
|
792
|
+
{
|
|
793
|
+
if (!tmpConfig.proxy.upstream)
|
|
794
|
+
{
|
|
795
|
+
this.log.error(`quackageExamples.mode is 'proxy' but no upstream URL was configured.`);
|
|
796
|
+
this.log.error(`Set quackageExamples.proxy.upstream in package.json (or QUACKAGE_PROXY_UPSTREAM / UPSTREAM env var).`);
|
|
797
|
+
return fCallback(new Error('Proxy mode requires an upstream.'));
|
|
798
|
+
}
|
|
799
|
+
this.log.info(`Examples mode: proxy → ${tmpConfig.proxy.upstream}`);
|
|
800
|
+
this.log.info(`Proxying paths: ${tmpConfig.proxy.paths.join(', ')}`);
|
|
801
|
+
return this.serveStaticWithProxy(tmpExamplesFolder, tmpIndexHTML, tmpPort, tmpExamples, tmpProjectName, tmpConfig.proxy, fCallback);
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
if (tmpConfig.mode === 'static')
|
|
805
|
+
{
|
|
806
|
+
this.log.info(`Examples mode: static (no API)`);
|
|
807
|
+
return this.serveStaticHTTP(tmpExamplesFolder, tmpIndexHTML, tmpPort, fCallback);
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
// Default: bookstore API
|
|
559
811
|
this.log.info(`Initializing bookstore API with SQLite ...`);
|
|
560
812
|
this.initializeBookstoreAPI(tmpPort, tmpExamplesFolder,
|
|
561
813
|
(pError, pHarnessFable) =>
|
package/docs/css/docuserve.css
DELETED
|
@@ -1,327 +0,0 @@
|
|
|
1
|
-
/* ============================================================================
|
|
2
|
-
Pict Docuserve - Base Styles & Theme Variables
|
|
3
|
-
============================================================================ */
|
|
4
|
-
|
|
5
|
-
/* ----------------------------------------------------------------------------
|
|
6
|
-
Theme variables — light defaults on :root.
|
|
7
|
-
Dark mode applies when either:
|
|
8
|
-
(a) the user explicitly selected dark via <html data-theme="dark">
|
|
9
|
-
(b) the user hasn't chosen anything AND the system prefers dark
|
|
10
|
-
An explicit <html data-theme="light"> pins the light palette regardless.
|
|
11
|
-
---------------------------------------------------------------------------- */
|
|
12
|
-
|
|
13
|
-
:root
|
|
14
|
-
{
|
|
15
|
-
/* Surfaces */
|
|
16
|
-
--docuserve-bg: #FDFBF7;
|
|
17
|
-
--docuserve-bg-elevated: #FFFFFF;
|
|
18
|
-
--docuserve-border: #DDD6CA;
|
|
19
|
-
--docuserve-border-soft: #EAE3D8;
|
|
20
|
-
|
|
21
|
-
/* Text */
|
|
22
|
-
--docuserve-text: #2A241E;
|
|
23
|
-
--docuserve-text-strong: #3D3229;
|
|
24
|
-
--docuserve-text-muted: #5E5549;
|
|
25
|
-
--docuserve-text-dim: #8A7F72;
|
|
26
|
-
|
|
27
|
-
/* Accent / links */
|
|
28
|
-
--docuserve-accent: #2E7D74;
|
|
29
|
-
--docuserve-accent-hover: #236660;
|
|
30
|
-
|
|
31
|
-
/* Top bar */
|
|
32
|
-
--docuserve-topbar-bg: #3D3229;
|
|
33
|
-
--docuserve-topbar-text: #E8E0D4;
|
|
34
|
-
--docuserve-topbar-text-muted: #B5AA9A;
|
|
35
|
-
--docuserve-topbar-text-dim: #8A7F72;
|
|
36
|
-
--docuserve-topbar-hover-bg: #524438;
|
|
37
|
-
--docuserve-topbar-version-bg: rgba(255, 255, 255, 0.06);
|
|
38
|
-
--docuserve-topbar-version-border: rgba(255, 255, 255, 0.08);
|
|
39
|
-
--docuserve-topbar-version-text: #B5AA9A;
|
|
40
|
-
|
|
41
|
-
/* Sidebar */
|
|
42
|
-
--docuserve-sidebar-bg: #FAF7F1;
|
|
43
|
-
--docuserve-sidebar-border: #DDD6CA;
|
|
44
|
-
--docuserve-sidebar-border-soft: #E5DED1;
|
|
45
|
-
--docuserve-sidebar-text: #423D37;
|
|
46
|
-
--docuserve-sidebar-group-title: #3D3229;
|
|
47
|
-
--docuserve-sidebar-module-text: #5E5549;
|
|
48
|
-
--docuserve-sidebar-hover-bg: #EAE3D8;
|
|
49
|
-
--docuserve-sidebar-hover-text: #2E7D74;
|
|
50
|
-
--docuserve-sidebar-active-bg: #E5DED1;
|
|
51
|
-
--docuserve-sidebar-active-text: #2E7D74;
|
|
52
|
-
--docuserve-sidebar-search-bg: #FFFFFF;
|
|
53
|
-
--docuserve-sidebar-search-border: #DDD6CA;
|
|
54
|
-
|
|
55
|
-
/* Inline code */
|
|
56
|
-
--docuserve-inline-code-bg: #F0ECE4;
|
|
57
|
-
--docuserve-inline-code-text: #9E3A50;
|
|
58
|
-
|
|
59
|
-
/* Code block panel */
|
|
60
|
-
--docuserve-code-bg: #F6F3EE;
|
|
61
|
-
--docuserve-code-border: #E5DED1;
|
|
62
|
-
--docuserve-code-gutter-bg: #EFEAE0;
|
|
63
|
-
--docuserve-code-gutter-border: #DDD6CA;
|
|
64
|
-
--docuserve-code-gutter-text: #A59986;
|
|
65
|
-
--docuserve-code-text: #2A241E;
|
|
66
|
-
|
|
67
|
-
/* Syntax tokens — low-chroma dark-on-light palette */
|
|
68
|
-
--docuserve-tok-keyword: #A03472;
|
|
69
|
-
--docuserve-tok-string: #1A6640;
|
|
70
|
-
--docuserve-tok-number: #B25A00;
|
|
71
|
-
--docuserve-tok-comment: #8A7F72;
|
|
72
|
-
--docuserve-tok-operator: #2E7D74;
|
|
73
|
-
--docuserve-tok-punctuation: #2A241E;
|
|
74
|
-
--docuserve-tok-function: #2A5DB0;
|
|
75
|
-
--docuserve-tok-property: #9E3A50;
|
|
76
|
-
--docuserve-tok-tag: #9E3A50;
|
|
77
|
-
--docuserve-tok-attr-name: #B25A00;
|
|
78
|
-
--docuserve-tok-attr-value: #1A6640;
|
|
79
|
-
|
|
80
|
-
/* Tables, blockquotes, mermaid */
|
|
81
|
-
--docuserve-table-header-bg: #F5F0E8;
|
|
82
|
-
--docuserve-table-row-alt-bg: #F9F6F0;
|
|
83
|
-
--docuserve-blockquote-bg: #F7F5F0;
|
|
84
|
-
--docuserve-blockquote-border: #2E7D74;
|
|
85
|
-
--docuserve-blockquote-text: #5E5549;
|
|
86
|
-
--docuserve-mermaid-bg: #FFFFFF;
|
|
87
|
-
|
|
88
|
-
/* Scrollbars */
|
|
89
|
-
--docuserve-scrollbar-track: #F5F0E8;
|
|
90
|
-
--docuserve-scrollbar-thumb: #D4CCBE;
|
|
91
|
-
--docuserve-scrollbar-thumb-hover: #B5AA9A;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
@media (prefers-color-scheme: dark)
|
|
95
|
-
{
|
|
96
|
-
:root:not([data-theme="light"])
|
|
97
|
-
{
|
|
98
|
-
--docuserve-bg: #15120F;
|
|
99
|
-
--docuserve-bg-elevated: #1B1814;
|
|
100
|
-
--docuserve-border: #2F2823;
|
|
101
|
-
--docuserve-border-soft: #26211C;
|
|
102
|
-
|
|
103
|
-
--docuserve-text: #E8E0D4;
|
|
104
|
-
--docuserve-text-strong: #F2ECE0;
|
|
105
|
-
--docuserve-text-muted: #B5AA9A;
|
|
106
|
-
--docuserve-text-dim: #7A6F62;
|
|
107
|
-
|
|
108
|
-
--docuserve-accent: #5DB8A8;
|
|
109
|
-
--docuserve-accent-hover: #7FCCB8;
|
|
110
|
-
|
|
111
|
-
--docuserve-topbar-bg: #1A1612;
|
|
112
|
-
--docuserve-topbar-text: #E8E0D4;
|
|
113
|
-
--docuserve-topbar-text-muted: #B5AA9A;
|
|
114
|
-
--docuserve-topbar-text-dim: #7A6F62;
|
|
115
|
-
--docuserve-topbar-hover-bg: #2A241E;
|
|
116
|
-
--docuserve-topbar-version-bg: rgba(255, 255, 255, 0.05);
|
|
117
|
-
--docuserve-topbar-version-border: rgba(255, 255, 255, 0.09);
|
|
118
|
-
--docuserve-topbar-version-text: #B5AA9A;
|
|
119
|
-
|
|
120
|
-
--docuserve-sidebar-bg: #1B1814;
|
|
121
|
-
--docuserve-sidebar-border: #2F2823;
|
|
122
|
-
--docuserve-sidebar-border-soft: #26211C;
|
|
123
|
-
--docuserve-sidebar-text: #C9C0B3;
|
|
124
|
-
--docuserve-sidebar-group-title: #F2ECE0;
|
|
125
|
-
--docuserve-sidebar-module-text: #B5AA9A;
|
|
126
|
-
--docuserve-sidebar-hover-bg: #2A241E;
|
|
127
|
-
--docuserve-sidebar-hover-text: #7FCCB8;
|
|
128
|
-
--docuserve-sidebar-active-bg: #2F2823;
|
|
129
|
-
--docuserve-sidebar-active-text: #7FCCB8;
|
|
130
|
-
--docuserve-sidebar-search-bg: #26211C;
|
|
131
|
-
--docuserve-sidebar-search-border: #2F2823;
|
|
132
|
-
|
|
133
|
-
--docuserve-inline-code-bg: #2A241E;
|
|
134
|
-
--docuserve-inline-code-text: #E8B07A;
|
|
135
|
-
|
|
136
|
-
--docuserve-code-bg: #1E1A17;
|
|
137
|
-
--docuserve-code-border: #2F2823;
|
|
138
|
-
--docuserve-code-gutter-bg: #17130F;
|
|
139
|
-
--docuserve-code-gutter-border: #2F2823;
|
|
140
|
-
--docuserve-code-gutter-text: #6A6052;
|
|
141
|
-
--docuserve-code-text: #E8E0D4;
|
|
142
|
-
|
|
143
|
-
--docuserve-tok-keyword: #C678DD;
|
|
144
|
-
--docuserve-tok-string: #98C379;
|
|
145
|
-
--docuserve-tok-number: #D19A66;
|
|
146
|
-
--docuserve-tok-comment: #7F848E;
|
|
147
|
-
--docuserve-tok-operator: #56B6C2;
|
|
148
|
-
--docuserve-tok-punctuation: #E8E0D4;
|
|
149
|
-
--docuserve-tok-function: #61AFEF;
|
|
150
|
-
--docuserve-tok-property: #E06C75;
|
|
151
|
-
--docuserve-tok-tag: #E06C75;
|
|
152
|
-
--docuserve-tok-attr-name: #D19A66;
|
|
153
|
-
--docuserve-tok-attr-value: #98C379;
|
|
154
|
-
|
|
155
|
-
--docuserve-table-header-bg: #26211C;
|
|
156
|
-
--docuserve-table-row-alt-bg: #1F1B17;
|
|
157
|
-
--docuserve-blockquote-bg: #1F1B17;
|
|
158
|
-
--docuserve-blockquote-border: #5DB8A8;
|
|
159
|
-
--docuserve-blockquote-text: #C9C0B3;
|
|
160
|
-
--docuserve-mermaid-bg: #E8E0D4;
|
|
161
|
-
|
|
162
|
-
--docuserve-scrollbar-track: #1B1814;
|
|
163
|
-
--docuserve-scrollbar-thumb: #3A322B;
|
|
164
|
-
--docuserve-scrollbar-thumb-hover: #524438;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
:root[data-theme="dark"]
|
|
169
|
-
{
|
|
170
|
-
--docuserve-bg: #15120F;
|
|
171
|
-
--docuserve-bg-elevated: #1B1814;
|
|
172
|
-
--docuserve-border: #2F2823;
|
|
173
|
-
--docuserve-border-soft: #26211C;
|
|
174
|
-
|
|
175
|
-
--docuserve-text: #E8E0D4;
|
|
176
|
-
--docuserve-text-strong: #F2ECE0;
|
|
177
|
-
--docuserve-text-muted: #B5AA9A;
|
|
178
|
-
--docuserve-text-dim: #7A6F62;
|
|
179
|
-
|
|
180
|
-
--docuserve-accent: #5DB8A8;
|
|
181
|
-
--docuserve-accent-hover: #7FCCB8;
|
|
182
|
-
|
|
183
|
-
--docuserve-topbar-bg: #1A1612;
|
|
184
|
-
--docuserve-topbar-text: #E8E0D4;
|
|
185
|
-
--docuserve-topbar-text-muted: #B5AA9A;
|
|
186
|
-
--docuserve-topbar-text-dim: #7A6F62;
|
|
187
|
-
--docuserve-topbar-hover-bg: #2A241E;
|
|
188
|
-
--docuserve-topbar-version-bg: rgba(255, 255, 255, 0.05);
|
|
189
|
-
--docuserve-topbar-version-border: rgba(255, 255, 255, 0.09);
|
|
190
|
-
--docuserve-topbar-version-text: #B5AA9A;
|
|
191
|
-
|
|
192
|
-
--docuserve-sidebar-bg: #1B1814;
|
|
193
|
-
--docuserve-sidebar-border: #2F2823;
|
|
194
|
-
--docuserve-sidebar-border-soft: #26211C;
|
|
195
|
-
--docuserve-sidebar-text: #C9C0B3;
|
|
196
|
-
--docuserve-sidebar-group-title: #F2ECE0;
|
|
197
|
-
--docuserve-sidebar-module-text: #B5AA9A;
|
|
198
|
-
--docuserve-sidebar-hover-bg: #2A241E;
|
|
199
|
-
--docuserve-sidebar-hover-text: #7FCCB8;
|
|
200
|
-
--docuserve-sidebar-active-bg: #2F2823;
|
|
201
|
-
--docuserve-sidebar-active-text: #7FCCB8;
|
|
202
|
-
--docuserve-sidebar-search-bg: #26211C;
|
|
203
|
-
--docuserve-sidebar-search-border: #2F2823;
|
|
204
|
-
|
|
205
|
-
--docuserve-inline-code-bg: #2A241E;
|
|
206
|
-
--docuserve-inline-code-text: #E8B07A;
|
|
207
|
-
|
|
208
|
-
--docuserve-code-bg: #1E1A17;
|
|
209
|
-
--docuserve-code-border: #2F2823;
|
|
210
|
-
--docuserve-code-gutter-bg: #17130F;
|
|
211
|
-
--docuserve-code-gutter-border: #2F2823;
|
|
212
|
-
--docuserve-code-gutter-text: #6A6052;
|
|
213
|
-
--docuserve-code-text: #E8E0D4;
|
|
214
|
-
|
|
215
|
-
--docuserve-tok-keyword: #C678DD;
|
|
216
|
-
--docuserve-tok-string: #98C379;
|
|
217
|
-
--docuserve-tok-number: #D19A66;
|
|
218
|
-
--docuserve-tok-comment: #7F848E;
|
|
219
|
-
--docuserve-tok-operator: #56B6C2;
|
|
220
|
-
--docuserve-tok-punctuation: #E8E0D4;
|
|
221
|
-
--docuserve-tok-function: #61AFEF;
|
|
222
|
-
--docuserve-tok-property: #E06C75;
|
|
223
|
-
--docuserve-tok-tag: #E06C75;
|
|
224
|
-
--docuserve-tok-attr-name: #D19A66;
|
|
225
|
-
--docuserve-tok-attr-value: #98C379;
|
|
226
|
-
|
|
227
|
-
--docuserve-table-header-bg: #26211C;
|
|
228
|
-
--docuserve-table-row-alt-bg: #1F1B17;
|
|
229
|
-
--docuserve-blockquote-bg: #1F1B17;
|
|
230
|
-
--docuserve-blockquote-border: #5DB8A8;
|
|
231
|
-
--docuserve-blockquote-text: #C9C0B3;
|
|
232
|
-
--docuserve-mermaid-bg: #E8E0D4;
|
|
233
|
-
|
|
234
|
-
--docuserve-scrollbar-track: #1B1814;
|
|
235
|
-
--docuserve-scrollbar-thumb: #3A322B;
|
|
236
|
-
--docuserve-scrollbar-thumb-hover: #524438;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/* ----------------------------------------------------------------------------
|
|
240
|
-
Reset and base
|
|
241
|
-
---------------------------------------------------------------------------- */
|
|
242
|
-
|
|
243
|
-
*, *::before, *::after
|
|
244
|
-
{
|
|
245
|
-
box-sizing: border-box;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
html, body
|
|
249
|
-
{
|
|
250
|
-
margin: 0;
|
|
251
|
-
padding: 0;
|
|
252
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
253
|
-
font-size: 16px;
|
|
254
|
-
line-height: 1.5;
|
|
255
|
-
color: var(--docuserve-text);
|
|
256
|
-
background-color: var(--docuserve-bg);
|
|
257
|
-
-webkit-font-smoothing: antialiased;
|
|
258
|
-
-moz-osx-font-smoothing: grayscale;
|
|
259
|
-
transition: background-color 0.15s ease, color 0.15s ease;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/* Typography */
|
|
263
|
-
h1, h2, h3, h4, h5, h6
|
|
264
|
-
{
|
|
265
|
-
margin-top: 0;
|
|
266
|
-
line-height: 1.3;
|
|
267
|
-
color: var(--docuserve-text-strong);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
a
|
|
271
|
-
{
|
|
272
|
-
color: var(--docuserve-accent);
|
|
273
|
-
text-decoration: none;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
a:hover
|
|
277
|
-
{
|
|
278
|
-
color: var(--docuserve-accent-hover);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/* Application container */
|
|
282
|
-
#Docuserve-Application-Container
|
|
283
|
-
{
|
|
284
|
-
min-height: 100vh;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
/* Utility: scrollbar styling */
|
|
288
|
-
::-webkit-scrollbar
|
|
289
|
-
{
|
|
290
|
-
width: 8px;
|
|
291
|
-
height: 8px;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
::-webkit-scrollbar-track
|
|
295
|
-
{
|
|
296
|
-
background: var(--docuserve-scrollbar-track);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
::-webkit-scrollbar-thumb
|
|
300
|
-
{
|
|
301
|
-
background: var(--docuserve-scrollbar-thumb);
|
|
302
|
-
border-radius: 4px;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
::-webkit-scrollbar-thumb:hover
|
|
306
|
-
{
|
|
307
|
-
background: var(--docuserve-scrollbar-thumb-hover);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
/* Responsive adjustments */
|
|
311
|
-
@media (max-width: 768px)
|
|
312
|
-
{
|
|
313
|
-
html
|
|
314
|
-
{
|
|
315
|
-
font-size: 14px;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
#Docuserve-Sidebar-Container
|
|
319
|
-
{
|
|
320
|
-
display: none;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
.docuserve-body
|
|
324
|
-
{
|
|
325
|
-
flex-direction: column;
|
|
326
|
-
}
|
|
327
|
-
}
|