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.
Files changed (84) hide show
  1. package/.github/workflows/publish-image.yml +2 -2
  2. package/BUILDING-AND-PUBLISHING.md +6 -6
  3. package/CONTRIBUTING.md +1 -1
  4. package/Dockerfile +1 -1
  5. package/README.md +12 -27
  6. package/build-all.js +66 -0
  7. package/diagrams/architecture.excalidraw +2966 -0
  8. package/diagrams/architecture.mmd +17 -0
  9. package/diagrams/architecture.svg +2 -0
  10. package/docs/README.md +12 -12
  11. package/docs/_brand.json +18 -0
  12. package/docs/_cover.md +1 -1
  13. package/docs/_topbar.md +1 -1
  14. package/docs/_version.json +3 -3
  15. package/docs/api/reference.md +8 -8
  16. package/docs/architecture.md +6 -84
  17. package/docs/diagrams/component-diagram.excalidraw +2807 -0
  18. package/docs/diagrams/component-diagram.mmd +14 -0
  19. package/docs/diagrams/component-diagram.svg +2 -0
  20. package/docs/diagrams/component-stack.excalidraw +1169 -0
  21. package/docs/diagrams/component-stack.mmd +6 -0
  22. package/docs/diagrams/component-stack.svg +2 -0
  23. package/docs/diagrams/hook-execution-order.excalidraw +3230 -0
  24. package/docs/diagrams/hook-execution-order.mmd +19 -0
  25. package/docs/diagrams/hook-execution-order.svg +2 -0
  26. package/docs/diagrams/initialization-flow.excalidraw +1800 -0
  27. package/docs/diagrams/initialization-flow.mmd +22 -0
  28. package/docs/diagrams/initialization-flow.svg +2 -0
  29. package/docs/index.html +6 -7
  30. package/docs/lifecycle-hooks.md +2 -21
  31. package/docs/retold-catalog.json +141 -141
  32. package/docs/retold-keyword-index.json +6818 -1608
  33. package/package.json +130 -96
  34. package/source/services/RetoldDataService-Brand.js +13 -0
  35. package/source/services/comprehension-loader/pict-app/Pict-Application-ComprehensionLoader.js +65 -15
  36. package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-Layout.js +28 -74
  37. package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-Load.js +17 -17
  38. package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-SettingsPanel.js +62 -0
  39. package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-Shell.js +142 -0
  40. package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-StatusBar.js +125 -0
  41. package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-StatusDetail.js +89 -0
  42. package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-TopBar-Nav.js +42 -0
  43. package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-TopBar-User.js +48 -0
  44. package/source/services/comprehension-loader/web/comprehension-loader.js +5517 -6183
  45. package/source/services/comprehension-loader/web/comprehension-loader.js.map +1 -1
  46. package/source/services/comprehension-loader/web/comprehension-loader.min.js +75 -1
  47. package/source/services/comprehension-loader/web/comprehension-loader.min.js.map +1 -1
  48. package/source/services/comprehension-loader/web/favicons/favicon-dark.svg +13 -0
  49. package/source/services/comprehension-loader/web/favicons/favicon-light.svg +13 -0
  50. package/source/services/comprehension-loader/web/favicons/favicon.svg +13 -0
  51. package/source/services/comprehension-loader/web/index.html +3 -0
  52. package/source/services/comprehension-loader/web/pict.min.js +12 -0
  53. package/source/services/data-cloner/DataCloner-Command-Headless.js +102 -4
  54. package/source/services/data-cloner/DataCloner-Command-Schema.js +105 -8
  55. package/source/services/data-cloner/DataCloner-Command-Sync.js +111 -75
  56. package/source/services/data-cloner/pict-app/Pict-Application-DataCloner.js +70 -47
  57. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Export.js +3 -3
  58. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Layout.js +40 -86
  59. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-SettingsPanel.js +61 -0
  60. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Shell.js +136 -0
  61. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-StatusBar.js +117 -0
  62. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-StatusDetail.js +81 -0
  63. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Sync.js +18 -18
  64. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-TopBar-Nav.js +42 -0
  65. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-TopBar-User.js +48 -0
  66. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-ViewData.js +2 -2
  67. package/source/services/data-cloner/web/data-cloner.js +5874 -7986
  68. package/source/services/data-cloner/web/data-cloner.js.map +1 -1
  69. package/source/services/data-cloner/web/data-cloner.min.js +75 -1
  70. package/source/services/data-cloner/web/data-cloner.min.js.map +1 -1
  71. package/source/services/data-cloner/web/favicons/favicon-dark.svg +13 -0
  72. package/source/services/data-cloner/web/favicons/favicon-light.svg +13 -0
  73. package/source/services/data-cloner/web/favicons/favicon.svg +13 -0
  74. package/source/services/data-cloner/web/favicons/favicons/favicon-dark.svg +13 -0
  75. package/source/services/data-cloner/web/favicons/favicons/favicon-light.svg +13 -0
  76. package/source/services/data-cloner/web/favicons/favicons/favicon.svg +13 -0
  77. package/source/services/data-cloner/web/index.html +3 -0
  78. package/source/services/data-cloner/web/pict.min.js +12 -0
  79. package/test/Bundles_smoke_tests.js +43 -0
  80. package/test/ComprehensionLoader_smoke_tests.js +95 -0
  81. package/test/DataCloner-RuntimeOverrides_tests.js +344 -0
  82. package/test/DataCloner_smoke_tests.js +87 -0
  83. package/tools/smoke-converge-endpoint.js +190 -0
  84. package/docs/css/docuserve.css +0 -327
