retold-data-service 2.1.1 → 2.1.5

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 (89) hide show
  1. package/BUILDING-AND-PUBLISHING.md +2 -2
  2. package/Dockerfile +1 -1
  3. package/README.md +12 -27
  4. package/build-all.js +66 -0
  5. package/diagrams/architecture.excalidraw +2966 -0
  6. package/diagrams/architecture.mmd +17 -0
  7. package/diagrams/architecture.svg +2 -0
  8. package/docs/README.md +12 -12
  9. package/docs/_brand.json +18 -0
  10. package/docs/_cover.md +1 -1
  11. package/docs/_topbar.md +1 -1
  12. package/docs/_version.json +3 -3
  13. package/docs/api/reference.md +8 -8
  14. package/docs/architecture.md +6 -84
  15. package/docs/diagrams/component-diagram.excalidraw +2807 -0
  16. package/docs/diagrams/component-diagram.mmd +14 -0
  17. package/docs/diagrams/component-diagram.svg +2 -0
  18. package/docs/diagrams/component-stack.excalidraw +1169 -0
  19. package/docs/diagrams/component-stack.mmd +6 -0
  20. package/docs/diagrams/component-stack.svg +2 -0
  21. package/docs/diagrams/hook-execution-order.excalidraw +3230 -0
  22. package/docs/diagrams/hook-execution-order.mmd +19 -0
  23. package/docs/diagrams/hook-execution-order.svg +2 -0
  24. package/docs/diagrams/initialization-flow.excalidraw +1800 -0
  25. package/docs/diagrams/initialization-flow.mmd +22 -0
  26. package/docs/diagrams/initialization-flow.svg +2 -0
  27. package/docs/index.html +6 -7
  28. package/docs/lifecycle-hooks.md +2 -21
  29. package/docs/retold-catalog.json +141 -141
  30. package/docs/retold-keyword-index.json +6818 -1608
  31. package/package.json +130 -96
  32. package/source/services/RetoldDataService-Brand.js +13 -0
  33. package/source/services/comprehension-loader/pict-app/Pict-Application-ComprehensionLoader.js +65 -15
  34. package/source/services/comprehension-loader/pict-app/providers/Pict-Provider-ComprehensionLoader.js +2 -2
  35. package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-Layout.js +68 -114
  36. package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-Load.js +29 -29
  37. package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-Schema.js +3 -3
  38. package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-Session.js +2 -2
  39. package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-SettingsPanel.js +62 -0
  40. package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-Shell.js +142 -0
  41. package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-Source.js +7 -7
  42. package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-StatusBar.js +125 -0
  43. package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-StatusDetail.js +89 -0
  44. package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-TopBar-Nav.js +42 -0
  45. package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-TopBar-User.js +48 -0
  46. package/source/services/comprehension-loader/web/comprehension-loader.js +5475 -6243
  47. package/source/services/comprehension-loader/web/comprehension-loader.js.map +1 -1
  48. package/source/services/comprehension-loader/web/comprehension-loader.min.js +75 -1
  49. package/source/services/comprehension-loader/web/comprehension-loader.min.js.map +1 -1
  50. package/source/services/comprehension-loader/web/favicons/favicon-dark.svg +13 -0
  51. package/source/services/comprehension-loader/web/favicons/favicon-light.svg +13 -0
  52. package/source/services/comprehension-loader/web/favicons/favicon.svg +13 -0
  53. package/source/services/comprehension-loader/web/index.html +3 -0
  54. package/source/services/comprehension-loader/web/pict.min.js +12 -0
  55. package/source/services/data-cloner/DataCloner-Command-Headless.js +2 -1
  56. package/source/services/data-cloner/DataCloner-Command-Sync.js +110 -75
  57. package/source/services/data-cloner/pict-app/Pict-Application-DataCloner.js +70 -47
  58. package/source/services/data-cloner/pict-app/providers/Pict-Provider-DataCloner.js +3 -3
  59. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Connection.js +1 -1
  60. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Deploy.js +5 -5
  61. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Export.js +11 -11
  62. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Layout.js +89 -135
  63. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Schema.js +3 -3
  64. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Session.js +2 -2
  65. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-SettingsPanel.js +61 -0
  66. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Shell.js +136 -0
  67. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-StatusBar.js +117 -0
  68. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-StatusDetail.js +81 -0
  69. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Sync.js +38 -38
  70. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-TopBar-Nav.js +42 -0
  71. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-TopBar-User.js +48 -0
  72. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-ViewData.js +5 -5
  73. package/source/services/data-cloner/web/data-cloner.js +5855 -8067
  74. package/source/services/data-cloner/web/data-cloner.js.map +1 -1
  75. package/source/services/data-cloner/web/data-cloner.min.js +75 -1
  76. package/source/services/data-cloner/web/data-cloner.min.js.map +1 -1
  77. package/source/services/data-cloner/web/favicons/favicon-dark.svg +13 -0
  78. package/source/services/data-cloner/web/favicons/favicon-light.svg +13 -0
  79. package/source/services/data-cloner/web/favicons/favicon.svg +13 -0
  80. package/source/services/data-cloner/web/favicons/favicons/favicon-dark.svg +13 -0
  81. package/source/services/data-cloner/web/favicons/favicons/favicon-light.svg +13 -0
  82. package/source/services/data-cloner/web/favicons/favicons/favicon.svg +13 -0
  83. package/source/services/data-cloner/web/index.html +3 -0
  84. package/source/services/data-cloner/web/pict.min.js +12 -0
  85. package/test/Bundles_smoke_tests.js +43 -0
  86. package/test/ComprehensionLoader_smoke_tests.js +95 -0
  87. package/test/DataCloner-RuntimeOverrides_tests.js +344 -0
  88. package/test/DataCloner_smoke_tests.js +87 -0
  89. 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';
