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
@@ -2,35 +2,48 @@ const libPictApplication = require('pict-application');
2
2
 
3
3
  const libProvider = require('./providers/Pict-Provider-DataCloner.js');
4
4
 
5
- const libViewLayout = require('./views/PictView-DataCloner-Layout.js');
5
+ const libViewLayout = require('./views/PictView-DataCloner-Layout.js');
6
6
  const libViewConnection = require('./views/PictView-DataCloner-Connection.js');
7
- const libViewSession = require('./views/PictView-DataCloner-Session.js');
8
- const libViewSchema = require('./views/PictView-DataCloner-Schema.js');
9
- const libViewDeploy = require('./views/PictView-DataCloner-Deploy.js');
10
- const libViewSync = require('./views/PictView-DataCloner-Sync.js');
11
- const libViewExport = require('./views/PictView-DataCloner-Export.js');
12
- const libViewViewData = require('./views/PictView-DataCloner-ViewData.js');
13
- const libViewHistogram = require('pict-section-histogram');
7
+ const libViewSession = require('./views/PictView-DataCloner-Session.js');
8
+ const libViewSchema = require('./views/PictView-DataCloner-Schema.js');
9
+ const libViewDeploy = require('./views/PictView-DataCloner-Deploy.js');
10
+ const libViewSync = require('./views/PictView-DataCloner-Sync.js');
11
+ const libViewExport = require('./views/PictView-DataCloner-Export.js');
12
+ const libViewViewData = require('./views/PictView-DataCloner-ViewData.js');
13
+ const libViewHistogram = require('pict-section-histogram');
14
14
  const libViewConnectionForm = require('pict-section-connection-form');
15
15
 
16
+ const libPictSectionModal = require('pict-section-modal');
17
+ const libPictSectionTheme = require('pict-section-theme');
18
+ const libBrand = require('../../RetoldDataService-Brand.js');
19
+
20
+ const libViewShell = require('./views/PictView-DataCloner-Shell.js');
21
+ const libViewTopBarNav = require('./views/PictView-DataCloner-TopBar-Nav.js');
22
+ const libViewTopBarUser = require('./views/PictView-DataCloner-TopBar-User.js');
23
+ const libViewStatusBar = require('./views/PictView-DataCloner-StatusBar.js');
24
+ const libViewStatusDetail = require('./views/PictView-DataCloner-StatusDetail.js');
25
+ const libViewSettings = require('./views/PictView-DataCloner-SettingsPanel.js');
26
+
16
27
  class DataClonerApplication extends libPictApplication