@@ -0,0 +1,136 @@
1
+ 'use strict';
2
+
3
+ const libPictView = require('pict-view');
4
+
5
+ class DataClonerShellView extends libPictView
6
+ {
7
+ constructor(pFable, pOptions, pServiceHash)
8
+ {
9
+ super(pFable, pOptions, pServiceHash);
10
+ this._shellPanelsBuilt = false;
11
+ }
12
+
13
+ onAfterRender(pRenderable, pAddress, pRecord, pContent)
14
+ {
15
+ if (this.pict.CSSMap) { this.pict.CSSMap.injectCSS(); }
16
+ if (!this._shellPanelsBuilt)
17
+ {
18
+ this._buildShell();
19
+ this._shellPanelsBuilt = true;
20
+ }
21
+ return super.onAfterRender(pRenderable, pAddress, pRecord, pContent);
22
+ }
23
+
24
+ _buildShell()
25
+ {
26
+ let tmpModal = this.pict.views['Pict-Section-Modal'];
27
+ let tmpMount = document.getElementById('DataCloner-Shell-Mount');
28
+ if (!tmpModal || typeof tmpModal.shell !== 'function' || !tmpMount)
29
+ {
30
+ this.log.warn('DataCloner-Shell: Pict-Section-Modal or mount not available; shell not built.');
31
+ return;
32
+ }
33
+
34
+ this._shell = tmpModal.shell(tmpMount, { PersistenceKey: 'data-cloner-shell' });
35
+
36
+ this._shell.addPanel({
37
+ Hash: 'topbar', Side: 'top', Mode: 'fixed', Size: 56,
38
+ ContentDestinationId: 'Theme-TopBar', ContentView: 'Theme-TopBar'
39
+ });
40
+
41
+ this._shell.addPanel({
42
+ Hash: 'bottombar', Side: 'bottom', Mode: 'fixed', Size: 36,
43
+ ContentDestinationId: 'Theme-BottomBar', ContentView: 'Theme-BottomBar'
44
+ });
45
+
46
+ this._shell.addPanel({
47
+ Hash: 'status-detail', Side: 'bottom', Mode: 'resizable', Position: 'overlay',
48
+ Size: 320, MinSize: 200, MaxSize: 480,
49
+ Hidden: true, Collapsed: true, Title: 'Status Detail',
50
+ ContentDestinationId: 'DataCloner-StatusDetail-Panel',
51
+ ContentView: 'DataCloner-StatusDetail'
52
+ });
53
+
54
+ this._shell.addPanel({
55
+ Hash: 'settings', Side: 'right', Mode: 'resizable', Position: 'overlay',
56
+ Size: 360, MinSize: 280, MaxSize: 540,
57
+ Hidden: true, Collapsed: true, Title: 'Settings',
58
+ ContentDestinationId: 'DataCloner-Settings-Panel',
59
+ ContentView: 'DataCloner-SettingsPanel'
60
+ });
61
+
62
+ this._shell.center({ ContentDestinationId: 'DataCloner-Workspace' });
63
+ }
64
+
65
+ getSettingsPanel() { return this._shell ? this._shell.getPanel('settings') : null; }
66
+ getStatusDetailPanel() { return this._shell ? this._shell.getPanel('status-detail') : null; }
67
+
68
+ toggleSettingsPanel()
69
+ {
70
+ let tmpPanel = this.getSettingsPanel();
71
+ if (tmpPanel) { tmpPanel.toggle(); }
72
+ }
73
+
74
+ toggleStatusDetail()
75
+ {
76
+ let tmpPanel = this.getStatusDetailPanel();
77
+ if (!tmpPanel) { return; }
78
+ tmpPanel.toggle();
79
+
80
+ let tmpProvider = this.pict.providers.DataCloner;
81
+ let tmpOpen = !tmpPanel.Collapsed;
82
+ if (tmpProvider && tmpOpen && typeof tmpProvider.onStatusDetailExpanded === 'function')
83
+ {
84
+ tmpProvider.onStatusDetailExpanded();
85
+ }
86
+ else if (tmpProvider && !tmpOpen && typeof tmpProvider.onStatusDetailCollapsed === 'function')
87
+ {
88
+ tmpProvider.onStatusDetailCollapsed();
89
+ }
90
+ }
91
+
92
+ renderTopBar()
93
+ {
94
+ let tmpNav = this.pict.views['DataCloner-TopBar-Nav'];
95
+ let tmpUser = this.pict.views['DataCloner-TopBar-User'];
96
+ if (tmpNav) { tmpNav.render(); }
97
+ if (tmpUser) { tmpUser.render(); }
98
+ }
99
+ }
100
+
101
+ module.exports = DataClonerShellView;
102
+
103
+ module.exports.default_configuration =
104
+ {
105
+ ViewIdentifier: 'DataCloner-Shell',
106
+ DefaultRenderable: 'DataCloner-Shell',
107
+ DefaultDestinationAddress: '#DataCloner-Application-Container',
108
+ AutoRender: false,
109
+ CSS: /*css*/`
110
+ html, body { height: 100%; margin: 0; padding: 0; }
111
+ body {
112
+ background: var(--theme-color-background-primary, #f5f5f5);
113
+ color: var(--theme-color-text-primary, #333333);
114
+ font-family: var(--theme-typography-family-sans,
115
+ -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif);
116
+ }
117
+ #DataCloner-Application-Container { height: 100%; min-height: 0; overflow: hidden; }
118
+ .pict-modal-shell-host { height: 100%; }
119
+ .pict-modal-shell { background: var(--theme-color-background-primary, #f5f5f5); }
120
+ .pict-modal-shell-panel { background: var(--theme-color-background-panel, #ffffff); }
121
+ .pict-modal-shell-center {
122
+ background: var(--theme-color-background-primary, #f5f5f5);
123
+ overflow: auto;
124
+ padding: 20px;
125
+ }
126
+ `,
127
+ Templates: [{
128
+ Hash: 'DataCloner-Shell',
129
+ Template: /*html*/`<div id="DataCloner-Shell-Mount" style="height:100%"></div>`
130
+ }],
131
+ Renderables: [{
132
+ RenderableHash: 'DataCloner-Shell',
133
+ TemplateHash: 'DataCloner-Shell',
134
+ DestinationAddress: '#DataCloner-Application-Container'
135
+ }]
136
+ };
@@ -0,0 +1,117 @@
1
+ 'use strict';
2
+
3
+ const libPictView = require('pict-view');
4
+
5
+ class DataClonerStatusBarView extends libPictView
6
+ {
7
+ onAfterRender(pRenderable, pAddress, pRecord, pContent)
8
+ {
9
+ if (this.pict.CSSMap) { this.pict.CSSMap.injectCSS(); }
10
+ return super.onAfterRender(pRenderable, pAddress, pRecord, pContent);
11
+ }
12
+ }
13
+
14
+ module.exports = DataClonerStatusBarView;
15
+
16
+ module.exports.default_configuration =
17
+ {
18
+ ViewIdentifier: 'DataCloner-StatusBar',
19
+ DefaultRenderable: 'DataCloner-StatusBar',
20
+ DefaultDestinationAddress: '#Theme-BottomBar-Status',
21
+ AutoRender: false,
22
+ CSS: /*css*/`
23
+ .rds-status-bar {
24
+ display: flex; align-items: center; gap: 12px;
25
+ height: 100%; padding: 0 12px;
26
+ color: var(--theme-color-text-primary, #333333);
27
+ font-size: 0.88em;
28
+ border-top: 1px solid var(--theme-color-border-light, #e9e9e9);
29
+ border-left: 3px solid var(--theme-color-text-secondary, #6c757d);
30
+ position: relative;
31
+ }
32
+ .rds-status-bar.phase-idle { border-left-color: var(--theme-color-text-secondary, #6c757d); }
33
+ .rds-status-bar.phase-disconnected { border-left-color: var(--theme-color-status-error, #dc3545); }
34
+ .rds-status-bar.phase-ready { border-left-color: var(--theme-color-brand-primary, #4a90d9); }
35
+ .rds-status-bar.phase-loading { border-left-color: var(--theme-color-status-success, #28a745); }
36
+ .rds-status-bar.phase-stopping { border-left-color: var(--theme-color-status-warning, #ffc107); }
37
+ .rds-status-bar.phase-complete { border-left-color: var(--theme-color-status-success, #28a745); }
38
+
39
+ .rds-status-dot {
40
+ width: 10px; height: 10px; border-radius: 50%;
41
+ flex-shrink: 0;
42
+ background: var(--theme-color-text-secondary, #6c757d);
43
+ }
44
+ .rds-status-bar.phase-idle .rds-status-dot { background: var(--theme-color-text-secondary, #6c757d); }
45
+ .rds-status-bar.phase-disconnected .rds-status-dot { background: var(--theme-color-status-error, #dc3545); }
46
+ .rds-status-bar.phase-ready .rds-status-dot { background: var(--theme-color-brand-primary, #4a90d9); }
47
+ .rds-status-bar.phase-loading .rds-status-dot {
48
+ background: var(--theme-color-status-success, #28a745);
49
+ animation: rds-status-pulse 1.5s ease-in-out infinite;
50
+ }
51
+ .rds-status-bar.phase-stopping .rds-status-dot {
52
+ background: var(--theme-color-status-warning, #ffc107);
53
+ animation: rds-status-pulse 0.8s ease-in-out infinite;
54
+ }
55
+ .rds-status-bar.phase-complete .rds-status-dot { background: var(--theme-color-status-success, #28a745); }
56
+
57
+ @keyframes rds-status-pulse {
58
+ 0%, 100% { opacity: 1; transform: scale(1); }
59
+ 50% { opacity: 0.4; transform: scale(0.8); }
60
+ }
61
+
62
+ .rds-status-message { flex: 1; line-height: 1.2; }
63
+ .rds-status-meta {
64
+ display: flex; gap: 12px; flex-shrink: 0;
65
+ font-size: 0.92em; color: var(--theme-color-text-secondary, #666);
66
+ }
67
+ .rds-status-meta .live-status-meta-item { white-space: nowrap; }
68
+ .rds-status-meta .live-status-meta-item strong { color: var(--theme-color-text-primary, #333); }
69
+
70
+ .rds-status-detail-btn {
71
+ padding: 2px 8px;
72
+ background: transparent;
73
+ border: 1px solid var(--theme-color-border-default, #ccc);
74
+ border-radius: 3px;
75
+ color: var(--theme-color-text-secondary, #666);
76
+ cursor: pointer;
77
+ font-size: 0.92em;
78
+ line-height: 1;
79
+ }
80
+ .rds-status-detail-btn:hover {
81
+ background: var(--theme-color-background-hover, #f0f0f0);
82
+ color: var(--theme-color-text-primary, #333);
83
+ }
84
+
85
+ .rds-status-progress-bar {
86
+ position: absolute; left: 0; right: 0; bottom: 0;
87
+ height: 2px;
88
+ background: var(--theme-color-background-tertiary, #e9ecef);
89
+ overflow: hidden;
90
+ }
91
+ .rds-status-progress-fill {
92
+ height: 100%;
93
+ background: var(--theme-color-status-success, #28a745);
94
+ transition: width 1s ease;
95
+ }
96
+ `,
97
+ Templates: [{
98
+ Hash: 'DataCloner-StatusBar',
99
+ Template: /*html*/`
100
+ <div id="liveStatusBar" class="rds-status-bar phase-idle">
101
+ <div class="rds-status-dot live-status-dot"></div>
102
+ <div id="liveStatusMessage" class="rds-status-message live-status-message">Idle</div>
103
+ <div id="liveStatusMeta" class="rds-status-meta live-status-meta"></div>
104
+ <button class="rds-status-detail-btn"
105
+ onclick="_Pict.views['DataCloner-Shell'].toggleStatusDetail()"
106
+ title="Show detail">Detail</button>
107
+ <div class="rds-status-progress-bar live-status-progress-bar">
108
+ <div id="liveStatusProgressFill" class="rds-status-progress-fill live-status-progress-fill" style="width:0%"></div>
109
+ </div>
110
+ </div>`
111
+ }],
112
+ Renderables: [{
113
+ RenderableHash: 'DataCloner-StatusBar',
114
+ TemplateHash: 'DataCloner-StatusBar',
115
+ DestinationAddress: '#Theme-BottomBar-Status'
116
+ }]
117
+ };
@@ -0,0 +1,81 @@
1
+ 'use strict';
2
+
3
+ const libPictView = require('pict-view');
4
+
5
+ class DataClonerStatusDetailView extends libPictView
6
+ {
7
+ onAfterRender(pRenderable, pAddress, pRecord, pContent)
8
+ {
9
+ if (this.pict.CSSMap) { this.pict.CSSMap.injectCSS(); }
10
+ return super.onAfterRender(pRenderable, pAddress, pRecord, pContent);
11
+ }
12
+ }
13
+
14
+ module.exports = DataClonerStatusDetailView;
15
+
16
+ module.exports.default_configuration =
17
+ {
18
+ ViewIdentifier: 'DataCloner-StatusDetail',
19
+ DefaultRenderable: 'DataCloner-StatusDetail',
20
+ DefaultDestinationAddress: '#DataCloner-StatusDetail-Panel',
21
+ AutoRender: false,
22
+ CSS: /*css*/`
23
+ .rds-status-detail-body {
24
+ padding: 12px 20px 16px;
25
+ max-height: 100%;
26
+ overflow-y: auto;
27
+ color: var(--theme-color-text-primary, #333);
28
+ }
29
+ .rds-status-detail-section { margin-bottom: 14px; }
30
+ .rds-status-detail-section:last-child { margin-bottom: 0; }
31
+ .rds-status-detail-section-title {
32
+ font-size: 0.85em; font-weight: 600;
33
+ color: var(--theme-color-text-secondary, #555);
34
+ text-transform: uppercase; letter-spacing: 0.5px;
35
+ margin-bottom: 8px; padding-bottom: 4px;
36
+ border-bottom: 1px solid var(--theme-color-border-light, #eee);
37
+ }
38
+ .running-op-row { display: flex; align-items: center; gap: 12px; padding: 6px 0; font-size: 0.9em; }
39
+ .running-op-name { font-weight: 600; min-width: 180px; }
40
+ .running-op-bar {
41
+ flex: 1; height: 8px;
42
+ background: var(--theme-color-background-tertiary, #e9ecef);
43
+ border-radius: 4px; overflow: hidden; min-width: 120px;
44
+ }
45
+ .running-op-bar-fill {
46
+ height: 100%;
47
+ background: var(--theme-color-brand-primary, #4a90d9);
48
+ transition: width 0.5s ease;
49
+ }
50
+ .running-op-count { font-size: 0.85em; color: var(--theme-color-text-secondary, #666); white-space: nowrap; }
51
+ .running-op-pending { color: var(--theme-color-text-muted, #888); font-size: 0.85em; font-style: italic; padding: 4px 0; }
52
+ .completed-op-row { padding: 8px 0; border-bottom: 1px solid var(--theme-color-background-tertiary, #f0f0f0); }
53
+ .completed-op-row:last-child { border-bottom: none; }
54
+ .completed-op-header { display: flex; align-items: center; gap: 10px; font-size: 0.9em; margin-bottom: 4px; }
55
+ .completed-op-name { font-weight: 600; }
56
+ .completed-op-stats { color: var(--theme-color-text-secondary, #666); font-size: 0.85em; }
57
+ .completed-op-checkmark { color: var(--theme-color-status-success, #28a745); }
58
+ .error-op-row { padding: 6px 0; border-bottom: 1px solid var(--theme-color-background-tertiary, #f0f0f0); font-size: 0.9em; }
59
+ .error-op-row:last-child { border-bottom: none; }
60
+ .error-op-header { display: flex; align-items: center; gap: 8px; }
61
+ .error-op-name { font-weight: 600; color: var(--theme-color-status-error, #dc3545); }
62
+ .error-op-status { font-size: 0.82em; color: var(--theme-color-status-error, #dc3545); }
63
+ .error-op-message { font-size: 0.82em; color: var(--theme-color-text-muted, #888); margin-top: 2px; padding-left: 18px; }
64
+ `,
65
+ Templates: [{
66
+ Hash: 'DataCloner-StatusDetail',
67
+ Template: /*html*/`
68
+ <div class="rds-status-detail-body">
69
+ <div class="rds-status-detail-section">
70
+ <div class="rds-status-detail-section-title">Throughput</div>
71
+ <div id="DataCloner-Throughput-Histogram" style="height:120px"></div>
72
+ </div>
73
+ <div id="DataCloner-StatusDetail-Container"></div>
74
+ </div>`
75
+ }],
76
+ Renderables: [{
77
+ RenderableHash: 'DataCloner-StatusDetail',
78
+ TemplateHash: 'DataCloner-StatusDetail',
79
+ DestinationAddress: '#DataCloner-StatusDetail-Panel'
80
+ }]
81
+ };
@@ -196,7 +196,7 @@ class DataClonerSyncView extends libPictView
196
196
  // --- Summary Cards ---
