retold-data-service 2.1.2 → 2.1.6
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/.github/workflows/publish-image.yml +2 -2
- package/BUILDING-AND-PUBLISHING.md +6 -6
- package/CONTRIBUTING.md +1 -1
- package/Dockerfile +1 -1
- package/README.md +12 -27
- package/build-all.js +66 -0
- package/diagrams/architecture.excalidraw +2966 -0
- package/diagrams/architecture.mmd +17 -0
- package/diagrams/architecture.svg +2 -0
- package/docs/README.md +12 -12
- package/docs/_brand.json +18 -0
- package/docs/_cover.md +1 -1
- package/docs/_topbar.md +1 -1
- package/docs/_version.json +3 -3
- package/docs/api/reference.md +8 -8
- package/docs/architecture.md +6 -84
- package/docs/diagrams/component-diagram.excalidraw +2807 -0
- package/docs/diagrams/component-diagram.mmd +14 -0
- package/docs/diagrams/component-diagram.svg +2 -0
- package/docs/diagrams/component-stack.excalidraw +1169 -0
- package/docs/diagrams/component-stack.mmd +6 -0
- package/docs/diagrams/component-stack.svg +2 -0
- package/docs/diagrams/hook-execution-order.excalidraw +3230 -0
- package/docs/diagrams/hook-execution-order.mmd +19 -0
- package/docs/diagrams/hook-execution-order.svg +2 -0
- package/docs/diagrams/initialization-flow.excalidraw +1800 -0
- package/docs/diagrams/initialization-flow.mmd +22 -0
- package/docs/diagrams/initialization-flow.svg +2 -0
- package/docs/index.html +6 -7
- package/docs/lifecycle-hooks.md +2 -21
- package/docs/retold-catalog.json +141 -141
- package/docs/retold-keyword-index.json +6818 -1608
- package/package.json +130 -96
- package/source/services/RetoldDataService-Brand.js +13 -0
- package/source/services/comprehension-loader/pict-app/Pict-Application-ComprehensionLoader.js +65 -15
- package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-Layout.js +28 -74
- package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-Load.js +17 -17
- package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-SettingsPanel.js +62 -0
- package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-Shell.js +142 -0
- package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-StatusBar.js +125 -0
- package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-StatusDetail.js +89 -0
- package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-TopBar-Nav.js +42 -0
- package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-TopBar-User.js +48 -0
- package/source/services/comprehension-loader/web/comprehension-loader.js +5517 -6183
- package/source/services/comprehension-loader/web/comprehension-loader.js.map +1 -1
- package/source/services/comprehension-loader/web/comprehension-loader.min.js +75 -1
- package/source/services/comprehension-loader/web/comprehension-loader.min.js.map +1 -1
- package/source/services/comprehension-loader/web/favicons/favicon-dark.svg +13 -0
- package/source/services/comprehension-loader/web/favicons/favicon-light.svg +13 -0
- package/source/services/comprehension-loader/web/favicons/favicon.svg +13 -0
- package/source/services/comprehension-loader/web/index.html +3 -0
- package/source/services/comprehension-loader/web/pict.min.js +12 -0
- package/source/services/data-cloner/DataCloner-Command-Headless.js +102 -4
- package/source/services/data-cloner/DataCloner-Command-Schema.js +105 -8
- package/source/services/data-cloner/DataCloner-Command-Sync.js +111 -75
- package/source/services/data-cloner/pict-app/Pict-Application-DataCloner.js +70 -47
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Export.js +3 -3
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Layout.js +40 -86
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-SettingsPanel.js +61 -0
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Shell.js +136 -0
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-StatusBar.js +117 -0
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-StatusDetail.js +81 -0
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Sync.js +18 -18
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-TopBar-Nav.js +42 -0
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-TopBar-User.js +48 -0
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-ViewData.js +2 -2
- package/source/services/data-cloner/web/data-cloner.js +5874 -7986
- package/source/services/data-cloner/web/data-cloner.js.map +1 -1
- package/source/services/data-cloner/web/data-cloner.min.js +75 -1
- package/source/services/data-cloner/web/data-cloner.min.js.map +1 -1
- package/source/services/data-cloner/web/favicons/favicon-dark.svg +13 -0
- package/source/services/data-cloner/web/favicons/favicon-light.svg +13 -0
- package/source/services/data-cloner/web/favicons/favicon.svg +13 -0
- package/source/services/data-cloner/web/favicons/favicons/favicon-dark.svg +13 -0
- package/source/services/data-cloner/web/favicons/favicons/favicon-light.svg +13 -0
- package/source/services/data-cloner/web/favicons/favicons/favicon.svg +13 -0
- package/source/services/data-cloner/web/index.html +3 -0
- package/source/services/data-cloner/web/pict.min.js +12 -0
- package/test/Bundles_smoke_tests.js +43 -0
- package/test/ComprehensionLoader_smoke_tests.js +95 -0
- package/test/DataCloner-RuntimeOverrides_tests.js +344 -0
- package/test/DataCloner_smoke_tests.js +87 -0
- package/tools/smoke-converge-endpoint.js +190 -0
- package/docs/css/docuserve.css +0 -327
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Live smoke test for the DataCloner index-convergence endpoint.
|
|
5
|
+
*
|
|
6
|
+
* Spawns the REAL data-cloner service and drives the actual HTTP path a headless
|
|
7
|
+
* clone uses: configure connection → fetch schema → deploy → POST
|
|
8
|
+
* /clone/schema/indices/converge → converge again (idempotency). Asserts the
|
|
9
|
+
* operational indexes the policy declares are the ones the endpoint creates,
|
|
10
|
+
* that a table with neither Deleted nor GUID gets none, and that a second run is
|
|
11
|
+
* a no-op. Exercises the provider registry + RetoldDataServiceConnectionManager
|
|
12
|
+
* + IndexPolicy + IndexConvergence together — the production service path.
|
|
13
|
+
*
|
|
14
|
+
* Uses retold's own committed BookStore test model (no external data).
|
|
15
|
+
*
|
|
16
|
+
* Usage: node tools/smoke-converge-endpoint.js [SQLite|MSSQL] [port]
|
|
17
|
+
* SQLite (default): zero infra.
|
|
18
|
+
* MSSQL: runtime-configures the local meadow-connection-mssql-test container
|
|
19
|
+
* (localhost:21433 / sa / db clonesmoke) and adds an independent
|
|
20
|
+
* sqlcmd cross-check + a managed-prune-through-the-endpoint check.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const libChildProcess = require('child_process');
|
|
24
|
+
const libHttp = require('http');
|
|
25
|
+
const libPath = require('path');
|
|
26
|
+
const libOs = require('os');
|
|
27
|
+
const libFs = require('fs');
|
|
28
|
+
|
|
29
|
+
const libIndexPolicy = require('meadow-integration/source/services/clone/Meadow-Service-IndexPolicy.js');
|
|
30
|
+
|
|
31
|
+
const PROVIDER = process.argv[2] || 'SQLite';
|
|
32
|
+
const PORT = parseInt(process.argv[3], 10) || 9500;
|
|
33
|
+
const BASE = `http://localhost:${PORT}`;
|
|
34
|
+
const DEPLOY_TABLES = [ 'Book', 'Author', 'BookAuthorJoin' ]; // Book/Author: Deleted+GUID; join: neither
|
|
35
|
+
const MODEL = require('../test/model/MeadowModel-Extended.json');
|
|
36
|
+
|
|
37
|
+
const MSSQL_CONFIG = { server: 'localhost', port: 21433, user: 'sa', password: 'Retold1234567890!', database: 'clonesmoke', ConnectionPoolLimit: 5 };
|
|
38
|
+
const MSSQL_CONTAINER = 'meadow-connection-mssql-test';
|
|
39
|
+
const SQLCMD = '/opt/mssql-tools18/bin/sqlcmd';
|
|
40
|
+
|
|
41
|
+
let _pass = 0, _fail = 0;
|
|
42
|
+
function check(pCondition, pMessage)
|
|
43
|
+
{
|
|
44
|
+
if (pCondition) { _pass++; console.log(`[ok] ${pMessage}`); }
|
|
45
|
+
else { _fail++; console.log(`[fail] ${pMessage}`); }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// -- tiny HTTP JSON client (promise) --
|
|
49
|
+
function httpJSON(pMethod, pPath, pBody)
|
|
50
|
+
{
|
|
51
|
+
return new Promise((fResolve, fReject) =>
|
|
52
|
+
{
|
|
53
|
+
let tmpData = pBody ? JSON.stringify(pBody) : null;
|
|
54
|
+
let tmpReq = libHttp.request(`${BASE}${pPath}`,
|
|
55
|
+
{ method: pMethod, headers: { 'Content-Type': 'application/json', 'Content-Length': tmpData ? Buffer.byteLength(tmpData) : 0 } },
|
|
56
|
+
(pRes) =>
|
|
57
|
+
{
|
|
58
|
+
let tmpChunks = '';
|
|
59
|
+
pRes.on('data', (pChunk) => { tmpChunks += pChunk; });
|
|
60
|
+
pRes.on('end', () =>
|
|
61
|
+
{
|
|
62
|
+
let tmpParsed;
|
|
63
|
+
try { tmpParsed = tmpChunks ? JSON.parse(tmpChunks) : {}; }
|
|
64
|
+
catch (e) { tmpParsed = { _raw: tmpChunks }; }
|
|
65
|
+
fResolve({ status: pRes.statusCode, body: tmpParsed });
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
tmpReq.on('error', fReject);
|
|
69
|
+
if (tmpData) { tmpReq.write(tmpData); }
|
|
70
|
+
tmpReq.end();
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function sleep(pMs) { return new Promise((r) => setTimeout(r, pMs)); }
|
|
75
|
+
|
|
76
|
+
async function waitForHealth(pTimeoutMs)
|
|
77
|
+
{
|
|
78
|
+
let tmpDeadline = Date.now() + pTimeoutMs;
|
|
79
|
+
while (Date.now() < tmpDeadline)
|
|
80
|
+
{
|
|
81
|
+
try { let r = await httpJSON('GET', '/clone/sync/status', null); if (r.status === 200) { return true; } }
|
|
82
|
+
catch (e) { /* not up yet */ }
|
|
83
|
+
await sleep(250);
|
|
84
|
+
}
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function sqlcmd(pSQL)
|
|
89
|
+
{
|
|
90
|
+
let tmpOut = libChildProcess.execFileSync('docker',
|
|
91
|
+
[ 'exec', MSSQL_CONTAINER, SQLCMD, '-S', 'localhost', '-U', 'sa', '-P', MSSQL_CONFIG.password, '-C', '-d', MSSQL_CONFIG.database, '-h', '-1', '-Q', pSQL ],
|
|
92
|
+
{ encoding: 'utf8' });
|
|
93
|
+
return tmpOut;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Expected operational index names for a table, straight from the policy.
|
|
97
|
+
function expectedIndexNames(pTableName)
|
|
98
|
+
{
|
|
99
|
+
return libIndexPolicy.resolveDesiredIndexes(MODEL.Tables[pTableName], {}).map((pIndex) => pIndex.Name);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function main()
|
|
103
|
+
{
|
|
104
|
+
let tmpTempDir = libPath.join(libOs.tmpdir(), `rds-converge-smoke-${PORT}`);
|
|
105
|
+
libFs.rmSync(tmpTempDir, { recursive: true, force: true }); // fresh SQLite file per run
|
|
106
|
+
libFs.mkdirSync(tmpTempDir, { recursive: true });
|
|
107
|
+
|
|
108
|
+
console.log(`Spawning data-cloner on :${PORT} (provider target: ${PROVIDER})...\n`);
|
|
109
|
+
let tmpChild = libChildProcess.spawn('node',
|
|
110
|
+
[ libPath.resolve(__dirname, '..', 'bin', 'retold-data-service-clone.js'), '--port', String(PORT) ],
|
|
111
|
+
{ cwd: tmpTempDir, env: process.env, stdio: [ 'ignore', 'ignore', 'pipe' ] });
|
|
112
|
+
let tmpStderr = '';
|
|
113
|
+
tmpChild.stderr.on('data', (d) => { tmpStderr += d.toString(); });
|
|
114
|
+
|
|
115
|
+
try
|
|
116
|
+
{
|
|
117
|
+
let tmpHealthy = await waitForHealth(20000);
|
|
118
|
+
check(tmpHealthy, 'service came up healthy (GET /clone/sync/status)');
|
|
119
|
+
if (!tmpHealthy) { if (tmpStderr) { console.log('--- stderr ---\n' + tmpStderr.slice(-1200)); } return; }
|
|
120
|
+
|
|
121
|
+
// -- (MSSQL only) fresh DB + runtime-configure the connection --
|
|
122
|
+
if (PROVIDER === 'MSSQL')
|
|
123
|
+
{
|
|
124
|
+
libChildProcess.execFileSync('docker', [ 'exec', MSSQL_CONTAINER, SQLCMD, '-S', 'localhost', '-U', 'sa', '-P', MSSQL_CONFIG.password, '-C', '-Q',
|
|
125
|
+
"IF DB_ID('clonesmoke') IS NOT NULL BEGIN ALTER DATABASE clonesmoke SET SINGLE_USER WITH ROLLBACK IMMEDIATE; DROP DATABASE clonesmoke; END; CREATE DATABASE clonesmoke;" ], { encoding: 'utf8' });
|
|
126
|
+
let tmpCfg = await httpJSON('POST', '/clone/connection/configure', { Provider: 'MSSQL', Config: MSSQL_CONFIG });
|
|
127
|
+
check(tmpCfg.status === 200 && tmpCfg.body.Success, 'configured MSSQL connection');
|
|
128
|
+
let tmpStatus = await httpJSON('GET', '/clone/connection/status', null);
|
|
129
|
+
check(tmpStatus.body.Provider === 'MSSQL', `active provider is MSSQL (got ${tmpStatus.body.Provider})`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// -- fetch (raw schema object) → deploy --
|
|
133
|
+
let tmpFetch = await httpJSON('POST', '/clone/schema/fetch', { Schema: MODEL });
|
|
134
|
+
check(tmpFetch.status === 200 && tmpFetch.body.Success, `fetched schema (${tmpFetch.body.TableCount} tables)`);
|
|
135
|
+
|
|
136
|
+
let tmpDeploy = await httpJSON('POST', '/clone/schema/deploy', { Tables: DEPLOY_TABLES });
|
|
137
|
+
check(tmpDeploy.status === 200 && tmpDeploy.body.Success, 'deployed tables');
|
|
138
|
+
check(DEPLOY_TABLES.every((t) => (tmpDeploy.body.TablesDeployed || []).indexOf(t) > -1), `all ${DEPLOY_TABLES.length} tables deployed [${(tmpDeploy.body.TablesDeployed || []).join(', ')}]`);
|
|
139
|
+
|
|
140
|
+
// -- converge #1 --
|
|
141
|
+
let tmpConv1 = await httpJSON('POST', '/clone/schema/indices/converge', { Tables: DEPLOY_TABLES, IndexPolicy: { PruneScope: 'managed' } });
|
|
142
|
+
check(tmpConv1.status === 200 && tmpConv1.body.Success, 'converge #1 succeeded');
|
|
143
|
+
|
|
144
|
+
let tmpAllCreated = (tmpConv1.body.Results || []).reduce((pAcc, pR) => pAcc.concat(pR.created || []), []);
|
|
145
|
+
let tmpExpected = DEPLOY_TABLES.reduce((pAcc, t) => pAcc.concat(expectedIndexNames(t)), []);
|
|
146
|
+
console.log(` created: [${tmpAllCreated.join(', ')}]`);
|
|
147
|
+
check(tmpExpected.every((n) => tmpAllCreated.indexOf(n) > -1), `all policy-declared indexes created [${tmpExpected.join(', ')}]`);
|
|
148
|
+
check(tmpAllCreated.length === tmpExpected.length && tmpAllCreated.slice().sort().join(',') === tmpExpected.slice().sort().join(','),
|
|
149
|
+
`created set exactly matches the policy (${tmpAllCreated.length} indexes, no extras)`);
|
|
150
|
+
// Deleted-gated composite: BookAuthorJoin has a GUID column but no Deleted →
|
|
151
|
+
// it should get the GUID lookup index but NO (Deleted, ID) composite.
|
|
152
|
+
check(tmpAllCreated.indexOf('IX_M_SYNC_BookAuthorJoin_GUIDBookAuthorJoin') > -1, 'GUID-only join table got its GUID index');
|
|
153
|
+
check(!tmpAllCreated.some((n) => n.indexOf('IX_M_SYNC_BookAuthorJoin_Deleted_') === 0), 'GUID-only join table got NO composite (composite correctly gated on Deleted)');
|
|
154
|
+
|
|
155
|
+
// -- converge #2 (idempotency) --
|
|
156
|
+
let tmpConv2 = await httpJSON('POST', '/clone/schema/indices/converge', { Tables: DEPLOY_TABLES, IndexPolicy: { PruneScope: 'managed' } });
|
|
157
|
+
let tmpCreated2 = (tmpConv2.body.Results || []).reduce((pAcc, pR) => pAcc + (pR.created || []).length, 0);
|
|
158
|
+
let tmpDropped2 = (tmpConv2.body.Results || []).reduce((pAcc, pR) => pAcc + (pR.dropped || []).length, 0);
|
|
159
|
+
check(tmpConv2.body.Success && tmpCreated2 === 0 && tmpDropped2 === 0, `idempotent — converge #2 created ${tmpCreated2}, dropped ${tmpDropped2}`);
|
|
160
|
+
|
|
161
|
+
// -- MSSQL: independent sqlcmd cross-check + managed-prune-through-the-endpoint --
|
|
162
|
+
if (PROVIDER === 'MSSQL')
|
|
163
|
+
{
|
|
164
|
+
let tmpIdxRows = sqlcmd("SET NOCOUNT ON; SELECT i.name + '|' + CAST(i.is_unique AS VARCHAR) FROM sys.indexes i JOIN sys.tables t ON t.object_id=i.object_id WHERE t.name='Book' AND i.type>0 AND i.is_primary_key=0;");
|
|
165
|
+
let tmpNames = tmpIdxRows.split('\n').map((s) => s.trim()).filter(Boolean);
|
|
166
|
+
check(tmpNames.some((r) => r.startsWith('IX_M_SYNC_Book_Deleted_') && r.endsWith('|0')), `sqlcmd: Book composite present + NON-unique (${tmpNames.join(' ')})`);
|
|
167
|
+
check(tmpNames.some((r) => r === 'IX_M_SYNC_Book_GUIDBook|0'), 'sqlcmd: Book GUID index present + NON-unique');
|
|
168
|
+
|
|
169
|
+
// Seed a precursor [Deleted] single-column index out-of-band, then converge
|
|
170
|
+
// again — managed prune should drop it (it names a column) via the endpoint.
|
|
171
|
+
sqlcmd("IF NOT EXISTS(SELECT * FROM sys.indexes WHERE name='Deleted' AND object_id=OBJECT_ID('dbo.Book')) CREATE INDEX [Deleted] ON [dbo].[Book]([Deleted]);");
|
|
172
|
+
let tmpConv3 = await httpJSON('POST', '/clone/schema/indices/converge', { Tables: [ 'Book' ], IndexPolicy: { PruneScope: 'managed' } });
|
|
173
|
+
let tmpDropped3 = (tmpConv3.body.Results || []).reduce((pAcc, pR) => pAcc.concat(pR.dropped || []), []);
|
|
174
|
+
check(tmpConv3.body.Success && tmpDropped3.indexOf('Deleted') > -1, `managed prune via endpoint dropped precursor [Deleted] (dropped: [${tmpDropped3.join(', ')}])`);
|
|
175
|
+
let tmpAfter = sqlcmd("SET NOCOUNT ON; SELECT name FROM sys.indexes WHERE object_id=OBJECT_ID('dbo.Book') AND type>0 AND is_primary_key=0;").split('\n').map((s) => s.trim()).filter(Boolean);
|
|
176
|
+
check(tmpAfter.indexOf('Deleted') === -1 && tmpAfter.some((n) => n.indexOf('IX_M_SYNC_Book') === 0), `sqlcmd: precursor gone, operational indexes remain [${tmpAfter.join(', ')}]`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
finally
|
|
180
|
+
{
|
|
181
|
+
tmpChild.kill('SIGTERM');
|
|
182
|
+
await sleep(300);
|
|
183
|
+
try { tmpChild.kill('SIGKILL'); } catch (e) {}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
console.log(`\n──────── ${_pass} ok, ${_fail} fail (${PROVIDER}) ────────`);
|
|
187
|
+
process.exit(_fail > 0 ? 1 : 0);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
main().catch((pErr) => { console.error('harness error:', pErr); process.exit(2); });
|
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
|
-
}
|