17
28
  {
18
29
  constructor(pFable, pOptions, pServiceHash)
19
30
  {
20
31
  super(pFable, pOptions, pServiceHash);
21
32
 
22
- // Register provider
23
- this.pict.addProvider('DataCloner', libProvider.default_configuration, libProvider);
33
+ // 1. Modal section (provides shell + panels + modal API).
34
+ this.pict.addView('Pict-Section-Modal',
35
+ libPictSectionModal.default_configuration, libPictSectionModal);
24
36
 
25
- // Register views
26
- this.pict.addView('DataCloner-Layout', libViewLayout.default_configuration, libViewLayout);
37
+ // 2. Provider + existing section views.
38
+ this.pict.addProvider('DataCloner', libProvider.default_configuration, libProvider);
39
+ this.pict.addView('DataCloner-Layout', libViewLayout.default_configuration, libViewLayout);
27
40
  this.pict.addView('DataCloner-Connection', libViewConnection.default_configuration, libViewConnection);
28
- this.pict.addView('DataCloner-Session', libViewSession.default_configuration, libViewSession);
29
- this.pict.addView('DataCloner-Schema', libViewSchema.default_configuration, libViewSchema);
30
- this.pict.addView('DataCloner-Deploy', libViewDeploy.default_configuration, libViewDeploy);
31
- this.pict.addView('DataCloner-Sync', libViewSync.default_configuration, libViewSync);
32
- this.pict.addView('DataCloner-Export', libViewExport.default_configuration, libViewExport);
33
- this.pict.addView('DataCloner-ViewData', libViewViewData.default_configuration, libViewViewData);
41
+ this.pict.addView('DataCloner-Session', libViewSession.default_configuration, libViewSession);
42
+ this.pict.addView('DataCloner-Schema', libViewSchema.default_configuration, libViewSchema);
43
+ this.pict.addView('DataCloner-Deploy', libViewDeploy.default_configuration, libViewDeploy);
44
+ this.pict.addView('DataCloner-Sync', libViewSync.default_configuration, libViewSync);
45
+ this.pict.addView('DataCloner-Export', libViewExport.default_configuration, libViewExport);
46
+ this.pict.addView('DataCloner-ViewData', libViewViewData.default_configuration, libViewViewData);
34
47
  this.pict.addView('DataCloner-StatusHistogram',
35
48
  {
36
49
  ViewIdentifier: 'DataCloner-StatusHistogram',
@@ -43,14 +56,12 @@ class DataClonerApplication extends libPictApplication
43
56
  ShowValues: false,
44
57
  ShowLabels: true,
45
58
  MaxBarSize: 80,
46
- BarColor: '#4a90d9',
59
+ BarColor: 'var(--theme-color-brand-primary, #4a90d9)',
47
60
  Bins: []
48
61
  }, libViewHistogram);
49
62
 
50
- // Shared schema-driven connection form. Renders into the slot
51
- // the DataCloner-Connection accordion shell exposes; the
52
- // provider's bootstrapConnectionSchemas() pumps the schemas in
53
- // once the host's /clone/connection/schemas endpoint responds.
63
+ // Shared schema-driven connection form (renders into the
64
+ // DataCloner-Connection accordion shell's slot).
54
65
  this.pict.addView('PictSection-ConnectionForm',
55
66
  Object.assign({}, libViewConnectionForm.default_configuration,
56
67
  {
@@ -60,21 +71,39 @@ class DataClonerApplication extends libPictApplication
60
71
  ActiveAddress: 'AppData.DataCloner.Connection.ActiveProvider',
61
72
  FieldIDPrefix: 'datacloner-conn'
62
73
  }), libViewConnectionForm);
74
+
75
+ // 3. Shell host + slot views + status bar / detail + settings panel.
76
+ this.pict.addView('DataCloner-Shell',
77
+ libViewShell.default_configuration, libViewShell);
78
+ this.pict.addView('DataCloner-TopBar-Nav',
79
+ libViewTopBarNav.default_configuration, libViewTopBarNav);
80
+ this.pict.addView('DataCloner-TopBar-User',
81
+ libViewTopBarUser.default_configuration, libViewTopBarUser);
82
+ this.pict.addView('DataCloner-StatusBar',
83
+ libViewStatusBar.default_configuration, libViewStatusBar);
84
+ this.pict.addView('DataCloner-StatusDetail',
85
+ libViewStatusDetail.default_configuration, libViewStatusDetail);
86
+ this.pict.addView('DataCloner-SettingsPanel',
87
+ libViewSettings.default_configuration, libViewSettings);
88
+
89
+ // 4. Theme-Section provider — registered LAST so it can find the slot views.
90
+ this.pict.addProvider('Theme-Section',
91
+ {
92
+ ApplyDefault: 'pict-default',
93
+ DefaultMode: 'system',
94
+ DefaultScale: 1.0,
95
+ Brand: libBrand,
96
+ Views: ['Picker', 'ModeToggle', 'ScaleSelect', 'BrandMark', 'TopBar', 'BottomBar'],
97
+ ViewOptions:
98
+ {
99
+ TopBar: { NavView: 'DataCloner-TopBar-Nav', UserView: 'DataCloner-TopBar-User', Height: 56 },
100
+ BottomBar: { StatusView: 'DataCloner-StatusBar', Height: 36 }
101
+ }
102
+ }, libPictSectionTheme);
63
103
  }
64
104
 
65
105
  onAfterInitializeAsync(fCallback)
66
106
  {
67
- // Centralized state (replaces global variables).
68
- //
69
- // PersistFields covers the static, non-connection inputs only.
70
- // Connection-section fields (provider picker + per-provider
71
- // inputs) are schema-driven now: their DOM ids and
72
- // localStorage keys are derived at runtime from the host's
73
- // /clone/connection/schemas response and persistence is hooked
74
- // up by Pict-Provider-DataCloner#bootstrapConnectionSchemas
75
- // after the schema-driven Connection view re-renders. See
76
- // PictView-DataCloner-Connection.js for the field-id
77
- // convention.
78
107
  this.pict.AppData.DataCloner =
79
108
  {
80
109
  FetchedTables: [],
@@ -94,9 +123,6 @@ class DataClonerApplication extends libPictApplication
94
123
  'userName', 'password', 'schemaURL', 'pageSize', 'dateTimePrecisionMS',
95
124
  'syncMaxRecords'
96
125
  ],
97
- // Connection state — populated by bootstrapConnectionSchemas().
98
- // Initialized empty here so the Connection view's first
99
- // onBeforeRender finds a valid (if empty) shape.
100
126
  Connection:
101
127
  {
102
128
  Schemas: [],
@@ -108,15 +134,17 @@ class DataClonerApplication extends libPictApplication
108
134
  }
109
135
  };
110
136
 
111
- // Make pict available for inline onclick handlers
112
137
  window.pict = this.pict;
113
138
 
114
- // Render layout (which chains child view renders via onAfterRender).
115
- // The Connection view renders an empty shell here — the schemas
116
- // arrive asynchronously and trigger a re-render once they land.
139
+ // Render the shell first creates panel destination divs.
140
+ this.pict.views['DataCloner-Shell'].render();
141
+
142
+ // Render the layout (chains child renders) into #DataCloner-Workspace.
117
143
  this.pict.views['DataCloner-Layout'].render();
118
144
 
119
- // Post-render initialization for the static (non-connection) UI.
145
+ // Render the StatusBar into the BottomBar slot.
146
+ this.pict.views['DataCloner-StatusBar'].render();
147
+
120
148
  this.pict.providers.DataCloner.initPersistence();
121
149
  this.pict.providers.DataCloner.restoreDeployedTables();
122
150
  this.pict.providers.DataCloner.startLiveStatusPolling();
@@ -125,11 +153,6 @@ class DataClonerApplication extends libPictApplication
125
153
  this.pict.views['DataCloner-Layout'].collapseAllSections();
126
154
  this.pict.providers.DataCloner.initAutoProcess();
127
155
 
128
- // Async: fetch the host's connection-form schemas and re-render
129
- // the Connection section. bootstrapConnectionSchemas restores
130
- // localStorage values + hooks save listeners once the new DOM
131
- // is in place, then invokes onProviderChange() to surface the
132
- // active provider's form.
133
156
  this.pict.providers.DataCloner.bootstrapConnectionSchemas(function () { /* fire-and-forget */ });
134
157
 
135
158
  return fCallback();
@@ -459,11 +459,11 @@ module.exports.default_configuration =
459
459
  <div id="configExportStatus"></div>
460
460
  <div id="cliCommand" style="display:none; margin-bottom:10px">
461
461
  <label style="margin-bottom:4px">CLI Command <span style="color:var(--theme-color-text-muted, #888); font-weight:normal">(with config file)</span></label>
462
- <div style="background:var(--theme-color-text-primary, #1a1a1a); color:#4fc3f7; padding:10px 14px; border-radius:4px; font-family:monospace; font-size:0.9em; word-break:break-all; cursor:pointer" onclick="pict.views['DataCloner-Export'].copyCLI()" title="Click to copy"></div>
462
+ <div style="background:var(--theme-color-text-primary, #1a1a1a); color:var(--theme-color-status-info, #4fc3f7); padding:10px 14px; border-radius:4px; font-family:monospace; font-size:0.9em; word-break:break-all; cursor:pointer" onclick="pict.views['DataCloner-Export'].copyCLI()" title="Click to copy"></div>
463
463
  </div>
464
464
  <div id="cliOneShot" style="display:none; margin-bottom:10px">
465
465
  <label style="margin-bottom:4px">One-liner <span style="color:var(--theme-color-text-muted, #888); font-weight:normal">(no config file needed)</span></label>
466
- <div style="background:var(--theme-color-text-primary, #1a1a1a); color:#4fc3f7; padding:10px 14px; border-radius:4px; font-family:monospace; font-size:0.9em; word-break:break-all; cursor:pointer; white-space:pre-wrap" onclick="pict.views['DataCloner-Export'].copyOneShot()" title="Click to copy"></div>
466
+ <div style="background:var(--theme-color-text-primary, #1a1a1a); color:var(--theme-color-status-info, #4fc3f7); padding:10px 14px; border-radius:4px; font-family:monospace; font-size:0.9em; word-break:break-all; cursor:pointer; white-space:pre-wrap" onclick="pict.views['DataCloner-Export'].copyOneShot()" title="Click to copy"></div>
467
467
  </div>
468
468
  <textarea id="configOutput" style="display:none; width:100%; min-height:300px; font-family:monospace; font-size:0.85em; padding:10px; border:1px solid var(--theme-color-border-default, #ccc); border-radius:4px; background:var(--theme-color-background-secondary, #fafafa); tab-size:4; resize:vertical" readonly></textarea>
469
469
 
@@ -476,7 +476,7 @@ module.exports.default_configuration =
476
476
  </div>
477
477
  <div id="mdwintCLICommand" style="margin-bottom:10px">
478
478
  <label style="margin-bottom:4px">CLI Command</label>
479
- <div style="background:var(--theme-color-text-primary, #1a1a1a); color:#4fc3f7; padding:10px 14px; border-radius:4px; font-family:monospace; font-size:0.9em; word-break:break-all; cursor:pointer" onclick="pict.views['DataCloner-Export'].copyMdwintCLI()" title="Click to copy"></div>
479
+ <div style="background:var(--theme-color-text-primary, #1a1a1a); color:var(--theme-color-status-info, #4fc3f7); padding:10px 14px; border-radius:4px; font-family:monospace; font-size:0.9em; word-break:break-all; cursor:pointer" onclick="pict.views['DataCloner-Export'].copyMdwintCLI()" title="Click to copy"></div>
480
480
  </div>
481
481
  <div id="mdwintConfigStatus"></div>
482
482
  <textarea id="mdwintConfigOutput" style="width:100%; min-height:250px; font-family:monospace; font-size:0.85em; padding:10px; border:1px solid var(--theme-color-border-default, #ccc); border-radius:4px; background:var(--theme-color-background-secondary, #fafafa); tab-size:4; resize:vertical" readonly></textarea>
@@ -45,37 +45,6 @@ class DataClonerLayoutView extends libPictView
45
45
  tmpCards[i].classList.remove('open');
46
46
  }
