retold-data-service 2.1.2 → 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 (80) 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/views/PictView-ComprehensionLoader-Layout.js +28 -74
  35. package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-Load.js +17 -17
  36. package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-SettingsPanel.js +62 -0
  37. package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-Shell.js +142 -0
  38. package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-StatusBar.js +125 -0
  39. package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-StatusDetail.js +89 -0
  40. package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-TopBar-Nav.js +42 -0
  41. package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-TopBar-User.js +48 -0
  42. package/source/services/comprehension-loader/web/comprehension-loader.js +5415 -6183
  43. package/source/services/comprehension-loader/web/comprehension-loader.js.map +1 -1
  44. package/source/services/comprehension-loader/web/comprehension-loader.min.js +75 -1
  45. package/source/services/comprehension-loader/web/comprehension-loader.min.js.map +1 -1
  46. package/source/services/comprehension-loader/web/favicons/favicon-dark.svg +13 -0
  47. package/source/services/comprehension-loader/web/favicons/favicon-light.svg +13 -0
  48. package/source/services/comprehension-loader/web/favicons/favicon.svg +13 -0
  49. package/source/services/comprehension-loader/web/index.html +3 -0
  50. package/source/services/comprehension-loader/web/pict.min.js +12 -0
  51. package/source/services/data-cloner/DataCloner-Command-Headless.js +2 -1
  52. package/source/services/data-cloner/DataCloner-Command-Sync.js +110 -75
  53. package/source/services/data-cloner/pict-app/Pict-Application-DataCloner.js +70 -47
  54. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Export.js +3 -3
  55. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Layout.js +40 -86
  56. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-SettingsPanel.js +61 -0
  57. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Shell.js +136 -0
  58. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-StatusBar.js +117 -0
  59. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-StatusDetail.js +81 -0
  60. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Sync.js +18 -18
  61. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-TopBar-Nav.js +42 -0
  62. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-TopBar-User.js +48 -0
  63. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-ViewData.js +2 -2
  64. package/source/services/data-cloner/web/data-cloner.js +5772 -7986
  65. package/source/services/data-cloner/web/data-cloner.js.map +1 -1
  66. package/source/services/data-cloner/web/data-cloner.min.js +75 -1
  67. package/source/services/data-cloner/web/data-cloner.min.js.map +1 -1
  68. package/source/services/data-cloner/web/favicons/favicon-dark.svg +13 -0
  69. package/source/services/data-cloner/web/favicons/favicon-light.svg +13 -0
  70. package/source/services/data-cloner/web/favicons/favicon.svg +13 -0
  71. package/source/services/data-cloner/web/favicons/favicons/favicon-dark.svg +13 -0
  72. package/source/services/data-cloner/web/favicons/favicons/favicon-light.svg +13 -0
  73. package/source/services/data-cloner/web/favicons/favicons/favicon.svg +13 -0
  74. package/source/services/data-cloner/web/index.html +3 -0
  75. package/source/services/data-cloner/web/pict.min.js +12 -0
  76. package/test/Bundles_smoke_tests.js +43 -0
  77. package/test/ComprehensionLoader_smoke_tests.js +95 -0
  78. package/test/DataCloner-RuntimeOverrides_tests.js +344 -0
  79. package/test/DataCloner_smoke_tests.js +87 -0
  80. package/docs/css/docuserve.css +0 -327
@@ -253,7 +253,8 @@ module.exports = (pDataClonerService, pConfig, pCLIOptions, fCallback) =>
253
253
  MaxRecordsPerEntity: tmpMaxRecords || tmpSync.MaxRecordsPerEntity || 0,
254
254
  DateTimePrecisionMS: tmpSync.DateTimePrecisionMS,
255
255
  BackSyncTimeLimit: tmpSync.BackSyncTimeLimit,
256
- TrueUpPageSize: tmpSync.TrueUpPageSize
256
+ TrueUpPageSize: tmpSync.TrueUpPageSize,
257
+ SyncEntityOptions: tmpSync.SyncEntityOptions
257
258
  });
258
259
 
259
260
  tmpFable.log.info(`Headless: Starting ${tmpSyncBody.SyncMode} sync...`);