@@ -224,24 +224,24 @@ class DataClonerSyncView extends libPictView
224
224
  + '<div class="report-card">'
225
225
  + ' <div class="card-label">Records</div>'
226
226
  + ' <div class="card-value">' + tmpTotalSynced + '</div>'
227
- + ' <div style="font-size:0.75em; color:#888">of ' + tmpTotalRecords + '</div>'
227
+ + ' <div style="font-size:0.75em; color:var(--theme-color-text-muted, #888)">of ' + tmpTotalRecords + '</div>'
228
228
  + '</div>';
229
229
 
230
230
  // --- Anomalies ---
231
231
  let tmpAnomalyContainer = document.getElementById('reportAnomalies');
232
232
  if (pReport.Anomalies.length === 0)
233
233
  {
234
- tmpAnomalyContainer.innerHTML = '<div style="color:#28a745; font-weight:600; font-size:0.9em">No anomalies detected.</div>';
234
+ tmpAnomalyContainer.innerHTML = '<div style="color:var(--theme-color-status-success, #28a745); font-weight:600; font-size:0.9em">No anomalies detected.</div>';
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>';
@@ -257,7 +257,7 @@ class DataClonerSyncView extends libPictView
257
257
  let tmpTopCount = Math.min(10, pReport.Tables.length);
258
258
  if (tmpTopCount > 0)
259
259
  {
260
- let tmpHtml = '<h4 style="margin:0 0 8px; font-size:0.95em; color:#444">Top Tables by Duration</h4>';
260
+ let tmpHtml = '<h4 style="margin:0 0 8px; font-size:0.95em; color:var(--theme-color-text-secondary, #444)">Top Tables by Duration</h4>';
261
261
  tmpHtml += '<table class="progress-table">';
262
262
  tmpHtml += '<tr><th>Table</th><th>Duration</th><th>Records</th><th>Status</th></tr>';
263
263
  for (let i = 0; i < tmpTopCount; i++)
@@ -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;
@@ -481,25 +481,25 @@ module.exports.default_configuration =
481
481
  DefaultDestinationAddress: '#DataCloner-Section-Sync',
482
482
  CSS: /*css*/`
483
483
  .progress-table { width: 100%; border-collapse: collapse; margin-top: 4px; margin-bottom: 4px; }
484
- .progress-table th, .progress-table td { text-align: left; padding: 6px 12px; border-bottom: 1px solid #eee; font-size: 0.9em; }
485
- .progress-table th { background: #f8f9fa; font-weight: 600; }
486
- .progress-table-muted td { color: #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; }
488
- .progress-bar-fill { height: 100%; background: #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 #e0e0e0; }
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: 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
+ .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: 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; }
492
- .sync-section-header-ok { color: #28a745; }
493
- .sync-section-count { font-weight: 400; color: #999; font-size: 0.95em; }
494
- .sync-section-overflow { font-size: 0.8em; color: #aaa; padding: 2px 12px 6px; }
495
- .sync-pending-count { text-align: right; color: #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; }
497
- .report-card .card-label { font-size: 0.8em; color: #666; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 4px; }
491
+ .sync-section-header-error { color: var(--theme-color-status-error, #dc3545); }
492
+ .sync-section-header-ok { color: var(--theme-color-status-success, #28a745); }
493
+ .sync-section-count { font-weight: 400; color: var(--theme-color-text-muted, #999); font-size: 0.95em; }
494
+ .sync-section-overflow { font-size: 0.8em; color: var(--theme-color-text-muted, #aaa); padding: 2px 12px 6px; }
495
+ .sync-pending-count { text-align: right; color: var(--theme-color-text-muted, #aaa); font-size: 0.85em; }
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
+ .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
- .report-card.outcome-success { border-left: 4px solid #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; }
499
+ .report-card.outcome-success { border-left: 4px solid var(--theme-color-status-success, #28a745); }
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
  [
@@ -534,30 +534,30 @@ module.exports.default_configuration =
534
534
  <button class="danger" style="margin:0" onclick="pict.views['DataCloner-Sync'].stopSync()">Stop Sync</button>
535
535
  </div>
536
536
  </div>
537
- <div style="font-size:0.8em; color:#888; margin-bottom:10px; padding-left:158px">Cross-DB tolerance for date comparison (default: 1000ms)</div>
537
+ <div style="font-size:0.8em; color:var(--theme-color-text-muted, #888); margin-bottom:10px; padding-left:158px">Cross-DB tolerance for date comparison (default: 1000ms)</div>
538
538
 
539
539
  <div style="margin-bottom:10px">
540
540
  <label style="margin-bottom:6px">Sync Mode</label>
541
541
  <div style="display:flex; gap:12px 20px; align-items:center; flex-wrap:wrap">
542
542
  <label style="font-weight:normal; margin:0; cursor:pointer">
543
543
  <input type="radio" name="syncMode" id="syncModeInitial" value="Initial" checked onchange="pict.views['DataCloner-Sync'].onSyncModeChanged()"> Initial
544
- <span style="color:#888; font-size:0.85em">(full clone)</span>
544
+ <span style="color:var(--theme-color-text-muted, #888); font-size:0.85em">(full clone)</span>
545
545
  </label>
546
546
  <label style="font-weight:normal; margin:0; cursor:pointer">
547
547
  <input type="radio" name="syncMode" id="syncModeOngoing" value="Ongoing" onchange="pict.views['DataCloner-Sync'].onSyncModeChanged()"> Ongoing
548
- <span style="color:#888; font-size:0.85em">(delta sync)</span>
548
+ <span style="color:var(--theme-color-text-muted, #888); font-size:0.85em">(delta sync)</span>
549
549
  </label>
550
550
  <label style="font-weight:normal; margin:0; cursor:pointer">
551
551
  <input type="radio" name="syncMode" id="syncModeOngoingEventualConsistency" value="OngoingEventualConsistency" onchange="pict.views['DataCloner-Sync'].onSyncModeChanged()"> Eventual
552
- <span style="color:#888; font-size:0.85em">(time-budgeted back-sync)</span>
552
+ <span style="color:var(--theme-color-text-muted, #888); font-size:0.85em">(time-budgeted back-sync)</span>
553
553
  </label>
554
554
  <label style="font-weight:normal; margin:0; cursor:pointer">
555
555
  <input type="radio" name="syncMode" id="syncModeTrueUp" value="TrueUp" onchange="pict.views['DataCloner-Sync'].onSyncModeChanged()"> True-Up
556
- <span style="color:#888; font-size:0.85em">(linear walk, one-time)</span>
556
+ <span style="color:var(--theme-color-text-muted, #888); font-size:0.85em">(linear walk, one-time)</span>
557
557
  </label>
558
558
  <label style="font-weight:normal; margin:0; cursor:pointer">
559
559
  <input type="radio" name="syncMode" id="syncModeComparisonOnly" value="ComparisonOnly" onchange="pict.views['DataCloner-Sync'].onSyncModeChanged()"> Compare
560
- <span style="color:#888; font-size:0.85em">(diff report, no sync)</span>
560
+ <span style="color:var(--theme-color-text-muted, #888); font-size:0.85em">(diff report, no sync)</span>
561
561
  </label>
562
562
  </div>
563
563
  </div>
@@ -569,7 +569,7 @@ module.exports.default_configuration =
569
569
  <input type="number" id="backSyncTimeLimit" value="30000" min="1000" max="600000" style="margin-bottom:0">
570
570
  </div>
571
571
  </div>
572
- <div style="font-size:0.8em; color:#888; padding-left:4px">Milliseconds devoted to backwards bisection per entity before pulling new records (default: 30000)</div>
572
+ <div style="font-size:0.8em; color:var(--theme-color-text-muted, #888); padding-left:4px">Milliseconds devoted to backwards bisection per entity before pulling new records (default: 30000)</div>
573
573
  </div>
574
574
 
575
575
  <div id="syncModeOptions-TrueUp" style="display:none; margin-bottom:10px">
@@ -579,7 +579,7 @@ module.exports.default_configuration =
579
579
  <input type="number" id="trueUpPageSize" value="500" min="10" max="10000" style="margin-bottom:0">
580
580
  </div>
581
581
  </div>
582
- <div style="font-size:0.8em; color:#888; padding-left:4px">Records per page for the linear keyset walk (default: 500)</div>
582
+ <div style="font-size:0.8em; color:var(--theme-color-text-muted, #888); padding-left:4px">Records per page for the linear keyset walk (default: 500)</div>
583
583
  </div>
584
584
 
585
585
  <div class="checkbox-row">
@@ -609,7 +609,7 @@ module.exports.default_configuration =
609
609
  <div id="syncProgress"></div>
610
610
 
611
611
  <!-- Sync Report (appears after sync completes) -->
612
- <div id="syncReportSection" style="display:none; margin-top:16px; padding-top:16px; border-top:2px solid #ddd">
612
+ <div id="syncReportSection" style="display:none; margin-top:16px; padding-top:16px; border-top:2px solid var(--theme-color-border-default, #ddd)">
613
613
  <h3 style="margin:0 0 12px; font-size:1.1em">Sync Report</h3>
614
614
 
615
615
  <!-- Summary cards -->
@@ -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
+ };
@@ -81,7 +81,7 @@ class DataClonerViewDataView extends libPictView
81
81
 
82
82
  if (!pRows || pRows.length === 0)
83
83
  {
84
- tmpContainer.innerHTML = '<p style="color:#666; font-size:0.9em; padding:8px">No rows.</p>';
84
+ tmpContainer.innerHTML = '<p style="color:var(--theme-color-text-secondary, #666); font-size:0.9em; padding:8px">No rows.</p>';
85
85
  return;
86
86
  }
87
87
 
@@ -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 #ddd; white-space: nowrap; position: sticky; top: 0; }
127
- .data-table td { padding: 4px 8px; border: 1px solid #eee; white-space: nowrap; max-width: 300px; overflow: hidden; text-overflow: ellipsis; }
128
- .data-table tr:nth-child(even) { background: #fafafa; }
129
- .data-table tr:hover { background: #f0f7ff; }
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
+ .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
+ .data-table tr:nth-child(even) { background: var(--theme-color-background-secondary, #fafafa); }
129
+ .data-table tr:hover { background: var(--theme-color-background-hover, #f0f7ff); }
130
130
  `,
131
131
  Templates:
132
132
  [