47
47
  }
48
-
49
- toggleStatusDetail()
50
- {
51
- let tmpDetail = document.getElementById('liveStatusDetail');
52
- let tmpMeta = document.getElementById('liveStatusMeta');
53
- let tmpMessage = document.getElementById('liveStatusMessage');
54
- let tmpToggle = document.getElementById('liveStatusToggle');
55
- let tmpBar = document.getElementById('liveStatusBar');
56
- if (!tmpDetail) return;
57
-
58
- let tmpIsExpanded = tmpDetail.style.display !== 'none';
59
-
60
- if (tmpIsExpanded)
61
- {
62
- tmpDetail.style.display = 'none';
63
- tmpMeta.style.display = '';
64
- tmpMessage.style.display = '';
65
- tmpToggle.innerHTML = '&#9660;';
66
- tmpBar.classList.remove('expanded');
67
- this.pict.providers.DataCloner.onStatusDetailCollapsed();
68
- }
69
- else
70
- {
71
- tmpDetail.style.display = '';
72
- tmpMeta.style.display = 'none';
73
- tmpMessage.style.display = 'none';
74
- tmpToggle.innerHTML = '&#9650;';
75
- tmpBar.classList.add('expanded');
76
- this.pict.providers.DataCloner.onStatusDetailExpanded();
77
- }
78
- }
79
48
  }
