@trops/dash-core 0.1.91 → 0.1.93

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.
@@ -593,6 +593,7 @@ const DASHBOARD_CONFIG_COMPATIBILITY$1 = "dashboard-config-compatibility";
593
593
  const DASHBOARD_CONFIG_PUBLISH$1 = "dashboard-config-publish";
594
594
  const DASHBOARD_CONFIG_PREVIEW$1 = "dashboard-config-preview";
595
595
  const DASHBOARD_CONFIG_CHECK_UPDATES$1 = "dashboard-config-check-updates";
596
+ const DASHBOARD_CONFIG_PROVIDER_SETUP$1 = "dashboard-config-provider-setup";
596
597
 
597
598
  var dashboardConfigEvents$1 = {
598
599
  DASHBOARD_CONFIG_EXPORT: DASHBOARD_CONFIG_EXPORT$1,
@@ -602,6 +603,25 @@ var dashboardConfigEvents$1 = {
602
603
  DASHBOARD_CONFIG_PUBLISH: DASHBOARD_CONFIG_PUBLISH$1,
603
604
  DASHBOARD_CONFIG_PREVIEW: DASHBOARD_CONFIG_PREVIEW$1,
604
605
  DASHBOARD_CONFIG_CHECK_UPDATES: DASHBOARD_CONFIG_CHECK_UPDATES$1,
606
+ DASHBOARD_CONFIG_PROVIDER_SETUP: DASHBOARD_CONFIG_PROVIDER_SETUP$1,
607
+ };
608
+
609
+ /**
610
+ * Dashboard Ratings Events
611
+ *
612
+ * IPC event constants for dashboard ratings CRUD.
613
+ */
614
+
615
+ const DASHBOARD_RATING_SAVE = "dashboard-rating-save";
616
+ const DASHBOARD_RATING_GET = "dashboard-rating-get";
617
+ const DASHBOARD_RATING_LIST = "dashboard-rating-list";
618
+ const DASHBOARD_RATING_DELETE = "dashboard-rating-delete";
619
+
620
+ var dashboardRatingsEvents$1 = {
621
+ DASHBOARD_RATING_SAVE,
622
+ DASHBOARD_RATING_GET,
623
+ DASHBOARD_RATING_LIST,
624
+ DASHBOARD_RATING_DELETE,
605
625
  };
606
626
 
607
627
  /**
@@ -626,6 +646,7 @@ const openaiEvents = openaiEvents$1;
626
646
  const llmEvents = llmEvents$1;
627
647
  const clientCacheEvents = clientCacheEvents$1;
628
648
  const dashboardConfigEvents = dashboardConfigEvents$1;
649
+ const dashboardRatingsEvents = dashboardRatingsEvents$1;
629
650
 
630
651
  const publicEvents = {
631
652
  ...dataEvents,
@@ -649,6 +670,7 @@ var events$8 = {
649
670
  ...llmEvents,
650
671
  ...clientCacheEvents,
651
672
  ...dashboardConfigEvents,
673
+ ...dashboardRatingsEvents,
652
674
  };
653
675
 
654
676
  /**
@@ -742,7 +764,7 @@ var secureStoreController$1 = {
742
764
  getData: getData$1,
743
765
  };
744
766
 
745
- const path$e = require$$1$1;
767
+ const path$f = require$$1$1;
746
768
  const {
747
769
  readFileSync,
748
770
  writeFileSync: writeFileSync$4,
@@ -760,7 +782,7 @@ const {
760
782
  function ensureDirectoryExistence$2(filePath) {
761
783
  try {
762
784
  // isDirectory
763
- var dirname = path$e.dirname(filePath);
785
+ var dirname = path$f.dirname(filePath);
764
786
  // check if the directory exists...return true
765
787
  // if not, we can pass in the dirname as the filepath
766
788
  // and check each directory recursively.
@@ -811,7 +833,7 @@ function checkDirectory$1(dir) {
811
833
  * @param {string} filepath path to the file
812
834
  * @returns
813
835
  */
814
- function getFileContents$7(filepath, defaultReturn = []) {
836
+ function getFileContents$8(filepath, defaultReturn = []) {
815
837
  try {
816
838
  // lets first make sure all is there...
817
839
  ensureDirectoryExistence$2(filepath);
@@ -852,7 +874,7 @@ function getFileContents$7(filepath, defaultReturn = []) {
852
874
  }
853
875
  }
854
876
 
855
- function writeToFile$2(filename, data) {
877
+ function writeToFile$3(filename, data) {
856
878
  try {
857
879
  // write the new pages configuration back to the file
858
880
  return writeFileSync$4(filename, data);
@@ -875,7 +897,7 @@ function removeFilesFromDirectory(directory, excludeFiles = []) {
875
897
 
876
898
  for (const file of files) {
877
899
  if (!excludeFiles.includes(file)) {
878
- unlinkSync(path$e.join(directory, file), (err) => {
900
+ unlinkSync(path$f.join(directory, file), (err) => {
879
901
  if (err) throw err;
880
902
  });
881
903
  }
@@ -886,19 +908,19 @@ function removeFilesFromDirectory(directory, excludeFiles = []) {
886
908
 
887
909
  var file = {
888
910
  ensureDirectoryExistence: ensureDirectoryExistence$2,
889
- getFileContents: getFileContents$7,
890
- writeToFile: writeToFile$2,
911
+ getFileContents: getFileContents$8,
912
+ writeToFile: writeToFile$3,
891
913
  removeFilesFromDirectory,
892
914
  checkDirectory: checkDirectory$1,
893
915
  };
894
916
 
895
- const { app: app$8 } = require$$0$1;
896
- const path$d = require$$1$1;
917
+ const { app: app$9 } = require$$0$1;
918
+ const path$e = require$$1$1;
897
919
  const { writeFileSync: writeFileSync$3 } = require$$2;
898
- const { getFileContents: getFileContents$6 } = file;
920
+ const { getFileContents: getFileContents$7 } = file;
899
921
 
900
922
  const configFilename$5 = "workspaces.json";
901
- const appName$6 = "Dashboard";
923
+ const appName$7 = "Dashboard";
902
924
 
903
925
  const workspaceController$1 = {
904
926
  /**
@@ -940,13 +962,13 @@ const workspaceController$1 = {
940
962
  saveWorkspaceForApplication: (win, appId, workspaceObject) => {
941
963
  try {
942
964
  // filename to the pages file (live pages)
943
- const filename = path$d.join(
944
- app$8.getPath("userData"),
945
- appName$6,
965
+ const filename = path$e.join(
966
+ app$9.getPath("userData"),
967
+ appName$7,
946
968
  appId,
947
969
  configFilename$5,
948
970
  );
949
- const workspacesArray = getFileContents$6(filename);
971
+ const workspacesArray = getFileContents$7(filename);
950
972
 
951
973
  // lets check to see if we already have this one!
952
974
  let indexOfExistingItem = null;
@@ -989,13 +1011,13 @@ const workspaceController$1 = {
989
1011
  saveMenuItemsForApplication: (win, appId, menuItems) => {
990
1012
  try {
991
1013
  // filename to the workspaces file
992
- const filename = path$d.join(
993
- app$8.getPath("userData"),
994
- appName$6,
1014
+ const filename = path$e.join(
1015
+ app$9.getPath("userData"),
1016
+ appName$7,
995
1017
  appId,
996
1018
  configFilename$5,
997
1019
  );
998
- const workspacesArray = getFileContents$6(filename);
1020
+ const workspacesArray = getFileContents$7(filename);
999
1021
 
1000
1022
  // Update menu items for workspaces
1001
1023
  // This assumes menuItems is an object with workspace IDs as keys
@@ -1038,13 +1060,13 @@ const workspaceController$1 = {
1038
1060
  */
1039
1061
  deleteWorkspaceForApplication: (win, appId, workspaceId) => {
1040
1062
  try {
1041
- const filename = path$d.join(
1042
- app$8.getPath("userData"),
1043
- appName$6,
1063
+ const filename = path$e.join(
1064
+ app$9.getPath("userData"),
1065
+ appName$7,
1044
1066
  appId,
1045
1067
  configFilename$5,
1046
1068
  );
1047
- const workspacesArray = getFileContents$6(filename);
1069
+ const workspacesArray = getFileContents$7(filename);
1048
1070
 
1049
1071
  const filtered = workspacesArray.filter(
1050
1072
  (workspace) => workspace.id !== workspaceId,
@@ -1072,14 +1094,14 @@ const workspaceController$1 = {
1072
1094
 
1073
1095
  listWorkspacesForApplication: (win, appId) => {
1074
1096
  try {
1075
- const filename = path$d.join(
1076
- app$8.getPath("userData"),
1077
- appName$6,
1097
+ const filename = path$e.join(
1098
+ app$9.getPath("userData"),
1099
+ appName$7,
1078
1100
  appId,
1079
1101
  configFilename$5,
1080
1102
  );
1081
1103
 
1082
- const workspacesArray = getFileContents$6(filename);
1104
+ const workspacesArray = getFileContents$7(filename);
1083
1105
  console.log(
1084
1106
  `[workspaceController] Loaded ${workspacesArray.length} workspaces for appId: ${appId}`,
1085
1107
  );
@@ -1100,13 +1122,13 @@ const workspaceController$1 = {
1100
1122
 
1101
1123
  listMenuItemsForApplication: (win, appId) => {
1102
1124
  try {
1103
- const filename = path$d.join(
1104
- app$8.getPath("userData"),
1105
- appName$6,
1125
+ const filename = path$e.join(
1126
+ app$9.getPath("userData"),
1127
+ appName$7,
1106
1128
  appId,
1107
1129
  configFilename$5,
1108
1130
  );
1109
- const workspacesArray = getFileContents$6(filename);
1131
+ const workspacesArray = getFileContents$7(filename);
1110
1132
 
1111
1133
  // Extract unique menu items from workspaces
1112
1134
  // Each workspace can have a menuId, we need to build the menu items list
@@ -1144,13 +1166,13 @@ const workspaceController$1 = {
1144
1166
 
1145
1167
  var workspaceController_1 = workspaceController$1;
1146
1168
 
1147
- const { app: app$7 } = require$$0$1;
1148
- const path$c = require$$1$1;
1169
+ const { app: app$8 } = require$$0$1;
1170
+ const path$d = require$$1$1;
1149
1171
  const { writeFileSync: writeFileSync$2 } = require$$2;
1150
- const { getFileContents: getFileContents$5 } = file;
1172
+ const { getFileContents: getFileContents$6 } = file;
1151
1173
 
1152
1174
  const configFilename$4 = "themes.json";
1153
- const appName$5 = "Dashboard";
1175
+ const appName$6 = "Dashboard";
1154
1176
 
1155
1177
  const themeController$1 = {
1156
1178
  /**
@@ -1165,13 +1187,13 @@ const themeController$1 = {
1165
1187
  saveThemeForApplication: (win, appId, name, obj) => {
1166
1188
  try {
1167
1189
  // filename to the pages file (live pages)
1168
- const filename = path$c.join(
1169
- app$7.getPath("userData"),
1170
- appName$5,
1190
+ const filename = path$d.join(
1191
+ app$8.getPath("userData"),
1192
+ appName$6,
1171
1193
  appId,
1172
1194
  configFilename$4,
1173
1195
  );
1174
- const data = getFileContents$5(filename, {});
1196
+ const data = getFileContents$6(filename, {});
1175
1197
 
1176
1198
  // Add/update the theme based on the name
1177
1199
  if (name in data === false) {
@@ -1211,14 +1233,14 @@ const themeController$1 = {
1211
1233
  */
1212
1234
  listThemesForApplication: (win, appId) => {
1213
1235
  try {
1214
- const filename = path$c.join(
1215
- app$7.getPath("userData"),
1216
- appName$5,
1236
+ const filename = path$d.join(
1237
+ app$8.getPath("userData"),
1238
+ appName$6,
1217
1239
  appId,
1218
1240
  configFilename$4,
1219
1241
  );
1220
1242
 
1221
- const data = getFileContents$5(filename, {});
1243
+ const data = getFileContents$6(filename, {});
1222
1244
 
1223
1245
  console.log(
1224
1246
  "[themeController] Loading themes from:",
@@ -1253,13 +1275,13 @@ const themeController$1 = {
1253
1275
  */
1254
1276
  deleteThemeForApplication: (win, appId, themeKey) => {
1255
1277
  try {
1256
- const filename = path$c.join(
1257
- app$7.getPath("userData"),
1258
- appName$5,
1278
+ const filename = path$d.join(
1279
+ app$8.getPath("userData"),
1280
+ appName$6,
1259
1281
  appId,
1260
1282
  configFilename$4,
1261
1283
  );
1262
- const data = getFileContents$5(filename, {});
1284
+ const data = getFileContents$6(filename, {});
1263
1285
 
1264
1286
  if (themeKey in data) {
1265
1287
  delete data[themeKey];
@@ -1300,8 +1322,8 @@ var xmlParser = require$$3;
1300
1322
  var JSONStream$1 = require$$4;
1301
1323
  const stream = require$$5;
1302
1324
  var csv = require$$6;
1303
- const path$b = require$$1$1;
1304
- const { app: app$6 } = require$$0$1;
1325
+ const path$c = require$$1$1;
1326
+ const { app: app$7 } = require$$0$1;
1305
1327
  const { ensureDirectoryExistence: ensureDirectoryExistence$1 } = file;
1306
1328
 
1307
1329
  const TRANSFORM_APP_NAME = "Dashboard";
@@ -1589,18 +1611,18 @@ let Transform$1 = class Transform {
1589
1611
  }
1590
1612
 
1591
1613
  // Validate file paths are within app data directory
1592
- const appDataDir = path$b.join(app$6.getPath("userData"), TRANSFORM_APP_NAME);
1593
- const resolvedFilepath = path$b.resolve(filepath);
1594
- const resolvedOutFilepath = path$b.resolve(outFilepath);
1614
+ const appDataDir = path$c.join(app$7.getPath("userData"), TRANSFORM_APP_NAME);
1615
+ const resolvedFilepath = path$c.resolve(filepath);
1616
+ const resolvedOutFilepath = path$c.resolve(outFilepath);
1595
1617
 
1596
- if (!resolvedFilepath.startsWith(appDataDir + path$b.sep)) {
1618
+ if (!resolvedFilepath.startsWith(appDataDir + path$c.sep)) {
1597
1619
  return reject(
1598
1620
  new Error(
1599
1621
  "Input file path must be within the application data directory",
1600
1622
  ),
1601
1623
  );
1602
1624
  }
1603
- if (!resolvedOutFilepath.startsWith(appDataDir + path$b.sep)) {
1625
+ if (!resolvedOutFilepath.startsWith(appDataDir + path$c.sep)) {
1604
1626
  return reject(
1605
1627
  new Error(
1606
1628
  "Output file path must be within the application data directory",
@@ -3458,18 +3480,18 @@ async function extractColorsFromImageURL$2(url, toDirectory) {
3458
3480
 
3459
3481
  var color = { extractColorsFromImageURL: extractColorsFromImageURL$2 };
3460
3482
 
3461
- const { app: app$5 } = require$$0$1;
3483
+ const { app: app$6 } = require$$0$1;
3462
3484
  var fs$7 = require$$2;
3463
- const path$a = require$$1$1;
3485
+ const path$b = require$$1$1;
3464
3486
  const events$5 = events$8;
3465
- const { getFileContents: getFileContents$4, writeToFile: writeToFile$1 } = file;
3487
+ const { getFileContents: getFileContents$5, writeToFile: writeToFile$2 } = file;
3466
3488
 
3467
3489
  // Convert Json to Csv
3468
3490
  const ObjectsToCsv = require$$5$1;
3469
3491
  const Transform = transform;
3470
3492
  const { extractColorsFromImageURL: extractColorsFromImageURL$1 } = color;
3471
3493
  const https = require$$8;
3472
- const appName$4 = "Dashboard";
3494
+ const appName$5 = "Dashboard";
3473
3495
 
3474
3496
  const dataController$1 = {
3475
3497
  /**
@@ -3483,16 +3505,16 @@ const dataController$1 = {
3483
3505
  convertJsonToCsvFile: (win, appId, jsonObject, toFilename = "test.csv") => {
3484
3506
  try {
3485
3507
  // filename to the pages file (live pages)
3486
- const filename = path$a.join(
3487
- app$5.getPath("userData"),
3488
- appName$4,
3508
+ const filename = path$b.join(
3509
+ app$6.getPath("userData"),
3510
+ appName$5,
3489
3511
  appId,
3490
3512
  "data",
3491
3513
  toFilename,
3492
3514
  );
3493
3515
 
3494
3516
  // make sure the file exists...
3495
- const fileContents = getFileContents$4(filename, "");
3517
+ const fileContents = getFileContents$5(filename, "");
3496
3518
 
3497
3519
  const csv = new ObjectsToCsv(jsonObject);
3498
3520
 
@@ -3611,9 +3633,9 @@ const dataController$1 = {
3611
3633
  }
3612
3634
 
3613
3635
  // Validate toFilepath is within the app data directory
3614
- const appDataDir = path$a.join(app$5.getPath("userData"), appName$4);
3615
- const resolvedFilepath = path$a.resolve(toFilepath);
3616
- if (!resolvedFilepath.startsWith(appDataDir + path$a.sep)) {
3636
+ const appDataDir = path$b.join(app$6.getPath("userData"), appName$5);
3637
+ const resolvedFilepath = path$b.resolve(toFilepath);
3638
+ if (!resolvedFilepath.startsWith(appDataDir + path$b.sep)) {
3617
3639
  throw new Error(
3618
3640
  "File path must be within the application data directory",
3619
3641
  );
@@ -3760,9 +3782,9 @@ const dataController$1 = {
3760
3782
  try {
3761
3783
  if (data) {
3762
3784
  // filename to the pages file (live pages)
3763
- const toFilename = path$a.join(
3764
- app$5.getPath("userData"),
3765
- appName$4,
3785
+ const toFilename = path$b.join(
3786
+ app$6.getPath("userData"),
3787
+ appName$5,
3766
3788
  "data",
3767
3789
  filename,
3768
3790
  );
@@ -3770,7 +3792,7 @@ const dataController$1 = {
3770
3792
  //console.log("saving to file ", toFilename);
3771
3793
 
3772
3794
  // // call this to make sure the directory structure exists
3773
- let fileContents = getFileContents$4(toFilename, returnEmpty);
3795
+ let fileContents = getFileContents$5(toFilename, returnEmpty);
3774
3796
  if (fileContents === null || fileContents === "") {
3775
3797
  fileContents = JSON.stringify(returnEmpty);
3776
3798
  }
@@ -3786,7 +3808,7 @@ const dataController$1 = {
3786
3808
  const tempWriteContents = JSON.parse(fileContents);
3787
3809
  tempWriteContents[stamp] = data;
3788
3810
  writeContents = JSON.stringify(tempWriteContents);
3789
- writeToFile$1(toFilename, writeContents);
3811
+ writeToFile$2(toFilename, writeContents);
3790
3812
  }
3791
3813
 
3792
3814
  if (JSON.stringify(returnEmpty) === "[]") {
@@ -3795,20 +3817,20 @@ const dataController$1 = {
3795
3817
  writeContents = JSON.stringify(tempWriteContents);
3796
3818
  // writeContents = JSON.parse(fileContents);
3797
3819
  // writeContents.push({ [stamp]: data });
3798
- writeToFile$1(toFilename, writeContents);
3820
+ writeToFile$2(toFilename, writeContents);
3799
3821
  }
3800
3822
  } else {
3801
3823
  // overwrite existing
3802
3824
  writeContents = JSON.stringify(data);
3803
3825
  if (JSON.stringify(returnEmpty) === "{}") {
3804
- writeToFile$1(
3826
+ writeToFile$2(
3805
3827
  toFilename,
3806
3828
  writeContents,
3807
3829
  // JSON.stringify({ [stamp]: data })
3808
3830
  );
3809
3831
  }
3810
3832
  if (JSON.stringify(returnEmpty) === "[]") {
3811
- writeToFile$1(
3833
+ writeToFile$2(
3812
3834
  toFilename,
3813
3835
  writeContents,
3814
3836
  // JSON.stringify([{ [stamp]: data }])
@@ -3842,15 +3864,15 @@ const dataController$1 = {
3842
3864
  try {
3843
3865
  if (filename) {
3844
3866
  // filename to the pages file (live pages)
3845
- const fromFilename = path$a.join(
3846
- app$5.getPath("userData"),
3847
- appName$4,
3867
+ const fromFilename = path$b.join(
3868
+ app$6.getPath("userData"),
3869
+ appName$5,
3848
3870
  "data",
3849
3871
  filename,
3850
3872
  );
3851
3873
  console.log("reading from file ", fromFilename, returnIfEmpty);
3852
3874
  // make sure the file exists...
3853
- const fileContents = getFileContents$4(fromFilename, returnIfEmpty);
3875
+ const fileContents = getFileContents$5(fromFilename, returnIfEmpty);
3854
3876
 
3855
3877
  console.log("file contents ", fileContents, fromFilename);
3856
3878
 
@@ -3920,9 +3942,9 @@ const dataController$1 = {
3920
3942
  try {
3921
3943
  console.log(url);
3922
3944
  const fileExtension = ".jpg";
3923
- const filename = path$a.join(
3924
- app$5.getPath("userData"),
3925
- appName$4,
3945
+ const filename = path$b.join(
3946
+ app$6.getPath("userData"),
3947
+ appName$5,
3926
3948
  "@algolia/dash-electron",
3927
3949
  "data",
3928
3950
  "imageExtract" + fileExtension,
@@ -3945,13 +3967,13 @@ var dataController_1 = dataController$1;
3945
3967
  * settingsController
3946
3968
  */
3947
3969
 
3948
- const { app: app$4 } = require$$0$1;
3949
- const path$9 = require$$1$1;
3970
+ const { app: app$5 } = require$$0$1;
3971
+ const path$a = require$$1$1;
3950
3972
  const fs$6 = require$$2;
3951
- const { getFileContents: getFileContents$3, writeToFile } = file;
3973
+ const { getFileContents: getFileContents$4, writeToFile: writeToFile$1 } = file;
3952
3974
 
3953
3975
  const configFilename$3 = "settings.json";
3954
- const appName$3 = "Dashboard";
3976
+ const appName$4 = "Dashboard";
3955
3977
 
3956
3978
  // Helper function to recursively copy directory
3957
3979
  function copyDirectory(source, destination) {
@@ -3961,8 +3983,8 @@ function copyDirectory(source, destination) {
3961
3983
 
3962
3984
  const files = fs$6.readdirSync(source);
3963
3985
  for (const file of files) {
3964
- const srcPath = path$9.join(source, file);
3965
- const destPath = path$9.join(destination, file);
3986
+ const srcPath = path$a.join(source, file);
3987
+ const destPath = path$a.join(destination, file);
3966
3988
  const stat = fs$6.lstatSync(srcPath);
3967
3989
 
3968
3990
  // Skip symlinks to prevent following links to sensitive files
@@ -3990,12 +4012,12 @@ const settingsController$1 = {
3990
4012
  try {
3991
4013
  if (data) {
3992
4014
  // <appId>/settings.json
3993
- const filename = path$9.join(
3994
- app$4.getPath("userData"),
3995
- appName$3,
4015
+ const filename = path$a.join(
4016
+ app$5.getPath("userData"),
4017
+ appName$4,
3996
4018
  configFilename$3,
3997
4019
  );
3998
- writeToFile(filename, JSON.stringify(data, null, 2));
4020
+ writeToFile$1(filename, JSON.stringify(data, null, 2));
3999
4021
  console.log("[settingsController] Settings saved successfully");
4000
4022
  // Return the data for ipcMain.handle() - modern promise-based approach
4001
4023
  return {
@@ -4026,13 +4048,13 @@ const settingsController$1 = {
4026
4048
  getSettingsForApplication: (win) => {
4027
4049
  try {
4028
4050
  // <appId>/settings.json
4029
- const filename = path$9.join(
4030
- app$4.getPath("userData"),
4031
- appName$3,
4051
+ const filename = path$a.join(
4052
+ app$5.getPath("userData"),
4053
+ appName$4,
4032
4054
  configFilename$3,
4033
4055
  );
4034
4056
  // make sure the file exists...
4035
- const fileContents = getFileContents$3(filename, {});
4057
+ const fileContents = getFileContents$4(filename, {});
4036
4058
  console.log("[settingsController] Settings loaded successfully");
4037
4059
  // Return the data for ipcMain.handle() - modern promise-based approach
4038
4060
  return {
@@ -4057,15 +4079,15 @@ const settingsController$1 = {
4057
4079
  */
4058
4080
  getDataDirectory: (win) => {
4059
4081
  try {
4060
- const settingsPath = path$9.join(
4061
- app$4.getPath("userData"),
4062
- appName$3,
4082
+ const settingsPath = path$a.join(
4083
+ app$5.getPath("userData"),
4084
+ appName$4,
4063
4085
  configFilename$3,
4064
4086
  );
4065
- const settings = getFileContents$3(settingsPath, {});
4087
+ const settings = getFileContents$4(settingsPath, {});
4066
4088
  const userDataDir =
4067
4089
  settings.userDataDirectory ||
4068
- path$9.join(app$4.getPath("userData"), appName$3);
4090
+ path$a.join(app$5.getPath("userData"), appName$4);
4069
4091
 
4070
4092
  console.log("[settingsController] Data directory retrieved successfully");
4071
4093
  // Return the data for ipcMain.handle() - modern promise-based approach
@@ -4102,14 +4124,14 @@ const settingsController$1 = {
4102
4124
  }
4103
4125
 
4104
4126
  // Update settings
4105
- const settingsPath = path$9.join(
4106
- app$4.getPath("userData"),
4107
- appName$3,
4127
+ const settingsPath = path$a.join(
4128
+ app$5.getPath("userData"),
4129
+ appName$4,
4108
4130
  configFilename$3,
4109
4131
  );
4110
- const settings = getFileContents$3(settingsPath, {});
4132
+ const settings = getFileContents$4(settingsPath, {});
4111
4133
  settings.userDataDirectory = newPath;
4112
- writeToFile(settingsPath, JSON.stringify(settings, null, 2));
4134
+ writeToFile$1(settingsPath, JSON.stringify(settings, null, 2));
4113
4135
 
4114
4136
  console.log("[settingsController] Data directory set successfully");
4115
4137
  // Return the data for ipcMain.handle() - modern promise-based approach
@@ -4136,20 +4158,20 @@ const settingsController$1 = {
4136
4158
  migrateDataDirectory: (win, oldPath, newPath) => {
4137
4159
  try {
4138
4160
  // Resolve paths to prevent traversal
4139
- const resolvedOldPath = path$9.resolve(oldPath);
4140
- const resolvedNewPath = path$9.resolve(newPath);
4161
+ const resolvedOldPath = path$a.resolve(oldPath);
4162
+ const resolvedNewPath = path$a.resolve(newPath);
4141
4163
 
4142
4164
  // Validate oldPath is the current configured data directory
4143
- const settingsCheckPath = path$9.join(
4144
- app$4.getPath("userData"),
4145
- appName$3,
4165
+ const settingsCheckPath = path$a.join(
4166
+ app$5.getPath("userData"),
4167
+ appName$4,
4146
4168
  configFilename$3,
4147
4169
  );
4148
- const currentSettings = getFileContents$3(settingsCheckPath, {});
4170
+ const currentSettings = getFileContents$4(settingsCheckPath, {});
4149
4171
  const currentDataDir =
4150
4172
  currentSettings.userDataDirectory ||
4151
- path$9.join(app$4.getPath("userData"), appName$3);
4152
- if (resolvedOldPath !== path$9.resolve(currentDataDir)) {
4173
+ path$a.join(app$5.getPath("userData"), appName$4);
4174
+ if (resolvedOldPath !== path$a.resolve(currentDataDir)) {
4153
4175
  throw new Error("Source path must be the current data directory");
4154
4176
  }
4155
4177
 
@@ -4185,14 +4207,14 @@ const settingsController$1 = {
4185
4207
  copyDirectory(resolvedOldPath, resolvedNewPath);
4186
4208
 
4187
4209
  // Update settings to use new path
4188
- const settingsPath = path$9.join(
4189
- app$4.getPath("userData"),
4190
- appName$3,
4210
+ const settingsPath = path$a.join(
4211
+ app$5.getPath("userData"),
4212
+ appName$4,
4191
4213
  configFilename$3,
4192
4214
  );
4193
- const settings = getFileContents$3(settingsPath, {});
4215
+ const settings = getFileContents$4(settingsPath, {});
4194
4216
  settings.userDataDirectory = resolvedNewPath;
4195
- writeToFile(settingsPath, JSON.stringify(settings, null, 2));
4217
+ writeToFile$1(settingsPath, JSON.stringify(settings, null, 2));
4196
4218
 
4197
4219
  console.log("[settingsController] Data directory migrated successfully");
4198
4220
  // Return the data for ipcMain.handle() - modern promise-based approach
@@ -4799,14 +4821,14 @@ function requireProviderController () {
4799
4821
  return providerController_1;
4800
4822
  }
4801
4823
 
4802
- const { app: app$3 } = require$$0$1;
4803
- const path$8 = require$$1$1;
4824
+ const { app: app$4 } = require$$0$1;
4825
+ const path$9 = require$$1$1;
4804
4826
  const { writeFileSync: writeFileSync$1 } = require$$2;
4805
4827
  const events$4 = events$8;
4806
- const { getFileContents: getFileContents$2 } = file;
4828
+ const { getFileContents: getFileContents$3 } = file;
4807
4829
 
4808
4830
  const configFilename$2 = "layouts.json";
4809
- const appName$2 = "Dashboard";
4831
+ const appName$3 = "Dashboard";
4810
4832
 
4811
4833
  const layoutController$1 = {
4812
4834
  /**
@@ -4820,13 +4842,13 @@ const layoutController$1 = {
4820
4842
  saveLayoutForApplication: (win, appId, layoutObject) => {
4821
4843
  try {
4822
4844
  // filename to the pages file (live pages)
4823
- const filename = path$8.join(
4824
- app$3.getPath("userData"),
4825
- appName$2,
4845
+ const filename = path$9.join(
4846
+ app$4.getPath("userData"),
4847
+ appName$3,
4826
4848
  appId,
4827
4849
  configFilename$2,
4828
4850
  );
4829
- const layoutsArray = getFileContents$2(filename);
4851
+ const layoutsArray = getFileContents$3(filename);
4830
4852
 
4831
4853
  // add the pageObject to the pages file
4832
4854
  layoutsArray.push(layoutObject);
@@ -4853,13 +4875,13 @@ const layoutController$1 = {
4853
4875
  */
4854
4876
  listLayoutsForApplication: (win, appId) => {
4855
4877
  try {
4856
- const filename = path$8.join(
4857
- app$3.getPath("userData"),
4858
- appName$2,
4878
+ const filename = path$9.join(
4879
+ app$4.getPath("userData"),
4880
+ appName$3,
4859
4881
  appId,
4860
4882
  configFilename$2,
4861
4883
  );
4862
- const layoutsArray = getFileContents$2(filename);
4884
+ const layoutsArray = getFileContents$3(filename);
4863
4885
  win.webContents.send(events$4.LAYOUT_LIST_COMPLETE, {
4864
4886
  layouts: layoutsArray,
4865
4887
  });
@@ -4896,7 +4918,7 @@ const {
4896
4918
  const {
4897
4919
  StreamableHTTPClientTransport,
4898
4920
  } = require$$2$3;
4899
- const path$7 = require$$1$1;
4921
+ const path$8 = require$$1$1;
4900
4922
  const fs$5 = require$$2;
4901
4923
 
4902
4924
  /**
@@ -5313,7 +5335,7 @@ const mcpController$2 = {
5313
5335
  }
5314
5336
 
5315
5337
  // Interpolate {{MCP_DIR}} in args to resolve local MCP server scripts
5316
- const mcpDir = path$7.join(__dirname, "..", "mcp");
5338
+ const mcpDir = path$8.join(__dirname, "..", "mcp");
5317
5339
  for (let i = 0; i < args.length; i++) {
5318
5340
  if (
5319
5341
  typeof args[i] === "string" &&
@@ -5688,7 +5710,7 @@ const mcpController$2 = {
5688
5710
  */
5689
5711
  getCatalog: (win) => {
5690
5712
  try {
5691
- const catalogPath = path$7.join(
5713
+ const catalogPath = path$8.join(
5692
5714
  __dirname,
5693
5715
  "..",
5694
5716
  "mcp",
@@ -5800,7 +5822,7 @@ const mcpController$2 = {
5800
5822
  }
5801
5823
 
5802
5824
  // Interpolate {{MCP_DIR}} in authCommand args (same as startServer)
5803
- const mcpDir = path$7.join(__dirname, "..", "mcp");
5825
+ const mcpDir = path$8.join(__dirname, "..", "mcp");
5804
5826
  const resolvedArgs = (authCommand.args || []).map((arg) =>
5805
5827
  typeof arg === "string" && arg.includes("{{MCP_DIR}}")
5806
5828
  ? arg.replace(/\{\{MCP_DIR\}\}/g, mcpDir)
@@ -5896,7 +5918,7 @@ var mcpControllerExports = mcpController$3.exports;
5896
5918
  * - Support two-level browsing: packages (bundles) and widgets within packages
5897
5919
  */
5898
5920
 
5899
- const path$6 = require$$1$1;
5921
+ const path$7 = require$$1$1;
5900
5922
  const fs$4 = require$$2;
5901
5923
 
5902
5924
  // Default registry URL (GitHub Pages)
@@ -5913,7 +5935,7 @@ let cacheTimestamp = 0;
5913
5935
  * Get the local test registry path for dev mode
5914
5936
  */
5915
5937
  function getTestRegistryPath() {
5916
- return path$6.join(__dirname, "..", "registry", "test-registry-index.json");
5938
+ return path$7.join(__dirname, "..", "registry", "test-registry-index.json");
5917
5939
  }
5918
5940
 
5919
5941
  /**
@@ -6173,7 +6195,7 @@ var registryController$1 = {
6173
6195
  var fs$3 = require$$2;
6174
6196
  var JSONStream = require$$4;
6175
6197
  const algoliasearch$1 = require$$2$4;
6176
- const path$5 = require$$3$2;
6198
+ const path$6 = require$$3$2;
6177
6199
  const { ensureDirectoryExistence, checkDirectory } = file;
6178
6200
 
6179
6201
  let AlgoliaIndex$1 = class AlgoliaIndex {
@@ -6280,7 +6302,7 @@ let AlgoliaIndex$1 = class AlgoliaIndex {
6280
6302
  if (err) reject(err);
6281
6303
  if (files) {
6282
6304
  files.forEach((file) => {
6283
- fs$3.unlinkSync(path$5.join(directoryPath, file));
6305
+ fs$3.unlinkSync(path$6.join(directoryPath, file));
6284
6306
  });
6285
6307
  resolve();
6286
6308
  }
@@ -6303,7 +6325,7 @@ let AlgoliaIndex$1 = class AlgoliaIndex {
6303
6325
  let results = [];
6304
6326
  for (const fileIndex in files) {
6305
6327
  // for each file lets read the file and then push to algolia
6306
- const pathToBatch = path$5.join(batchFilepath, files[fileIndex]);
6328
+ const pathToBatch = path$6.join(batchFilepath, files[fileIndex]);
6307
6329
  const fileContents = await this.readFile(pathToBatch);
6308
6330
  if (fileContents) {
6309
6331
  if ("data" in fileContents && "filepath" in fileContents) {
@@ -6757,25 +6779,25 @@ const openaiController$1 = {
6757
6779
 
6758
6780
  var openaiController_1 = openaiController$1;
6759
6781
 
6760
- const { app: app$2 } = require$$0$1;
6761
- const path$4 = require$$1$1;
6782
+ const { app: app$3 } = require$$0$1;
6783
+ const path$5 = require$$1$1;
6762
6784
  const { writeFileSync } = require$$2;
6763
- const { getFileContents: getFileContents$1 } = file;
6785
+ const { getFileContents: getFileContents$2 } = file;
6764
6786
 
6765
6787
  const configFilename$1 = "menuItems.json";
6766
- const appName$1 = "Dashboard";
6788
+ const appName$2 = "Dashboard";
6767
6789
 
6768
6790
  const menuItemsController$1 = {
6769
6791
  saveMenuItemForApplication: (win, appId, menuItem) => {
6770
6792
  try {
6771
6793
  // filename to the pages file (live pages)
6772
- const filename = path$4.join(
6773
- app$2.getPath("userData"),
6774
- appName$1,
6794
+ const filename = path$5.join(
6795
+ app$3.getPath("userData"),
6796
+ appName$2,
6775
6797
  appId,
6776
6798
  configFilename$1,
6777
6799
  );
6778
- const menuItemsArray = getFileContents$1(filename);
6800
+ const menuItemsArray = getFileContents$2(filename);
6779
6801
 
6780
6802
  menuItemsArray.filter((mi) => mi !== null);
6781
6803
 
@@ -6805,13 +6827,13 @@ const menuItemsController$1 = {
6805
6827
 
6806
6828
  listMenuItemsForApplication: (win, appId) => {
6807
6829
  try {
6808
- const filename = path$4.join(
6809
- app$2.getPath("userData"),
6810
- appName$1,
6830
+ const filename = path$5.join(
6831
+ app$3.getPath("userData"),
6832
+ appName$2,
6811
6833
  appId,
6812
6834
  configFilename$1,
6813
6835
  );
6814
- const menuItemsArray = getFileContents$1(filename);
6836
+ const menuItemsArray = getFileContents$2(filename);
6815
6837
  const filtered = menuItemsArray.filter((mi) => mi !== null);
6816
6838
  // Return the data for ipcMain.handle() - modern promise-based approach
6817
6839
  return {
@@ -6831,14 +6853,14 @@ const menuItemsController$1 = {
6831
6853
 
6832
6854
  var menuItemsController_1 = menuItemsController$1;
6833
6855
 
6834
- const path$3 = require$$1$1;
6835
- const { app: app$1 } = require$$0$1;
6856
+ const path$4 = require$$1$1;
6857
+ const { app: app$2 } = require$$0$1;
6836
6858
 
6837
6859
  const pluginController$1 = {
6838
6860
  install: (win, packageName, filepath) => {
6839
6861
  try {
6840
- const rootPath = path$3.join(
6841
- app$1.getPath("userData"),
6862
+ const rootPath = path$4.join(
6863
+ app$2.getPath("userData"),
6842
6864
  "plugins",
6843
6865
  packageName,
6844
6866
  );
@@ -8598,6 +8620,59 @@ function checkDashboardUpdates(workspaces = [], registryPackages = []) {
8598
8620
  return updates;
8599
8621
  }
8600
8622
 
8623
+ /**
8624
+ * Build a provider setup manifest for a dashboard's requirements.
8625
+ * Compares required providers against configured providers,
8626
+ * returning status (configured/needs-setup) for each.
8627
+ *
8628
+ * @param {Array} requiredProviders - Provider requirements from dashboard config
8629
+ * @param {Array} configuredProviders - User's configured providers (from providerController)
8630
+ * @returns {Object} Setup manifest with per-provider status
8631
+ */
8632
+ function buildProviderSetupManifest(
8633
+ requiredProviders = [],
8634
+ configuredProviders = [],
8635
+ ) {
8636
+ const configuredByType = new Map();
8637
+ for (const p of configuredProviders) {
8638
+ const key = p.type || p.name || "";
8639
+ if (key) {
8640
+ configuredByType.set(key.toLowerCase(), p);
8641
+ }
8642
+ }
8643
+
8644
+ const providers = requiredProviders.map((req) => {
8645
+ const typeKey = (req.type || "").toLowerCase();
8646
+ const configured = configuredByType.get(typeKey);
8647
+
8648
+ return {
8649
+ type: req.type || "",
8650
+ providerClass: req.providerClass || "",
8651
+ required: req.required !== false,
8652
+ usedBy: req.usedBy || [],
8653
+ status: configured ? "configured" : "needs-setup",
8654
+ configuredProvider: configured || null,
8655
+ };
8656
+ });
8657
+
8658
+ const configuredCount = providers.filter(
8659
+ (p) => p.status === "configured",
8660
+ ).length;
8661
+ const needsSetupCount = providers.filter(
8662
+ (p) => p.status === "needs-setup",
8663
+ ).length;
8664
+
8665
+ return {
8666
+ allConfigured: needsSetupCount === 0,
8667
+ summary: {
8668
+ total: providers.length,
8669
+ configured: configuredCount,
8670
+ needsSetup: needsSetupCount,
8671
+ },
8672
+ providers,
8673
+ };
8674
+ }
8675
+
8601
8676
  var dashboardConfigUtils$1 = {
8602
8677
  collectComponentNames: collectComponentNames$1,
8603
8678
  extractEventWiring: extractEventWiring$1,
@@ -8608,6 +8683,7 @@ var dashboardConfigUtils$1 = {
8608
8683
  generateRegistryManifest,
8609
8684
  buildDashboardPreview,
8610
8685
  checkDashboardUpdates,
8686
+ buildProviderSetupManifest,
8611
8687
  };
8612
8688
 
8613
8689
  var widgetRegistry$1 = {exports: {}};
@@ -8625,7 +8701,7 @@ var dynamicWidgetLoader$2 = {exports: {}};
8625
8701
  */
8626
8702
 
8627
8703
  const fs$1 = require$$2;
8628
- const path$2 = require$$1$1;
8704
+ const path$3 = require$$1$1;
8629
8705
 
8630
8706
  /**
8631
8707
  * Find the widgets/ directory, handling nested ZIP extraction.
@@ -8640,13 +8716,13 @@ const path$2 = require$$1$1;
8640
8716
  * @returns {string|null} Path to the widgets/ directory, or null
8641
8717
  */
8642
8718
  function findWidgetsDir$1(widgetPath) {
8643
- const direct = path$2.join(widgetPath, "widgets");
8719
+ const direct = path$3.join(widgetPath, "widgets");
8644
8720
  if (fs$1.existsSync(direct)) {
8645
8721
  return direct;
8646
8722
  }
8647
8723
 
8648
8724
  // Check configs/ directory (used by packageZip.js for distributed widgets)
8649
- const configs = path$2.join(widgetPath, "configs");
8725
+ const configs = path$3.join(widgetPath, "configs");
8650
8726
  if (fs$1.existsSync(configs)) {
8651
8727
  return configs;
8652
8728
  }
@@ -8663,7 +8739,7 @@ function findWidgetsDir$1(widgetPath) {
8663
8739
  );
8664
8740
 
8665
8741
  for (const subdir of subdirs) {
8666
- const nested = path$2.join(widgetPath, subdir.name, "widgets");
8742
+ const nested = path$3.join(widgetPath, subdir.name, "widgets");
8667
8743
  if (fs$1.existsSync(nested)) {
8668
8744
  console.log(`[WidgetCompiler] Found nested widgets/ at ${nested}`);
8669
8745
  return nested;
@@ -8712,14 +8788,14 @@ async function compileWidget(widgetPath) {
8712
8788
  // Compute relative path from the entry file (in widgetPath) to widgetsDir,
8713
8789
  // since widgetsDir may be nested (e.g., ./weather-widget/widgets/).
8714
8790
  const relWidgetsDir =
8715
- "./" + path$2.relative(widgetPath, widgetsDir).split(path$2.sep).join("/");
8791
+ "./" + path$3.relative(widgetPath, widgetsDir).split(path$3.sep).join("/");
8716
8792
  const imports = [];
8717
8793
  const exportParts = [];
8718
8794
 
8719
8795
  for (const dashFile of dashFiles) {
8720
8796
  const componentName = dashFile.replace(".dash.js", "");
8721
8797
  const componentFile = `${componentName}.js`;
8722
- const componentFilePath = path$2.join(widgetsDir, componentFile);
8798
+ const componentFilePath = path$3.join(widgetsDir, componentFile);
8723
8799
  const hasComponent = fs$1.existsSync(componentFilePath);
8724
8800
 
8725
8801
  // Import the config (always)
@@ -8746,9 +8822,9 @@ async function compileWidget(widgetPath) {
8746
8822
  const entryContent = [...imports, "", ...exportParts, ""].join("\n");
8747
8823
 
8748
8824
  // Write temporary entry file in the widget root
8749
- const entryPath = path$2.join(widgetPath, "__compile_entry.js");
8750
- const distDir = path$2.join(widgetPath, "dist");
8751
- const outPath = path$2.join(distDir, "index.cjs.js");
8825
+ const entryPath = path$3.join(widgetPath, "__compile_entry.js");
8826
+ const distDir = path$3.join(widgetPath, "dist");
8827
+ const outPath = path$3.join(distDir, "index.cjs.js");
8752
8828
 
8753
8829
  try {
8754
8830
  // Ensure dist/ directory exists
@@ -8825,7 +8901,7 @@ var widgetCompiler$1 = { compileWidget, findWidgetsDir: findWidgetsDir$1 };
8825
8901
  */
8826
8902
 
8827
8903
  const fs = require$$2;
8828
- const path$1 = require$$1$1;
8904
+ const path$2 = require$$1$1;
8829
8905
  const vm = require$$2$5;
8830
8906
  const { findWidgetsDir } = widgetCompiler$1;
8831
8907
 
@@ -8866,9 +8942,9 @@ class DynamicWidgetLoader {
8866
8942
  );
8867
8943
 
8868
8944
  const widgetsDir =
8869
- findWidgetsDir(widgetPath) || path$1.join(widgetPath, "widgets");
8870
- const componentPath = path$1.join(widgetsDir, `${componentName}.js`);
8871
- const configPath = path$1.join(widgetsDir, `${componentName}.dash.js`);
8945
+ findWidgetsDir(widgetPath) || path$2.join(widgetPath, "widgets");
8946
+ const componentPath = path$2.join(widgetsDir, `${componentName}.js`);
8947
+ const configPath = path$2.join(widgetsDir, `${componentName}.dash.js`);
8872
8948
 
8873
8949
  if (!fs.existsSync(componentPath)) {
8874
8950
  throw new Error(`Component file not found: ${componentPath}`);
@@ -9997,10 +10073,10 @@ var widgetRegistryExports = widgetRegistry$1.exports;
9997
10073
  * applies event wiring. (Import is implemented in DASH-13.)
9998
10074
  */
9999
10075
 
10000
- const { app, dialog } = require$$0$1;
10001
- const path = require$$1$1;
10076
+ const { app: app$1, dialog } = require$$0$1;
10077
+ const path$1 = require$$1$1;
10002
10078
  const AdmZip = require$$3$3;
10003
- const { getFileContents } = file;
10079
+ const { getFileContents: getFileContents$1 } = file;
10004
10080
  const {
10005
10081
  validateDashboardConfig,
10006
10082
  applyDefaults,
@@ -10016,7 +10092,7 @@ const {
10016
10092
  const { searchRegistry, getPackage } = registryController$1;
10017
10093
 
10018
10094
  const configFilename = "workspaces.json";
10019
- const appName = "Dashboard";
10095
+ const appName$1 = "Dashboard";
10020
10096
 
10021
10097
  /**
10022
10098
  * Export a workspace as a .dashboard.json config inside a ZIP file.
@@ -10039,13 +10115,13 @@ async function exportDashboardConfig$1(
10039
10115
  ) {
10040
10116
  try {
10041
10117
  // 1. Read workspace from workspaces.json
10042
- const filename = path.join(
10043
- app.getPath("userData"),
10044
- appName,
10118
+ const filename = path$1.join(
10119
+ app$1.getPath("userData"),
10120
+ appName$1,
10045
10121
  appId,
10046
10122
  configFilename,
10047
10123
  );
10048
- const workspacesArray = getFileContents(filename);
10124
+ const workspacesArray = getFileContents$1(filename);
10049
10125
  const workspace = workspacesArray.find(
10050
10126
  (w) => w.id === workspaceId || w.id === Number(workspaceId),
10051
10127
  );
@@ -10108,7 +10184,7 @@ async function exportDashboardConfig$1(
10108
10184
 
10109
10185
  const { canceled, filePath } = await dialog.showSaveDialog(win, {
10110
10186
  title: "Export Dashboard as ZIP",
10111
- defaultPath: path.join(app.getPath("desktop"), `${sanitizedName}.zip`),
10187
+ defaultPath: path$1.join(app$1.getPath("desktop"), `${sanitizedName}.zip`),
10112
10188
  filters: [{ name: "ZIP Archive", extensions: ["zip"] }],
10113
10189
  });
10114
10190
 
@@ -10182,7 +10258,7 @@ async function importDashboardConfig$1(win, appId, widgetRegistry = null) {
10182
10258
  const zip = new AdmZip(zipPath);
10183
10259
 
10184
10260
  // Validate ZIP entries for path traversal
10185
- const tempDir = path.join(app.getPath("temp"), "dash-import");
10261
+ const tempDir = path$1.join(app$1.getPath("temp"), "dash-import");
10186
10262
  const { validateZipEntries } = widgetRegistryExports;
10187
10263
  validateZipEntries(zip, tempDir);
10188
10264
 
@@ -10458,7 +10534,7 @@ async function installDashboardFromRegistry$1(
10458
10534
  const zip = new AdmZip(Buffer.from(buffer));
10459
10535
 
10460
10536
  // 3. Validate ZIP entries
10461
- const tempDir = path.join(app.getPath("temp"), "dash-registry-import");
10537
+ const tempDir = path$1.join(app$1.getPath("temp"), "dash-registry-import");
10462
10538
  const { validateZipEntries } = widgetRegistryExports;
10463
10539
  validateZipEntries(zip, tempDir);
10464
10540
 
@@ -10586,13 +10662,13 @@ async function prepareDashboardForPublish$1(
10586
10662
  const { generateRegistryManifest } = dashboardConfigUtils$1;
10587
10663
 
10588
10664
  // 1. Read workspace
10589
- const filename = path.join(
10590
- app.getPath("userData"),
10591
- appName,
10665
+ const filename = path$1.join(
10666
+ app$1.getPath("userData"),
10667
+ appName$1,
10592
10668
  appId,
10593
10669
  configFilename,
10594
10670
  );
10595
- const workspacesArray = getFileContents(filename);
10671
+ const workspacesArray = getFileContents$1(filename);
10596
10672
  const workspace = workspacesArray.find(
10597
10673
  (w) => w.id === workspaceId || w.id === Number(workspaceId),
10598
10674
  );
@@ -10690,8 +10766,8 @@ async function prepareDashboardForPublish$1(
10690
10766
  const sanitizedName = manifest.name;
10691
10767
  const { canceled, filePath } = await dialog.showSaveDialog(win, {
10692
10768
  title: "Save Dashboard Package for Registry",
10693
- defaultPath: path.join(
10694
- app.getPath("desktop"),
10769
+ defaultPath: path$1.join(
10770
+ app$1.getPath("desktop"),
10695
10771
  `${sanitizedName}-v${manifest.version}.zip`,
10696
10772
  ),
10697
10773
  filters: [{ name: "ZIP Archive", extensions: ["zip"] }],
@@ -10791,13 +10867,13 @@ async function checkDashboardUpdatesForApp$1(appId) {
10791
10867
  const { fetchRegistryIndex } = registryController$1;
10792
10868
 
10793
10869
  try {
10794
- const filename = path.join(
10795
- app.getPath("userData"),
10796
- appName,
10870
+ const filename = path$1.join(
10871
+ app$1.getPath("userData"),
10872
+ appName$1,
10797
10873
  appId,
10798
10874
  configFilename,
10799
10875
  );
10800
- const workspaces = getFileContents(filename) || [];
10876
+ const workspaces = getFileContents$1(filename) || [];
10801
10877
 
10802
10878
  const index = await fetchRegistryIndex();
10803
10879
  const registryPackages = index.packages || [];
@@ -10822,6 +10898,31 @@ async function checkDashboardUpdatesForApp$1(appId) {
10822
10898
  }
10823
10899
  }
10824
10900
 
10901
+ /**
10902
+ * Get a provider setup manifest for a dashboard's requirements.
10903
+ * Compares required providers against the user's configured providers.
10904
+ *
10905
+ * @param {string} appId - Application identifier
10906
+ * @param {Array} requiredProviders - Provider requirements from dashboard config
10907
+ * @returns {Object} Setup manifest with per-provider status
10908
+ */
10909
+ function getProviderSetupManifest$1(appId, requiredProviders = []) {
10910
+ const { buildProviderSetupManifest } = dashboardConfigUtils$1;
10911
+ const { listProviders } = requireProviderController();
10912
+
10913
+ let configuredProviders = [];
10914
+ try {
10915
+ configuredProviders = listProviders(null, appId) || [];
10916
+ } catch (err) {
10917
+ console.warn(
10918
+ "[DashboardConfigController] Could not list providers:",
10919
+ err.message,
10920
+ );
10921
+ }
10922
+
10923
+ return buildProviderSetupManifest(requiredProviders, configuredProviders);
10924
+ }
10925
+
10825
10926
  var dashboardConfigController$1 = {
10826
10927
  exportDashboardConfig: exportDashboardConfig$1,
10827
10928
  importDashboardConfig: importDashboardConfig$1,
@@ -10830,6 +10931,7 @@ var dashboardConfigController$1 = {
10830
10931
  prepareDashboardForPublish: prepareDashboardForPublish$1,
10831
10932
  getDashboardPreview: getDashboardPreview$1,
10832
10933
  checkDashboardUpdatesForApp: checkDashboardUpdatesForApp$1,
10934
+ getProviderSetupManifest: getProviderSetupManifest$1,
10833
10935
  };
10834
10936
 
10835
10937
  /**
@@ -10859,6 +10961,164 @@ clientCache$1.registerFactory("openai", (credentials) => {
10859
10961
  return new OpenAI({ apiKey: credentials.apiKey });
10860
10962
  });
10861
10963
 
10964
+ /**
10965
+ * dashboardRatingsUtils.js
10966
+ *
10967
+ * Pure utility functions for dashboard ratings logic.
10968
+ * No Electron or file I/O dependencies — safe to test anywhere.
10969
+ */
10970
+
10971
+ /**
10972
+ * Validate and build a rating entry.
10973
+ *
10974
+ * @param {string} packageName - Dashboard package name
10975
+ * @param {Object} rating - Rating input
10976
+ * @param {number} rating.stars - 1-5 star rating
10977
+ * @param {string} [rating.review] - Optional text review
10978
+ * @returns {Object} { success, rating, error }
10979
+ */
10980
+ function validateAndBuildRating$1(packageName, rating = {}) {
10981
+ if (!packageName) {
10982
+ return { success: false, error: "Package name is required" };
10983
+ }
10984
+
10985
+ const stars = Number(rating.stars);
10986
+ if (!stars || stars < 1 || stars > 5 || !Number.isInteger(stars)) {
10987
+ return {
10988
+ success: false,
10989
+ error: "Stars must be an integer between 1 and 5",
10990
+ };
10991
+ }
10992
+
10993
+ const entry = {
10994
+ stars,
10995
+ review:
10996
+ typeof rating.review === "string" ? rating.review.slice(0, 1000) : "",
10997
+ ratedAt: new Date().toISOString(),
10998
+ };
10999
+
11000
+ return { success: true, rating: entry };
11001
+ }
11002
+
11003
+ /**
11004
+ * Enrich packages with user rating data (pure function).
11005
+ *
11006
+ * @param {Array} packages - Registry packages
11007
+ * @param {Object} ratings - Map of packageName → rating data
11008
+ * @returns {Array} Packages with userRating field added
11009
+ */
11010
+ function enrichWithRatings$1(packages, ratings) {
11011
+ return packages.map((pkg) => ({
11012
+ ...pkg,
11013
+ userRating: ratings[pkg.name] || null,
11014
+ }));
11015
+ }
11016
+
11017
+ var dashboardRatingsUtils = {
11018
+ validateAndBuildRating: validateAndBuildRating$1,
11019
+ enrichWithRatings: enrichWithRatings$1,
11020
+ };
11021
+
11022
+ /**
11023
+ * dashboardRatingsController.js
11024
+ *
11025
+ * Local storage for dashboard ratings and reviews.
11026
+ * Stores user ratings per dashboard package in a JSON file.
11027
+ * Runs in the Electron main process.
11028
+ */
11029
+
11030
+ const { app } = require$$0$1;
11031
+ const path = require$$1$1;
11032
+ const { getFileContents, writeToFile } = file;
11033
+ const {
11034
+ validateAndBuildRating,
11035
+ enrichWithRatings,
11036
+ } = dashboardRatingsUtils;
11037
+
11038
+ const ratingsFilename = "dashboard-ratings.json";
11039
+ const appName = "Dashboard";
11040
+
11041
+ /**
11042
+ * Get the ratings file path for an app.
11043
+ */
11044
+ function getRatingsPath(appId) {
11045
+ return path.join(app.getPath("userData"), appName, appId, ratingsFilename);
11046
+ }
11047
+
11048
+ /**
11049
+ * Read all ratings from disk.
11050
+ */
11051
+ function readRatings(appId) {
11052
+ const filepath = getRatingsPath(appId);
11053
+ return getFileContents(filepath, {});
11054
+ }
11055
+
11056
+ /**
11057
+ * Write ratings to disk.
11058
+ */
11059
+ function writeRatings(appId, ratings) {
11060
+ const filepath = getRatingsPath(appId);
11061
+ writeToFile(filepath, JSON.stringify(ratings, null, 2));
11062
+ }
11063
+
11064
+ /**
11065
+ * Save or update a rating for a dashboard package.
11066
+ */
11067
+ function saveDashboardRating$1(appId, packageName, rating = {}) {
11068
+ const result = validateAndBuildRating(packageName, rating);
11069
+ if (!result.success) return result;
11070
+
11071
+ const ratings = readRatings(appId);
11072
+ ratings[packageName] = result.rating;
11073
+ writeRatings(appId, ratings);
11074
+
11075
+ return { success: true, rating: ratings[packageName] };
11076
+ }
11077
+
11078
+ /**
11079
+ * Get the user's rating for a specific dashboard package.
11080
+ */
11081
+ function getDashboardRating$1(appId, packageName) {
11082
+ const ratings = readRatings(appId);
11083
+ return ratings[packageName] || null;
11084
+ }
11085
+
11086
+ /**
11087
+ * Get all dashboard ratings.
11088
+ */
11089
+ function listDashboardRatings$1(appId) {
11090
+ return readRatings(appId);
11091
+ }
11092
+
11093
+ /**
11094
+ * Delete a rating for a dashboard package.
11095
+ */
11096
+ function deleteDashboardRating$1(appId, packageName) {
11097
+ const ratings = readRatings(appId);
11098
+ if (!ratings[packageName]) {
11099
+ return { success: false, error: "No rating found for this package" };
11100
+ }
11101
+ delete ratings[packageName];
11102
+ writeRatings(appId, ratings);
11103
+ return { success: true };
11104
+ }
11105
+
11106
+ /**
11107
+ * Enrich registry packages with local rating data.
11108
+ */
11109
+ function enrichPackagesWithRatings$1(packages, appId) {
11110
+ const ratings = readRatings(appId);
11111
+ return enrichWithRatings(packages, ratings);
11112
+ }
11113
+
11114
+ var dashboardRatingsController = {
11115
+ saveDashboardRating: saveDashboardRating$1,
11116
+ getDashboardRating: getDashboardRating$1,
11117
+ listDashboardRatings: listDashboardRatings$1,
11118
+ deleteDashboardRating: deleteDashboardRating$1,
11119
+ enrichPackagesWithRatings: enrichPackagesWithRatings$1,
11120
+ };
11121
+
10862
11122
  /**
10863
11123
  * Controller exports.
10864
11124
  */
@@ -10926,7 +11186,15 @@ const {
10926
11186
  prepareDashboardForPublish,
10927
11187
  getDashboardPreview,
10928
11188
  checkDashboardUpdatesForApp,
11189
+ getProviderSetupManifest,
10929
11190
  } = dashboardConfigController$1;
11191
+ const {
11192
+ saveDashboardRating,
11193
+ getDashboardRating,
11194
+ listDashboardRatings,
11195
+ deleteDashboardRating,
11196
+ enrichPackagesWithRatings,
11197
+ } = dashboardRatingsController;
10930
11198
 
10931
11199
  var controller = {
10932
11200
  showDialog,
@@ -10976,6 +11244,12 @@ var controller = {
10976
11244
  prepareDashboardForPublish,
10977
11245
  getDashboardPreview,
10978
11246
  checkDashboardUpdatesForApp,
11247
+ getProviderSetupManifest,
11248
+ saveDashboardRating,
11249
+ getDashboardRating,
11250
+ listDashboardRatings,
11251
+ deleteDashboardRating,
11252
+ enrichPackagesWithRatings,
10979
11253
  };
10980
11254
 
10981
11255
  const { ipcRenderer: ipcRenderer$i } = require$$0$1;
@@ -12359,6 +12633,7 @@ const {
12359
12633
  DASHBOARD_CONFIG_PUBLISH,
12360
12634
  DASHBOARD_CONFIG_PREVIEW,
12361
12635
  DASHBOARD_CONFIG_CHECK_UPDATES,
12636
+ DASHBOARD_CONFIG_PROVIDER_SETUP,
12362
12637
  } = events$8;
12363
12638
 
12364
12639
  const dashboardConfigApi$2 = {
@@ -12451,6 +12726,19 @@ const dashboardConfigApi$2 = {
12451
12726
  */
12452
12727
  checkDashboardUpdates: (appId) =>
12453
12728
  ipcRenderer$1.invoke(DASHBOARD_CONFIG_CHECK_UPDATES, { appId }),
12729
+
12730
+ /**
12731
+ * Get provider setup manifest for a dashboard's requirements.
12732
+ *
12733
+ * @param {string} appId - Application identifier
12734
+ * @param {Array} requiredProviders - Provider requirements from dashboard config
12735
+ * @returns {Promise<Object>} Setup manifest with per-provider status
12736
+ */
12737
+ getProviderSetupManifest: (appId, requiredProviders) =>
12738
+ ipcRenderer$1.invoke(DASHBOARD_CONFIG_PROVIDER_SETUP, {
12739
+ appId,
12740
+ requiredProviders,
12741
+ }),
12454
12742
  };
12455
12743
 
12456
12744
  var dashboardConfigApi_1 = dashboardConfigApi$2;