197
197
  let tmpCardsContainer = document.getElementById('reportSummaryCards');
198
198
  let tmpOutcomeClass = 'outcome-' + pReport.Outcome.toLowerCase();
199
- let tmpOutcomeColor = { Success: '#28a745', Partial: '#ffc107', Error: '#dc3545', Stopped: '#6c757d' }[pReport.Outcome] || '#666';
199
+ let tmpOutcomeColor = { Success: 'var(--theme-color-status-success, #28a745)', Partial: 'var(--theme-color-status-warning, #ffc107)', Error: 'var(--theme-color-status-error, #dc3545)', Stopped: 'var(--theme-color-text-secondary, #6c757d)' }[pReport.Outcome] || 'var(--theme-color-text-secondary, #666)';
200
200
 
201
201
  let tmpDurationSec = pReport.RunTimestamps.DurationSeconds || 0;
202
202
  let tmpDurationStr = tmpDurationSec < 60 ? tmpDurationSec + 's' : Math.floor(tmpDurationSec / 60) + 'm ' + (tmpDurationSec % 60) + 's';
@@ -235,13 +235,13 @@ class DataClonerSyncView extends libPictView
235
235
  }
236
236
  else
237
237
  {
238
- let tmpHtml = '<h4 style="margin:0 0 8px; color:#dc3545; font-size:0.95em">Anomalies (' + pReport.Anomalies.length + ')</h4>';
238
+ let tmpHtml = '<h4 style="margin:0 0 8px; color:var(--theme-color-status-error, #dc3545); font-size:0.95em">Anomalies (' + pReport.Anomalies.length + ')</h4>';
239
239
  tmpHtml += '<table class="progress-table">';
240
240
  tmpHtml += '<tr><th>Table</th><th>Type</th><th>Message</th></tr>';
241
241
  for (let i = 0; i < pReport.Anomalies.length; i++)
242
242
  {
243
243
  let tmpAnomaly = pReport.Anomalies[i];
244
- let tmpTypeColor = tmpAnomaly.Type === 'Error' ? '#dc3545' : (tmpAnomaly.Type === 'Partial' ? '#ffc107' : '#6c757d');
244
+ let tmpTypeColor = tmpAnomaly.Type === 'Error' ? 'var(--theme-color-status-error, #dc3545)' : (tmpAnomaly.Type === 'Partial' ? 'var(--theme-color-status-warning, #ffc107)' : 'var(--theme-color-text-secondary, #6c757d)');
245
245
  tmpHtml += '<tr>';
246
246
  tmpHtml += '<td><strong>' + this.pict.providers.DataCloner.escapeHtml(tmpAnomaly.Table) + '</strong></td>';
247
247
  tmpHtml += '<td style="color:' + tmpTypeColor + '">' + tmpAnomaly.Type + '</td>';
@@ -265,7 +265,7 @@ class DataClonerSyncView extends libPictView
265
265
  let tmpTable = pReport.Tables[i];
266
266
  let tmpDur = tmpTable.DurationSeconds < 60 ? tmpTable.DurationSeconds + 's' : Math.floor(tmpTable.DurationSeconds / 60) + 'm ' + (tmpTable.DurationSeconds % 60) + 's';
267
267
  let tmpRecs = tmpTable.Total.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
268
- let tmpStatusColor = { Complete: '#28a745', Error: '#dc3545', Partial: '#ffc107' }[tmpTable.Status] || '#666';
268
+ let tmpStatusColor = { Complete: 'var(--theme-color-status-success, #28a745)', Error: 'var(--theme-color-status-error, #dc3545)', Partial: 'var(--theme-color-status-warning, #ffc107)' }[tmpTable.Status] || 'var(--theme-color-text-secondary, #666)';
269
269
  tmpHtml += '<tr>';
270
270
  tmpHtml += '<td><strong>' + this.pict.providers.DataCloner.escapeHtml(tmpTable.Name) + '</strong></td>';
271
271
  tmpHtml += '<td>' + tmpDur + '</td>';
@@ -369,11 +369,11 @@ class DataClonerSyncView extends libPictView
369
369
  }
370
370
 
371
371
  // Color the progress bar based on status
372
- let tmpBarColor = '#28a745'; // green
373
- if (pTable.Status === 'Error') tmpBarColor = '#dc3545';
374
- else if (pTable.Status === 'Partial') tmpBarColor = '#ffc107';
375
- else if (pTable.Status === 'Syncing') tmpBarColor = '#4a90d9';
376
- else if (pTable.Status === 'Pending') tmpBarColor = '#adb5bd';
372
+ let tmpBarColor = 'var(--theme-color-status-success, #28a745)'; // green
373
+ if (pTable.Status === 'Error') tmpBarColor = 'var(--theme-color-status-error, #dc3545)';
374
+ else if (pTable.Status === 'Partial') tmpBarColor = 'var(--theme-color-status-warning, #ffc107)';
375
+ else if (pTable.Status === 'Syncing') tmpBarColor = 'var(--theme-color-brand-primary, #4a90d9)';
376
+ else if (pTable.Status === 'Pending') tmpBarColor = 'var(--theme-color-text-muted, #adb5bd)';
377
377
 
378
378
  // Status badge
379
379
  let tmpStatusBadge = pTable.Status;
@@ -482,24 +482,24 @@ module.exports.default_configuration =
482
482
  CSS: /*css*/`
483
483
  .progress-table { width: 100%; border-collapse: collapse; margin-top: 4px; margin-bottom: 4px; }
484
484
  .progress-table th, .progress-table td { text-align: left; padding: 6px 12px; border-bottom: 1px solid var(--theme-color-border-light, #eee); font-size: 0.9em; }
485
- .progress-table th { background: #f8f9fa; font-weight: 600; }
486
- .progress-table-muted td { color: var(--theme-color-text-muted, #888); padding: 3px 12px; font-size: 0.85em; border-bottom: 1px solid #f4f5f6; }
487
- .progress-bar-container { width: 120px; height: 16px; background: #e9ecef; border-radius: 8px; overflow: hidden; display: inline-block; vertical-align: middle; }
485
+ .progress-table th { background: var(--theme-color-background-secondary, #f8f9fa); font-weight: 600; }
486
+ .progress-table-muted td { color: var(--theme-color-text-muted, #888); padding: 3px 12px; font-size: 0.85em; border-bottom: 1px solid var(--theme-color-background-secondary, #f4f5f6); }
487
+ .progress-bar-container { width: 120px; height: 16px; background: var(--theme-color-background-tertiary, #e9ecef); border-radius: 8px; overflow: hidden; display: inline-block; vertical-align: middle; }
488
488
  .progress-bar-fill { height: 100%; background: var(--theme-color-status-success, #28a745); transition: width 0.3s; }
489
- .sync-section-header { font-size: 0.8em; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; color: #4a90d9; padding: 10px 0 2px 0; margin-top: 6px; border-top: 1px solid var(--theme-color-border-default, #e0e0e0); }
489
+ .sync-section-header { font-size: 0.8em; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; color: var(--theme-color-brand-primary, #4a90d9); padding: 10px 0 2px 0; margin-top: 6px; border-top: 1px solid var(--theme-color-border-default, #e0e0e0); }
490
490
  .sync-section-header:first-child { border-top: none; margin-top: 10px; }
491
- .sync-section-header-error { color: #dc3545; }
491
+ .sync-section-header-error { color: var(--theme-color-status-error, #dc3545); }
492
492
  .sync-section-header-ok { color: var(--theme-color-status-success, #28a745); }
493
493
  .sync-section-count { font-weight: 400; color: var(--theme-color-text-muted, #999); font-size: 0.95em; }
494
494
  .sync-section-overflow { font-size: 0.8em; color: var(--theme-color-text-muted, #aaa); padding: 2px 12px 6px; }
495
495
  .sync-pending-count { text-align: right; color: var(--theme-color-text-muted, #aaa); font-size: 0.85em; }
496
- .report-card { background: #f8f9fa; border-radius: 8px; padding: 12px 16px; min-width: 140px; text-align: center; border: 1px solid #e9ecef; }
496
+ .report-card { background: var(--theme-color-background-secondary, #f8f9fa); border-radius: 8px; padding: 12px 16px; min-width: 140px; text-align: center; border: 1px solid var(--theme-color-background-tertiary, #e9ecef); }
497
497
  .report-card .card-label { font-size: 0.8em; color: var(--theme-color-text-secondary, #666); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 4px; }
498
498
  .report-card .card-value { font-size: 1.4em; font-weight: 700; }
499
499
  .report-card.outcome-success { border-left: 4px solid var(--theme-color-status-success, #28a745); }
500
- .report-card.outcome-partial { border-left: 4px solid #ffc107; }
501
- .report-card.outcome-error { border-left: 4px solid #dc3545; }
502
- .report-card.outcome-stopped { border-left: 4px solid #6c757d; }
500
+ .report-card.outcome-partial { border-left: 4px solid var(--theme-color-status-warning, #ffc107); }
501
+ .report-card.outcome-error { border-left: 4px solid var(--theme-color-status-error, #dc3545); }
502
+ .report-card.outcome-stopped { border-left: 4px solid var(--theme-color-text-secondary, #6c757d); }
503
503
  `,
504
504
  Templates:
505
505
  [
@@ -0,0 +1,42 @@
1
+ 'use strict';
2
+
3
+ const libPictView = require('pict-view');
4
+
5
+ class DataClonerTopBarNavView extends libPictView
6
+ {
7
+ onAfterRender(pRenderable, pAddress, pRecord, pContent)
8
+ {
9
+ if (this.pict.CSSMap) { this.pict.CSSMap.injectCSS(); }
10
+ return super.onAfterRender(pRenderable, pAddress, pRecord, pContent);
11
+ }
12
+ }
13
+
14
+ module.exports = DataClonerTopBarNavView;
15
+
16
+ module.exports.default_configuration =
17
+ {
18
+ ViewIdentifier: 'DataCloner-TopBar-Nav',
19
+ DefaultRenderable: 'DataCloner-TopBar-Nav',
20
+ DefaultDestinationAddress: '#Theme-TopBar-Nav',
21
+ AutoRender: false,
22
+ CSS: /*css*/`
23
+ .rds-nav {
24
+ display: flex; align-items: center; height: 100%;
25
+ padding: 0 12px; gap: 8px;
26
+ color: var(--theme-color-text-on-brand,
27
+ var(--theme-color-text-primary, #1a1a1a));
28
+ font-weight: 500;
29
+ }
30
+ .rds-nav-sep { opacity: 0.5; font-weight: 400; margin: 0 2px; }
31
+ .rds-nav-app { font-weight: 600; opacity: 0.95; }
32
+ `,
33
+ Templates: [{
34
+ Hash: 'DataCloner-TopBar-Nav',
35
+ Template: /*html*/`<div class="rds-nav"><span class="rds-nav-sep">·</span><span class="rds-nav-app">Data Cloner</span></div>`
36
+ }],
37
+ Renderables: [{
38
+ RenderableHash: 'DataCloner-TopBar-Nav',
39
+ TemplateHash: 'DataCloner-TopBar-Nav',
40
+ DestinationAddress: '#Theme-TopBar-Nav'
41
+ }]
42
+ };
@@ -0,0 +1,48 @@
1
+ 'use strict';
2
+
3
+ const libPictView = require('pict-view');
4
+
5
+ class DataClonerTopBarUserView extends libPictView
6
+ {
7
+ onAfterRender(pRenderable, pAddress, pRecord, pContent)
8
+ {
9
+ if (this.pict.CSSMap) { this.pict.CSSMap.injectCSS(); }
10
+ return super.onAfterRender(pRenderable, pAddress, pRecord, pContent);
11
+ }
12
+ }
13
+
14
+ module.exports = DataClonerTopBarUserView;
15
+
16
+ module.exports.default_configuration =
17
+ {
18
+ ViewIdentifier: 'DataCloner-TopBar-User',
19
+ DefaultRenderable: 'DataCloner-TopBar-User',
20
+ DefaultDestinationAddress: '#Theme-TopBar-User',
21
+ AutoRender: false,
22
+ CSS: /*css*/`
23
+ .rds-user { display: flex; align-items: center; height: 100%; gap: 8px; padding: 0 12px; }
24
+ .rds-user-btn {
25
+ padding: 4px 8px;
26
+ border: 1px solid var(--theme-color-border-default, #5E5549);
27
+ background: transparent;
28
+ color: var(--theme-color-text-on-brand,
29
+ var(--theme-color-text-secondary, #1a1a1a));
30
+ border-radius: 4px;
31
+ cursor: pointer;
32
+ display: inline-flex; align-items: center; justify-content: center;
33
+ font-size: 1em; line-height: 1;
34
+ }
35
+ .rds-user-btn:hover {
36
+ background: var(--theme-color-background-hover, rgba(255,255,255,0.08));
37
+ }
38
+ `,
39
+ Templates: [{
40
+ Hash: 'DataCloner-TopBar-User',
41
+ Template: /*html*/`<div class="rds-user"><button class="rds-user-btn" onclick="_Pict.views['DataCloner-Shell'].toggleSettingsPanel()" title="Settings" aria-label="Settings">{~I:Settings~}</button></div>`
42
+ }],
43
+ Renderables: [{
44
+ RenderableHash: 'DataCloner-TopBar-User',
45
+ TemplateHash: 'DataCloner-TopBar-User',
46
+ DestinationAddress: '#Theme-TopBar-User'
47
+ }]
48
+ };
@@ -123,10 +123,10 @@ module.exports.default_configuration =
123
123
  DefaultDestinationAddress: '#DataCloner-Section-ViewData',
124
124
  CSS: /*css*/`
125
125
  .data-table { width: 100%; border-collapse: collapse; font-size: 0.8em; font-family: monospace; }
126
- .data-table th { background: #f8f9fa; font-weight: 600; text-align: left; padding: 4px 8px; border: 1px solid var(--theme-color-border-default, #ddd); white-space: nowrap; position: sticky; top: 0; }
126
+ .data-table th { background: var(--theme-color-background-secondary, #f8f9fa); font-weight: 600; text-align: left; padding: 4px 8px; border: 1px solid var(--theme-color-border-default, #ddd); white-space: nowrap; position: sticky; top: 0; }
127
127
  .data-table td { padding: 4px 8px; border: 1px solid var(--theme-color-border-light, #eee); white-space: nowrap; max-width: 300px; overflow: hidden; text-overflow: ellipsis; }
128
128
  .data-table tr:nth-child(even) { background: var(--theme-color-background-secondary, #fafafa); }
129
- .data-table tr:hover { background: #f0f7ff; }
129
+ .data-table tr:hover { background: var(--theme-color-background-hover, #f0f7ff); }
130
130
  `,
131
131
  Templates:
132
132
  [