80
49
 
81
50
  module.exports = DataClonerLayoutView;
@@ -84,7 +53,7 @@ module.exports.default_configuration =
84
53
  {
85
54
  ViewIdentifier: 'DataCloner-Layout',
86
55
  DefaultRenderable: 'DataCloner-Layout',
87
- DefaultDestinationAddress: '#DataCloner-Application-Container',
56
+ DefaultDestinationAddress: '#DataCloner-Workspace',
88
57
  CSS: /*css*/`
89
58
  * { box-sizing: border-box; margin: 0; padding: 0; }
90
59
  body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: var(--theme-color-background-secondary, #f5f5f5); color: var(--theme-color-text-primary, #333); padding: 20px; }
@@ -97,7 +66,7 @@ h2 { margin-bottom: 12px; color: var(--theme-color-text-secondary, #444); font-s
97
66
  .accordion-row { display: flex; gap: 0; margin-bottom: 16px; align-items: stretch; }
98
67
  .accordion-number {
99
68
  flex: 0 0 48px; display: flex; align-items: flex-start; justify-content: center;
100
- padding-top: 16px; font-size: 1.6em; font-weight: 700; color: #4a90d9;
69
+ padding-top: 16px; font-size: 1.6em; font-weight: 700; color: var(--theme-color-brand-primary, #4a90d9);
101
70
  user-select: none;
102
71
  }
103
72
  .accordion-card {
@@ -126,11 +95,11 @@ h2 { margin-bottom: 12px; color: var(--theme-color-text-secondary, #444); font-s
126
95
  .accordion-actions { display: flex; align-items: baseline; gap: 8px; flex-shrink: 0; }
127
96
  .accordion-card.open .accordion-actions { display: none; }
128
97
  .accordion-go {
129
- font-size: 0.82em; color: #4a90d9; cursor: pointer; text-decoration: none;
98
+ font-size: 0.82em; color: var(--theme-color-brand-primary, #4a90d9); cursor: pointer; text-decoration: none;
130
99
  font-weight: 500; white-space: nowrap; padding: 2px 6px; border-radius: 3px;
131
100
  transition: background 0.15s;
132
101
  }
133
- .accordion-go:hover { background: #e8f0fe; text-decoration: underline; }
102
+ .accordion-go:hover { background: var(--theme-color-background-hover, #e8f0fe); text-decoration: underline; }
134
103
  .accordion-auto {
135
104
  font-size: 0.82em; color: var(--theme-color-text-muted, #999); white-space: nowrap; cursor: pointer;
136
105
  }
@@ -147,7 +116,7 @@ h2 { margin-bottom: 12px; color: var(--theme-color-text-secondary, #444); font-s
147
116
  }
148
117
  .accordion-phase.visible { display: flex; }
149
118
  .accordion-phase-ok { color: var(--theme-color-status-success, #28a745); }
150
- .accordion-phase-error { color: #dc3545; }
119
+ .accordion-phase-error { color: var(--theme-color-status-error, #dc3545); }
151
120
  .accordion-phase-busy { color: var(--theme-color-status-success, #28a745); }
152
121
  .accordion-phase-busy .phase-spinner {
153
122
  display: inline-block; width: 14px; height: 14px;
@@ -173,18 +142,18 @@ input[type="text"], input[type="password"], input[type="number"] {
173
142
  font-size: 0.95em; margin-bottom: 10px;
174
143
  }
175
144
  input[type="text"]:focus, input[type="password"]:focus, input[type="number"]:focus {
176
- outline: none; border-color: #4a90d9;
145
+ outline: none; border-color: var(--theme-color-brand-primary, #4a90d9);
177
146
  }
178
147
 
179
148
  button {
180
149
  padding: 8px 16px; border: none; border-radius: 4px; cursor: pointer;
181
150
  font-size: 0.9em; font-weight: 600; margin-right: 8px; margin-bottom: 8px;
182
151
  }
183
- button.primary { background: #4a90d9; color: var(--theme-color-background-panel, #fff); }
184
- button.primary:hover { background: #357abd; }
185
- button.secondary { background: #6c757d; color: var(--theme-color-background-panel, #fff); }
152
+ button.primary { background: var(--theme-color-brand-primary, #4a90d9); color: var(--theme-color-background-panel, #fff); }
153
+ button.primary:hover { background: var(--theme-color-brand-primary-hover, #357abd); }
154
+ button.secondary { background: var(--theme-color-text-secondary, #6c757d); color: var(--theme-color-background-panel, #fff); }
186
155
  button.secondary:hover { background: #5a6268; }
187
- button.danger { background: #dc3545; color: var(--theme-color-background-panel, #fff); }
156
+ button.danger { background: var(--theme-color-status-error, #dc3545); color: var(--theme-color-background-panel, #fff); }
188
157
  button.danger:hover { background: #c82333; }
189
158
  button.success { background: var(--theme-color-status-success, #28a745); color: var(--theme-color-background-panel, #fff); }
190
159
  button.success:hover { background: #218838; }
@@ -199,7 +168,7 @@ button:disabled { opacity: 0.5; cursor: not-allowed; }
199
168
  .inline-group { display: flex; gap: 8px; align-items: flex-end; margin-bottom: 10px; }
200
169
  .inline-group > div { flex: 1; }
201
170
 
202
- a { color: #4a90d9; }
171
+ a { color: var(--theme-color-brand-primary, #4a90d9); }
203
172
 
204
173
  select { background: var(--theme-color-background-panel, #fff); width: 100%; padding: 8px 12px; border: 1px solid var(--theme-color-border-default, #ccc); border-radius: 4px; font-size: 0.95em; margin-bottom: 10px; }
205
174
 
@@ -211,28 +180,28 @@ select { background: var(--theme-color-background-panel, #fff); width: 100%; pad
211
180
  .live-status-bar {
212
181
  background: var(--theme-color-background-panel, #fff); border-radius: 8px; margin-bottom: 16px;
213
182
  box-shadow: 0 1px 3px rgba(0,0,0,0.1);
214
- position: sticky; top: 0; z-index: 100; border-left: 4px solid #6c757d;
183
+ position: sticky; top: 0; z-index: 100; border-left: 4px solid var(--theme-color-text-secondary, #6c757d);
215
184
  }
216
- .live-status-bar.phase-idle { border-left-color: #6c757d; }
217
- .live-status-bar.phase-disconnected { border-left-color: #dc3545; }
218
- .live-status-bar.phase-ready { border-left-color: #4a90d9; }
185
+ .live-status-bar.phase-idle { border-left-color: var(--theme-color-text-secondary, #6c757d); }
186
+ .live-status-bar.phase-disconnected { border-left-color: var(--theme-color-status-error, #dc3545); }
187
+ .live-status-bar.phase-ready { border-left-color: var(--theme-color-brand-primary, #4a90d9); }
219
188
  .live-status-bar.phase-syncing { border-left-color: var(--theme-color-status-success, #28a745); }
220
- .live-status-bar.phase-stopping { border-left-color: #ffc107; }
189
+ .live-status-bar.phase-stopping { border-left-color: var(--theme-color-status-warning, #ffc107); }
221
190
  .live-status-bar.phase-complete { border-left-color: var(--theme-color-status-success, #28a745); }
222
191
 
223
192
  .live-status-dot {
224
193
  width: 12px; height: 12px; border-radius: 50%; flex-shrink: 0;
225
- background: #6c757d;
194
+ background: var(--theme-color-text-secondary, #6c757d);
226
195
  }
227
- .live-status-bar.phase-idle .live-status-dot { background: #6c757d; }
228
- .live-status-bar.phase-disconnected .live-status-dot { background: #dc3545; }
229
- .live-status-bar.phase-ready .live-status-dot { background: #4a90d9; }
196
+ .live-status-bar.phase-idle .live-status-dot { background: var(--theme-color-text-secondary, #6c757d); }
197
+ .live-status-bar.phase-disconnected .live-status-dot { background: var(--theme-color-status-error, #dc3545); }
198
+ .live-status-bar.phase-ready .live-status-dot { background: var(--theme-color-brand-primary, #4a90d9); }
230
199
  .live-status-bar.phase-syncing .live-status-dot {
231
200
  background: var(--theme-color-status-success, #28a745);
232
201
  animation: live-pulse 1.5s ease-in-out infinite;
233
202
  }
234
203
  .live-status-bar.phase-stopping .live-status-dot {
235
- background: #ffc107;
204
+ background: var(--theme-color-status-warning, #ffc107);
236
205
  animation: live-pulse 0.8s ease-in-out infinite;
237
206
  }
238
207
  .live-status-bar.phase-complete .live-status-dot { background: var(--theme-color-status-success, #28a745); }
@@ -251,7 +220,7 @@ select { background: var(--theme-color-background-panel, #fff); width: 100%; pad
251
220
  .live-status-meta-item strong { color: var(--theme-color-text-primary, #333); }
252
221
 
253
222
  .live-status-progress-bar {
254
- height: 3px; background: #e9ecef; border-radius: 2px; overflow: hidden;
223
+ height: 3px; background: var(--theme-color-background-tertiary, #e9ecef); border-radius: 2px; overflow: hidden;
255
224
  position: absolute; bottom: 0; left: 0; right: 0;
256
225
  }
257
226
  .live-status-progress-fill {
@@ -263,7 +232,7 @@ select { background: var(--theme-color-background-panel, #fff); width: 100%; pad
263
232
  padding: 14px 20px; user-select: none;
264
233
  }
265
234
  .live-status-bar.expanded .live-status-header {
266
- border-bottom: 1px solid #e9ecef; padding-bottom: 10px;
235
+ border-bottom: 1px solid var(--theme-color-background-tertiary, #e9ecef); padding-bottom: 10px;
267
236
  }
268
237
  .live-status-expand-toggle {
269
238
  flex: 0 0 20px; display: flex; align-items: center; justify-content: center;
@@ -278,7 +247,7 @@ select { background: var(--theme-color-background-panel, #fff); width: 100%; pad
278
247
  /* Status Detail Summary Banner */
279
248
  .status-detail-summary {
280
249
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
281
- padding: 8px 0 12px; margin-bottom: 12px; border-bottom: 1px solid #e9ecef;
250
+ padding: 8px 0 12px; margin-bottom: 12px; border-bottom: 1px solid var(--theme-color-background-tertiary, #e9ecef);
282
251
  }
283
252
  .status-detail-summary-message {
284
253
  font-size: 0.92em; color: var(--theme-color-text-primary, #333); font-weight: 600;
@@ -303,10 +272,10 @@ select { background: var(--theme-color-background-panel, #fff); width: 100%; pad
303
272
  }
304
273
  .running-op-name { font-weight: 600; min-width: 180px; }
305
274
  .running-op-bar {
306
- flex: 1; height: 8px; background: #e9ecef; border-radius: 4px; overflow: hidden;
275
+ flex: 1; height: 8px; background: var(--theme-color-background-tertiary, #e9ecef); border-radius: 4px; overflow: hidden;
307
276
  min-width: 120px;
308
277
  }
309
- .running-op-bar-fill { height: 100%; background: #4a90d9; transition: width 0.5s ease; }
278
+ .running-op-bar-fill { height: 100%; background: var(--theme-color-brand-primary, #4a90d9); transition: width 0.5s ease; }
310
279
  .running-op-count { font-size: 0.85em; color: var(--theme-color-text-secondary, #666); white-space: nowrap; }
311
280
  .running-op-pending { color: var(--theme-color-text-muted, #888); font-size: 0.85em; font-style: italic; padding: 4px 0; }
312
281
 
@@ -323,29 +292,29 @@ select { background: var(--theme-color-background-panel, #fff); width: 100%; pad
323
292
  /* Ratio Bar */
324
293
  .ratio-bar-container {
325
294
  display: flex; height: 10px; border-radius: 5px; overflow: hidden;
326
- background: #e9ecef; margin: 4px 0;
295
+ background: var(--theme-color-background-tertiary, #e9ecef); margin: 4px 0;
327
296
  }
328
297
  .ratio-bar-segment { height: 100%; transition: width 0.5s ease; }
329
- .ratio-bar-segment.unchanged { background: #6c757d; }
298
+ .ratio-bar-segment.unchanged { background: var(--theme-color-text-secondary, #6c757d); }
330
299
  .ratio-bar-segment.new-records { background: var(--theme-color-status-success, #28a745); }
331
- .ratio-bar-segment.updated { background: #4a90d9; }
332
- .ratio-bar-segment.deleted { background: #dc3545; }
300
+ .ratio-bar-segment.updated { background: var(--theme-color-brand-primary, #4a90d9); }
301
+ .ratio-bar-segment.deleted { background: var(--theme-color-status-error, #dc3545); }
333
302
  .ratio-bar-legend {
334
303
  display: flex; gap: 12px; font-size: 0.75em; color: var(--theme-color-text-secondary, #666); margin-top: 2px; flex-wrap: wrap;
335
304
  }
336
305
  .ratio-bar-legend-item { display: flex; align-items: center; gap: 4px; }
337
306
  .ratio-bar-legend-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
338
- .ratio-bar-legend-dot.unchanged-dot { background: #6c757d; }
307
+ .ratio-bar-legend-dot.unchanged-dot { background: var(--theme-color-text-secondary, #6c757d); }
339
308
  .ratio-bar-legend-dot.new-dot { background: var(--theme-color-status-success, #28a745); }
340
- .ratio-bar-legend-dot.updated-dot { background: #4a90d9; }
341
- .ratio-bar-legend-dot.deleted-dot { background: #dc3545; }
309
+ .ratio-bar-legend-dot.updated-dot { background: var(--theme-color-brand-primary, #4a90d9); }
310
+ .ratio-bar-legend-dot.deleted-dot { background: var(--theme-color-status-error, #dc3545); }
342
311
 
343
312
  /* Error Operations */
344
313
  .error-op-row { padding: 6px 0; border-bottom: 1px solid var(--theme-color-background-tertiary, #f0f0f0); font-size: 0.9em; }
345
314
  .error-op-row:last-child { border-bottom: none; }
346
315
  .error-op-header { display: flex; align-items: center; gap: 8px; }
347
- .error-op-name { font-weight: 600; color: #dc3545; }
348
- .error-op-status { font-size: 0.82em; color: #dc3545; }
316
+ .error-op-name { font-weight: 600; color: var(--theme-color-status-error, #dc3545); }
317
+ .error-op-status { font-size: 0.82em; color: var(--theme-color-status-error, #dc3545); }
349
318
  .error-op-message { font-size: 0.82em; color: var(--theme-color-text-muted, #888); margin-top: 2px; padding-left: 18px; }
350
319
  .error-op-log-entries {
351
320
  font-size: 0.78em; color: var(--theme-color-text-muted, #888); margin-top: 4px; padding-left: 18px;
@@ -359,23 +328,23 @@ select { background: var(--theme-color-background-panel, #fff); width: 100%; pad
359
328
  .precount-table thead th {
360
329
  text-align: left; font-weight: 600; color: var(--theme-color-text-secondary, #555); font-size: 0.85em;
361
330
  text-transform: uppercase; letter-spacing: 0.3px;
362
- padding: 4px 8px 6px; border-bottom: 2px solid #e9ecef;
331
+ padding: 4px 8px 6px; border-bottom: 2px solid var(--theme-color-background-tertiary, #e9ecef);
363
332
  }
364
333
  .precount-table tbody td {
365
334
  padding: 4px 8px; border-bottom: 1px solid var(--theme-color-background-tertiary, #f0f0f0);
366
335
  }
367
- .precount-table tbody tr:last-child td { border-bottom: 1px solid #e9ecef; }
336
+ .precount-table tbody tr:last-child td { border-bottom: 1px solid var(--theme-color-background-tertiary, #e9ecef); }
368
337
  .precount-table tfoot td {
369
338
  padding: 6px 8px 2px; font-size: 0.95em;
370
339
  }
371
- .precount-error td { color: #dc3545; }
340
+ .precount-error td { color: var(--theme-color-status-error, #dc3545); }
372
341
  .precount-pending {
373
342
  color: var(--theme-color-text-muted, #888); font-size: 0.85em; font-style: italic; padding: 8px 0 2px;
374
343
  display: flex; align-items: center; gap: 6px;
375
344
  }
376
345
  .precount-spinner {
377
346
  display: inline-block; width: 12px; height: 12px;
378
- border: 2px solid var(--theme-color-border-default, #ddd); border-top-color: #4a90d9;
347
+ border: 2px solid var(--theme-color-border-default, #ddd); border-top-color: var(--theme-color-brand-primary, #4a90d9);
379
348
  border-radius: 50%; animation: precount-spin 0.8s linear infinite;
380
349
  }
381
350
  @keyframes precount-spin { to { transform: rotate(360deg); } }
@@ -387,21 +356,6 @@ select { background: var(--theme-color-background-panel, #fff); width: 100%; pad
387
356
  Template: /*html*/`
388
357
  <h1>Retold Data Cloner</h1>
389
358
 
390
- <!-- Live Status Bar (Expandable) -->
391
- <div id="liveStatusBar" class="live-status-bar phase-idle" style="position:relative">
392
- <div class="live-status-header" onclick="pict.views['DataCloner-Layout'].toggleStatusDetail()">
393
- <div class="live-status-dot"></div>
394
- <div class="live-status-message" id="liveStatusMessage">Idle</div>
395
- <div class="live-status-meta" id="liveStatusMeta"></div>
396
- <div class="live-status-expand-toggle" id="liveStatusToggle">&#9660;</div>
397
- </div>
398
- <div class="live-status-detail" id="liveStatusDetail" style="display:none">
399
- <div id="DataCloner-Throughput-Histogram"></div>
400
- <div id="DataCloner-StatusDetail-Container"></div>
401
- </div>
402
- <div class="live-status-progress-bar"><div class="live-status-progress-fill" id="liveStatusProgressFill" style="width:0%"></div></div>
403
- </div>
404
-
405
359
  <!-- Expand / Collapse All -->
406
360
  <div class="accordion-controls">
407
361
  <button onclick="pict.views['DataCloner-Layout'].expandAllSections()">Expand All</button>
@@ -424,7 +378,7 @@ select { background: var(--theme-color-background-panel, #fff); width: 100%; pad
424
378
  {
425
379
  RenderableHash: 'DataCloner-Layout',
426
380
  TemplateHash: 'DataCloner-Layout',
427
- DestinationAddress: '#DataCloner-Application-Container'
381
+ DestinationAddress: '#DataCloner-Workspace'
428
382
  }
429
383
  ]
430
384
  };
@@ -0,0 +1,61 @@
1
+ 'use strict';
2
+
3
+ const libPictView = require('pict-view');
4
+
5
+ class DataClonerSettingsPanelView extends libPictView
6
+ {
7
+ onAfterRender(pRenderable, pAddress, pRecord, pContent)
8
+ {
9
+ if (this.pict.CSSMap) { this.pict.CSSMap.injectCSS(); }
10
+
11
+ let tmpTheme = this.pict.providers && this.pict.providers['Theme-Section'];
12
+ if (tmpTheme && typeof tmpTheme.mount === 'function')
13
+ {
14
+ tmpTheme.mount({
15
+ Container: '#DataCloner-Settings-Theme',
16
+ Views: ['Picker', 'ModeToggle', 'ScaleSelect']
17
+ });
18
+ }
19
+ return super.onAfterRender(pRenderable, pAddress, pRecord, pContent);
20
+ }
21
+ }
22
+
23
+ module.exports = DataClonerSettingsPanelView;
24
+
25
+ module.exports.default_configuration =
26
+ {
27
+ ViewIdentifier: 'DataCloner-SettingsPanel',
28
+ DefaultRenderable: 'DataCloner-SettingsPanel',
29
+ DefaultDestinationAddress: '#DataCloner-Settings-Panel',
30
+ AutoRender: false,
31
+ CSS: /*css*/`
32
+ .rds-settings-body {
33
+ padding: 12px;
34
+ display: flex; flex-direction: column; gap: 16px;
35
+ color: var(--theme-color-text-primary, #333333);
36
+ }
37
+ .rds-settings-section { display: flex; flex-direction: column; gap: 6px; }
38
+ .rds-settings-label {
39
+ font-size: 0.85em;
40
+ font-weight: 600;
41
+ text-transform: uppercase;
42
+ letter-spacing: 0.04em;
43
+ color: var(--theme-color-text-secondary, #555555);
44
+ }
45
+ `,
46
+ Templates: [{
47
+ Hash: 'DataCloner-SettingsPanel',
48
+ Template: /*html*/`
49
+ <div class="rds-settings-body">
50
+ <div class="rds-settings-section">
51
+ <div class="rds-settings-label">Appearance</div>
52
+ <div id="DataCloner-Settings-Theme"></div>
53
+ </div>
54
+ </div>`
55
+ }],
56
+ Renderables: [{
57
+ RenderableHash: 'DataCloner-SettingsPanel',
58
+ TemplateHash: 'DataCloner-SettingsPanel',
59
+ DestinationAddress: '#DataCloner-Settings-Panel'
60
+ }]
61
+ };