@@ -10,6 +10,109 @@
10
10
  const libFableLog = require('fable-log');
11
11
  const libPath = require('path');
12
12
 
13
+ // Runtime-overridable sync properties for the /clone/sync/start route.
14
+ // Each entry is a coerce function returning the value to apply, or null
15
+ // if the input is invalid. Drives both the top-level body overrides and
16
+ // the per-entity `SyncEntityOptions` overrides — adding a new knob is a
17
+ // one-line change here.
18
+ const _RuntimeSyncProperties = {
19
+ BackSyncTimeLimit: (pVal) => { let tmpN = parseInt(pVal, 10); return (!isNaN(tmpN) && tmpN > 0) ? tmpN : null; },
20
+ MaxRecordsPerEntity: (pVal) => { let tmpN = parseInt(pVal, 10); return (!isNaN(tmpN) && tmpN > 0) ? tmpN : null; },
21
+ DateTimePrecisionMS: (pVal) => { let tmpN = parseInt(pVal, 10); return !isNaN(tmpN) ? tmpN : null; },
22
+ TrueUpPageSize: (pVal) => { let tmpN = parseInt(pVal, 10); return (!isNaN(tmpN) && tmpN > 0) ? tmpN : null; },
23
+ UseAdvancedIDPagination: (pVal) => !!pVal,
24
+ SyncDeletedRecords: (pVal) => !!pVal
25
+ };
26
+
27
+ /**
28
+ * Apply runtime sync overrides from a request body to a live MeadowSync.
29
+ *
30
+ * Mutates `pMeadowSync.MeadowSyncEntities[*]` properties, plus mirrors the
31
+ * "base" globals onto the MeadowSync orchestrator and `cloneState.SyncDeletedRecords`.
32
+ * Top-level body keys form the base config; per-entity overrides under
33
+ * `pBody.SyncEntityOptions[<TableName>]` are layered on top (per-entity wins).
34
+ *
35
+ * Only properties listed in `_RuntimeSyncProperties` are honored; unknown keys
36
+ * and invalid values are silently ignored. Unknown entity names produce a warn.
37
+ *
38
+ * @param {Object} pBody - Request body (or any plain object with the same shape).
39
+ * @param {Object} pMeadowSync - MeadowSync orchestrator instance with `MeadowSyncEntities`.
40
+ * @param {Object} pCloneState - Clone-state object whose `SyncDeletedRecords` mirrors the base.
41
+ * @param {Object} pLog - Logger with `info(msg)` and `warn(msg)`.
42
+ */
43
+ const applyRuntimeSyncOverrides = (pBody, pMeadowSync, pCloneState, pLog) =>
44
+ {
45
+ // Compute the base runtime config from top-level body keys.
46
+ let tmpBase = {};
47
+ let tmpBaseSchemaKeys = Object.keys(_RuntimeSyncProperties);
48
+ for (let i = 0; i < tmpBaseSchemaKeys.length; i++)
49
+ {
50
+ let tmpKey = tmpBaseSchemaKeys[i];
51
+ if (!pBody.hasOwnProperty(tmpKey)) continue;
52
+ let tmpCoerced = _RuntimeSyncProperties[tmpKey](pBody[tmpKey]);
53
+ if (tmpCoerced === null) continue;
54
+ tmpBase[tmpKey] = tmpCoerced;
55
+ }
56
+
57
+ let tmpPerEntity = (pBody.SyncEntityOptions && typeof(pBody.SyncEntityOptions) === 'object') ? pBody.SyncEntityOptions : {};
58
+
59
+ // Mirror base values onto the MeadowSync orchestrator + cloneState.
60
+ let tmpBaseKeys = Object.keys(tmpBase);
61
+ for (let i = 0; i < tmpBaseKeys.length; i++)
62
+ {
63
+ if (pMeadowSync.hasOwnProperty(tmpBaseKeys[i]))
64
+ {
65
+ pMeadowSync[tmpBaseKeys[i]] = tmpBase[tmpBaseKeys[i]];
66
+ }
67
+ }
68
+ if (tmpBase.hasOwnProperty('SyncDeletedRecords'))
69
+ {
70
+ pCloneState.SyncDeletedRecords = tmpBase.SyncDeletedRecords;
71
+ }
72
+
73
+ // Apply base to every entity.
74
+ let tmpAllEntityNames = Object.keys(pMeadowSync.MeadowSyncEntities || {});
75
+ for (let i = 0; i < tmpAllEntityNames.length; i++)
76
+ {
77
+ let tmpEntity = pMeadowSync.MeadowSyncEntities[tmpAllEntityNames[i]];
78
+ for (let j = 0; j < tmpBaseKeys.length; j++)
79
+ {
80
+ tmpEntity[tmpBaseKeys[j]] = tmpBase[tmpBaseKeys[j]];
81
+ }
82
+ }
83
+
84
+ // Layer per-entity overrides on top. Log per entity that actually had
85
+ // overrides applied; warn on unknown entity names.
86
+ let tmpPerEntityNames = Object.keys(tmpPerEntity);
87
+ for (let i = 0; i < tmpPerEntityNames.length; i++)
88
+ {
89
+ let tmpEntityName = tmpPerEntityNames[i];
90
+ let tmpEntity = pMeadowSync.MeadowSyncEntities && pMeadowSync.MeadowSyncEntities[tmpEntityName];
91
+ if (!tmpEntity)
92
+ {
93
+ pLog.warn(`SyncEntityOptions: no sync entity ${tmpEntityName} loaded; ignoring overrides for it.`);
94
+ continue;
95
+ }
96
+ let tmpOverrides = tmpPerEntity[tmpEntityName];
97
+ if (!tmpOverrides || typeof(tmpOverrides) !== 'object') continue;
98
+ let tmpApplied = [];
99
+ let tmpOverrideKeys = Object.keys(tmpOverrides);
100
+ for (let j = 0; j < tmpOverrideKeys.length; j++)
101
+ {
102
+ let tmpKey = tmpOverrideKeys[j];
103
+ if (!_RuntimeSyncProperties.hasOwnProperty(tmpKey)) continue;
104
+ let tmpCoerced = _RuntimeSyncProperties[tmpKey](tmpOverrides[tmpKey]);
105
+ if (tmpCoerced === null) continue;
106
+ tmpEntity[tmpKey] = tmpCoerced;
107
+ tmpApplied.push(`${tmpKey}=${tmpCoerced}`);
108
+ }
109
+ if (tmpApplied.length > 0)
110
+ {
111
+ pLog.info(`SyncEntityOptions: ${tmpEntityName} ← ${tmpApplied.join(', ')}`);
112
+ }
113
+ }
114
+ };
115
+
13
116
  module.exports = (pDataClonerService, pOratorServiceServer) =>
14
117
  {
15
118
  let tmpFable = pDataClonerService.fable;
@@ -43,81 +146,9 @@ module.exports = (pDataClonerService, pOratorServiceServer) =>
43
146
  let tmpRequestedMode = tmpBody.SyncMode || 'Initial';
44
147
  let tmpMaxRecords = parseInt(tmpBody.MaxRecordsPerEntity, 10) || 0;
45
148
 
46
- // Update SyncDeletedRecords from request if provided
47
- if (tmpBody.hasOwnProperty('SyncDeletedRecords'))
48
- {
49
- tmpCloneState.SyncDeletedRecords = !!tmpBody.SyncDeletedRecords;
50
- let tmpEntityNames = Object.keys(tmpFable.MeadowSync.MeadowSyncEntities);
51
- for (let i = 0; i < tmpEntityNames.length; i++)
52
- {
53
- tmpFable.MeadowSync.MeadowSyncEntities[tmpEntityNames[i]].SyncDeletedRecords = tmpCloneState.SyncDeletedRecords;
54
- }
55
- }
56
-
57
- // Update MaxRecordsPerEntity on sync entities
58
- if (tmpMaxRecords > 0)
59
- {
60
- let tmpEntityNames = Object.keys(tmpFable.MeadowSync.MeadowSyncEntities);
61
- for (let i = 0; i < tmpEntityNames.length; i++)
62
- {
63
- tmpFable.MeadowSync.MeadowSyncEntities[tmpEntityNames[i]].MaxRecordsPerEntity = tmpMaxRecords;
64
- }
65
- }
66
-
67
- // Update UseAdvancedIDPagination on all sync entities
68
- if (tmpBody.hasOwnProperty('UseAdvancedIDPagination'))
69
- {
70
- let tmpEntityNames = Object.keys(tmpFable.MeadowSync.MeadowSyncEntities);
71
- for (let i = 0; i < tmpEntityNames.length; i++)
72
- {
73
- tmpFable.MeadowSync.MeadowSyncEntities[tmpEntityNames[i]].UseAdvancedIDPagination = !!tmpBody.UseAdvancedIDPagination;
74
- }
75
- }
76
-
77
- // Update DateTimePrecisionMS on MeadowSync and all sync entities
78
- if (tmpBody.hasOwnProperty('DateTimePrecisionMS'))
79
- {
80
- let tmpPrecision = parseInt(tmpBody.DateTimePrecisionMS, 10);
81
- if (!isNaN(tmpPrecision))
82
- {
83
- tmpFable.MeadowSync.DateTimePrecisionMS = tmpPrecision;
84
- let tmpEntityNames = Object.keys(tmpFable.MeadowSync.MeadowSyncEntities);
85
- for (let i = 0; i < tmpEntityNames.length; i++)
86
- {
87
- tmpFable.MeadowSync.MeadowSyncEntities[tmpEntityNames[i]].DateTimePrecisionMS = tmpPrecision;
88
- }
89
- }
90
- }
91
-
92
- // Update BackSyncTimeLimit on MeadowSync and all sync entities
93
- if (tmpBody.hasOwnProperty('BackSyncTimeLimit'))
94
- {
95
- let tmpBackSyncTimeLimit = parseInt(tmpBody.BackSyncTimeLimit, 10);
96
- if (!isNaN(tmpBackSyncTimeLimit) && tmpBackSyncTimeLimit > 0)
97
- {
98
- tmpFable.MeadowSync.BackSyncTimeLimit = tmpBackSyncTimeLimit;
99
- let tmpEntityNames = Object.keys(tmpFable.MeadowSync.MeadowSyncEntities);
100
- for (let i = 0; i < tmpEntityNames.length; i++)
101
- {
102
- tmpFable.MeadowSync.MeadowSyncEntities[tmpEntityNames[i]].BackSyncTimeLimit = tmpBackSyncTimeLimit;
103
- }
104
- }
105
- }
106
-
107
- // Update TrueUpPageSize on MeadowSync and all sync entities
108
- if (tmpBody.hasOwnProperty('TrueUpPageSize'))
109
- {
110
- let tmpTrueUpPageSize = parseInt(tmpBody.TrueUpPageSize, 10);
111
- if (!isNaN(tmpTrueUpPageSize) && tmpTrueUpPageSize > 0)
112
- {
113
- tmpFable.MeadowSync.TrueUpPageSize = tmpTrueUpPageSize;
114
- let tmpEntityNames = Object.keys(tmpFable.MeadowSync.MeadowSyncEntities);
115
- for (let i = 0; i < tmpEntityNames.length; i++)
116
- {
117
- tmpFable.MeadowSync.MeadowSyncEntities[tmpEntityNames[i]].TrueUpPageSize = tmpTrueUpPageSize;
118
- }
119
- }
120
- }
149
+ // Apply global + per-entity runtime sync overrides. See
150
+ // applyRuntimeSyncOverrides() at module scope.
151
+ applyRuntimeSyncOverrides(tmpBody, tmpFable.MeadowSync, tmpCloneState, tmpFable.log);
121
152
 
122
153
  // If no tables specified, sync all entities
123
154
  if (tmpSelectedTables.length === 0)
@@ -579,3 +610,7 @@ module.exports = (pDataClonerService, pOratorServiceServer) =>
579
610
  return fNext();
580
611
  });
581
612
  };
613
+
614
+ // Exposed for unit tests.
615
+ module.exports._RuntimeSyncProperties = _RuntimeSyncProperties;
616
+ module.exports.applyRuntimeSyncOverrides = applyRuntimeSyncOverrides;
@@ -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>