@trops/dash-core 0.1.85 → 0.1.87

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.
@@ -423,12 +423,14 @@ const REGISTRY_FETCH_INDEX = "registry:fetch-index";
423
423
  const REGISTRY_SEARCH = "registry:search";
424
424
  const REGISTRY_GET_PACKAGE = "registry:get-package";
425
425
  const REGISTRY_CHECK_UPDATES = "registry:check-updates";
426
+ const REGISTRY_SEARCH_DASHBOARDS = "registry:search-dashboards";
426
427
 
427
428
  var registryEvents$1 = {
428
429
  REGISTRY_FETCH_INDEX,
429
430
  REGISTRY_SEARCH,
430
431
  REGISTRY_GET_PACKAGE,
431
432
  REGISTRY_CHECK_UPDATES,
433
+ REGISTRY_SEARCH_DASHBOARDS,
432
434
  };
433
435
 
434
436
  /**
@@ -586,10 +588,12 @@ var clientCacheEvents$1 = {
586
588
 
587
589
  const DASHBOARD_CONFIG_EXPORT$1 = "dashboard-config-export";
588
590
  const DASHBOARD_CONFIG_IMPORT$1 = "dashboard-config-import";
591
+ const DASHBOARD_CONFIG_INSTALL$1 = "dashboard-config-install";
589
592
 
590
593
  var dashboardConfigEvents$1 = {
591
- DASHBOARD_CONFIG_EXPORT: DASHBOARD_CONFIG_EXPORT$1,
592
- DASHBOARD_CONFIG_IMPORT: DASHBOARD_CONFIG_IMPORT$1,
594
+ DASHBOARD_CONFIG_EXPORT: DASHBOARD_CONFIG_EXPORT$1,
595
+ DASHBOARD_CONFIG_IMPORT: DASHBOARD_CONFIG_IMPORT$1,
596
+ DASHBOARD_CONFIG_INSTALL: DASHBOARD_CONFIG_INSTALL$1,
593
597
  };
594
598
 
595
599
  /**
@@ -6009,12 +6013,22 @@ async function fetchRegistryIndex(forceRefresh = false) {
6009
6013
  * @param {string} filters.category - Filter by category
6010
6014
  * @param {string} filters.author - Filter by author
6011
6015
  * @param {string} filters.tag - Filter by tag
6016
+ * @param {string} filters.type - Filter by package type ("widget" or "dashboard")
6017
+ * @param {string[]} filters.compatibleWidgets - Only return dashboards whose required widgets are all in this list
6012
6018
  * @returns {Promise<Object>} { packages: [...], totalWidgets: number }
6013
6019
  */
6014
6020
  async function searchRegistry$1(query = "", filters = {}) {
6015
6021
  const index = await fetchRegistryIndex();
6016
6022
  let packages = index.packages || [];
6017
6023
 
6024
+ // Apply type filter — packages without an explicit type default to "widget"
6025
+ if (filters.type) {
6026
+ const typeLower = filters.type.toLowerCase();
6027
+ packages = packages.filter(
6028
+ (pkg) => (pkg.type || "widget").toLowerCase() === typeLower,
6029
+ );
6030
+ }
6031
+
6018
6032
  // Apply search query
6019
6033
  if (query) {
6020
6034
  const q = query.toLowerCase();
@@ -6063,6 +6077,24 @@ async function searchRegistry$1(query = "", filters = {}) {
6063
6077
  );
6064
6078
  }
6065
6079
 
6080
+ // Apply compatibility filter — only dashboards whose required widgets
6081
+ // are all present in the user's installed widget list
6082
+ if (filters.compatibleWidgets && filters.compatibleWidgets.length) {
6083
+ const installedSet = new Set(
6084
+ filters.compatibleWidgets.map((w) => w.toLowerCase()),
6085
+ );
6086
+ packages = packages.filter((pkg) => {
6087
+ const requiredWidgets = (pkg.widgets || []).filter(
6088
+ (w) => w.required !== false,
6089
+ );
6090
+ return requiredWidgets.every(
6091
+ (w) =>
6092
+ installedSet.has((w.package || "").toLowerCase()) ||
6093
+ installedSet.has((w.name || "").toLowerCase()),
6094
+ );
6095
+ });
6096
+ }
6097
+
6066
6098
  // Count total widgets across matched packages
6067
6099
  const totalWidgets = packages.reduce(
6068
6100
  (sum, pkg) => sum + (pkg.widgets || []).length,
@@ -6110,9 +6142,22 @@ async function checkUpdates(installedWidgets = []) {
6110
6142
  return updates;
6111
6143
  }
6112
6144
 
6145
+ /**
6146
+ * Search the registry for dashboard packages only.
6147
+ * Convenience wrapper around searchRegistry with type: "dashboard".
6148
+ *
6149
+ * @param {string} query - Search query string
6150
+ * @param {Object} filters - Optional filters (category, author, tag, compatibleWidgets)
6151
+ * @returns {Promise<Object>} { packages: [...], totalWidgets: number }
6152
+ */
6153
+ async function searchDashboards(query = "", filters = {}) {
6154
+ return searchRegistry$1(query, { ...filters, type: "dashboard" });
6155
+ }
6156
+
6113
6157
  var registryController$1 = {
6114
6158
  fetchRegistryIndex,
6115
6159
  searchRegistry: searchRegistry$1,
6160
+ searchDashboards,
6116
6161
  getPackage: getPackage$1,
6117
6162
  checkUpdates,
6118
6163
  };
@@ -9931,134 +9976,292 @@ async function importDashboardConfig$1(win, appId, widgetRegistry = null) {
9931
9976
  // Apply defaults to fill in optional fields
9932
9977
  dashboardConfig = applyDefaults(dashboardConfig);
9933
9978
 
9934
- // 3. Auto-install missing widgets from registry
9935
- const installSummary = {
9936
- installed: [],
9937
- alreadyInstalled: [],
9938
- failed: [],
9979
+ // Delegate to shared import pipeline
9980
+ return await processDashboardConfig(
9981
+ win,
9982
+ appId,
9983
+ dashboardConfig,
9984
+ widgetRegistry,
9985
+ );
9986
+ } catch (error) {
9987
+ console.error(
9988
+ "[DashboardConfigController] Error importing dashboard:",
9989
+ error,
9990
+ );
9991
+ return {
9992
+ success: false,
9993
+ error: error.message,
9939
9994
  };
9995
+ }
9996
+ }
9940
9997
 
9941
- if (
9942
- widgetRegistry &&
9943
- dashboardConfig.widgets &&
9944
- dashboardConfig.widgets.length
9945
- ) {
9946
- const installedWidgets = widgetRegistry.getWidgets();
9947
- const installedPackages = new Set(installedWidgets.map((w) => w.name));
9998
+ /**
9999
+ * Shared import pipeline: install widgets, create workspace, wire events.
10000
+ * Used by both importDashboardConfig (ZIP) and installDashboardFromRegistry.
10001
+ *
10002
+ * @param {BrowserWindow} win - The main window
10003
+ * @param {string} appId - Application identifier
10004
+ * @param {Object} dashboardConfig - Validated dashboard config object
10005
+ * @param {Object} widgetRegistry - WidgetRegistry instance
10006
+ * @param {Object} options - Additional options
10007
+ * @param {string} options.source - Source label ("zip" or "registry")
10008
+ * @returns {Promise<Object>} Result with success, workspace, and summary
10009
+ */
10010
+ async function processDashboardConfig(
10011
+ win,
10012
+ appId,
10013
+ dashboardConfig,
10014
+ widgetRegistry = null,
10015
+ options = {},
10016
+ ) {
10017
+ const source = options.source || "zip";
9948
10018
 
9949
- for (const widgetDep of dashboardConfig.widgets) {
9950
- const packageName = widgetDep.package;
10019
+ // 1. Auto-install missing widgets from registry
10020
+ const installSummary = {
10021
+ installed: [],
10022
+ alreadyInstalled: [],
10023
+ failed: [],
10024
+ };
9951
10025
 
9952
- if (installedPackages.has(packageName)) {
9953
- installSummary.alreadyInstalled.push(packageName);
9954
- continue;
9955
- }
10026
+ if (
10027
+ widgetRegistry &&
10028
+ dashboardConfig.widgets &&
10029
+ dashboardConfig.widgets.length
10030
+ ) {
10031
+ const installedWidgets = widgetRegistry.getWidgets();
10032
+ const installedPackages = new Set(installedWidgets.map((w) => w.name));
9956
10033
 
9957
- // Try to find the widget in the registry and install it
9958
- try {
9959
- const registryPkg = await getPackage(packageName);
9960
- if (registryPkg && registryPkg.downloadUrl) {
9961
- await widgetRegistry.downloadWidget(
9962
- packageName,
9963
- registryPkg.downloadUrl,
9964
- registryPkg.dashConfigUrl || null,
9965
- );
9966
- installSummary.installed.push(packageName);
9967
- installedPackages.add(packageName);
9968
- } else {
9969
- installSummary.failed.push({
9970
- package: packageName,
9971
- reason: "Not found in registry",
9972
- });
9973
- }
9974
- } catch (installError) {
10034
+ for (const widgetDep of dashboardConfig.widgets) {
10035
+ const packageName = widgetDep.package;
10036
+
10037
+ if (installedPackages.has(packageName)) {
10038
+ installSummary.alreadyInstalled.push(packageName);
10039
+ continue;
10040
+ }
10041
+
10042
+ // Try to find the widget in the registry and install it
10043
+ try {
10044
+ const registryPkg = await getPackage(packageName);
10045
+ if (registryPkg && registryPkg.downloadUrl) {
10046
+ await widgetRegistry.downloadWidget(
10047
+ packageName,
10048
+ registryPkg.downloadUrl,
10049
+ registryPkg.dashConfigUrl || null,
10050
+ );
10051
+ installSummary.installed.push(packageName);
10052
+ installedPackages.add(packageName);
10053
+ } else {
9975
10054
  installSummary.failed.push({
9976
10055
  package: packageName,
9977
- reason: installError.message,
10056
+ reason: "Not found in registry",
9978
10057
  });
9979
10058
  }
10059
+ } catch (installError) {
10060
+ installSummary.failed.push({
10061
+ package: packageName,
10062
+ reason: installError.message,
10063
+ });
9980
10064
  }
9981
10065
  }
10066
+ }
9982
10067
 
9983
- // 4. Build workspace from config
9984
- const workspace = dashboardConfig.workspace;
10068
+ // 2. Build workspace from config
10069
+ const workspace = { ...dashboardConfig.workspace };
9985
10070
 
9986
- if (!workspace) {
10071
+ if (!workspace || !workspace.layout) {
10072
+ return {
10073
+ success: false,
10074
+ error: "Dashboard config has no workspace data",
10075
+ };
10076
+ }
10077
+
10078
+ // Generate a unique ID for the imported workspace
10079
+ workspace.id = Date.now();
10080
+
10081
+ // 3. Apply event wiring to layout
10082
+ const eventWiringSummary = [];
10083
+ if (
10084
+ dashboardConfig.eventWiring &&
10085
+ dashboardConfig.eventWiring.length &&
10086
+ workspace.layout
10087
+ ) {
10088
+ applyEventWiringToLayout(workspace.layout, dashboardConfig.eventWiring);
10089
+ for (const wire of dashboardConfig.eventWiring) {
10090
+ eventWiringSummary.push(
10091
+ `${wire.source?.widget}.${wire.source?.event} → ${wire.target?.widget}.${wire.target?.handler}`,
10092
+ );
10093
+ }
10094
+ }
10095
+
10096
+ // 4. Mark as not shareable (imported dashboards cannot be re-published)
10097
+ workspace._dashboardConfig = {
10098
+ shareable: false,
10099
+ source,
10100
+ importedFrom: dashboardConfig.name,
10101
+ importedAt: new Date().toISOString(),
10102
+ originalAuthor: dashboardConfig.author,
10103
+ schemaVersion: dashboardConfig.schemaVersion,
10104
+ };
10105
+
10106
+ // Save workspace to workspaces.json
10107
+ const workspaceController = workspaceController_1;
10108
+ const saveResult = workspaceController.saveWorkspaceForApplication(
10109
+ win,
10110
+ appId,
10111
+ workspace,
10112
+ );
10113
+
10114
+ if (saveResult.error) {
10115
+ return {
10116
+ success: false,
10117
+ error: `Failed to save workspace: ${saveResult.message}`,
10118
+ };
10119
+ }
10120
+
10121
+ // Build provider requirements summary
10122
+ const providerSummary = (dashboardConfig.providers || []).map((p) => ({
10123
+ type: p.type,
10124
+ providerClass: p.providerClass,
10125
+ required: p.required,
10126
+ usedBy: p.usedBy,
10127
+ }));
10128
+
10129
+ console.log(
10130
+ `[DashboardConfigController] Imported dashboard "${dashboardConfig.name}" (${source}) as workspace ${workspace.id}`,
10131
+ );
10132
+
10133
+ return {
10134
+ success: true,
10135
+ workspace,
10136
+ summary: {
10137
+ name: dashboardConfig.name,
10138
+ description: dashboardConfig.description || "",
10139
+ author: dashboardConfig.author,
10140
+ widgets: installSummary,
10141
+ eventsWired: eventWiringSummary,
10142
+ providersRequired: providerSummary,
10143
+ },
10144
+ };
10145
+ }
10146
+
10147
+ /**
10148
+ * Install a dashboard from the registry by package name.
10149
+ *
10150
+ * Fetches the dashboard ZIP from the registry, extracts the .dashboard.json,
10151
+ * validates it, and delegates to the shared import pipeline.
10152
+ *
10153
+ * @param {BrowserWindow} win - The main window
10154
+ * @param {string} appId - Application identifier
10155
+ * @param {string} packageName - Registry package name for the dashboard
10156
+ * @param {Object} widgetRegistry - WidgetRegistry instance
10157
+ * @returns {Promise<Object>} Result with success, workspace, and summary
10158
+ */
10159
+ async function installDashboardFromRegistry$1(
10160
+ win,
10161
+ appId,
10162
+ packageName,
10163
+ widgetRegistry = null,
10164
+ ) {
10165
+ try {
10166
+ // 1. Look up the dashboard package in the registry
10167
+ const registryPkg = await getPackage(packageName);
10168
+ if (!registryPkg) {
9987
10169
  return {
9988
10170
  success: false,
9989
- error: "Dashboard config has no workspace data",
10171
+ error: `Dashboard package not found in registry: ${packageName}`,
9990
10172
  };
9991
10173
  }
9992
10174
 
9993
- // Generate a unique ID for the imported workspace
9994
- workspace.id = Date.now();
9995
-
9996
- // 5. Apply event wiring to layout
9997
- const eventWiringSummary = [];
9998
- if (
9999
- dashboardConfig.eventWiring &&
10000
- dashboardConfig.eventWiring.length &&
10001
- workspace.layout
10002
- ) {
10003
- applyEventWiringToLayout(workspace.layout, dashboardConfig.eventWiring);
10004
- for (const wire of dashboardConfig.eventWiring) {
10005
- eventWiringSummary.push(
10006
- `${wire.source?.widget}.${wire.source?.event} → ${wire.target?.widget}.${wire.target?.handler}`,
10007
- );
10008
- }
10175
+ if (!registryPkg.downloadUrl) {
10176
+ return {
10177
+ success: false,
10178
+ error: `Dashboard package has no download URL: ${packageName}`,
10179
+ };
10009
10180
  }
10010
10181
 
10011
- // 6. Mark as not shareable (imported dashboards cannot be re-published)
10012
- workspace._dashboardConfig = {
10013
- shareable: false,
10014
- importedFrom: dashboardConfig.name,
10015
- importedAt: new Date().toISOString(),
10016
- originalAuthor: dashboardConfig.author,
10017
- schemaVersion: dashboardConfig.schemaVersion,
10018
- };
10182
+ // 2. Resolve the download URL and fetch the ZIP
10183
+ const version = registryPkg.version || "1.0.0";
10184
+ let downloadUrl = registryPkg.downloadUrl;
10185
+ downloadUrl = downloadUrl.replace("{version}", version);
10186
+ downloadUrl = downloadUrl.replace("{name}", packageName);
10019
10187
 
10020
- // Save workspace to workspaces.json
10021
- const workspaceController = workspaceController_1;
10022
- const saveResult = workspaceController.saveWorkspaceForApplication(
10023
- win,
10024
- appId,
10025
- workspace,
10188
+ // Enforce HTTPS
10189
+ const parsedUrl = new URL(downloadUrl);
10190
+ if (parsedUrl.protocol !== "https:") {
10191
+ return {
10192
+ success: false,
10193
+ error: `Dashboard downloads must use HTTPS. Refusing: ${downloadUrl}`,
10194
+ };
10195
+ }
10196
+
10197
+ console.log(
10198
+ `[DashboardConfigController] Fetching dashboard from: ${downloadUrl}`,
10026
10199
  );
10027
10200
 
10028
- if (saveResult.error) {
10201
+ const response = await fetch(downloadUrl);
10202
+ if (!response.ok) {
10029
10203
  return {
10030
10204
  success: false,
10031
- error: `Failed to save workspace: ${saveResult.message}`,
10205
+ error: `Failed to download dashboard: ${response.status} ${response.statusText}`,
10032
10206
  };
10033
10207
  }
10034
10208
 
10035
- // Build provider requirements summary
10036
- const providerSummary = (dashboardConfig.providers || []).map((p) => ({
10037
- type: p.type,
10038
- providerClass: p.providerClass,
10039
- required: p.required,
10040
- usedBy: p.usedBy,
10041
- }));
10209
+ const buffer = await response.arrayBuffer();
10210
+ const zip = new AdmZip(Buffer.from(buffer));
10042
10211
 
10043
- console.log(
10044
- `[DashboardConfigController] Imported dashboard "${dashboardConfig.name}" as workspace ${workspace.id}`,
10212
+ // 3. Validate ZIP entries
10213
+ const tempDir = path.join(app.getPath("temp"), "dash-registry-import");
10214
+ const { validateZipEntries } = widgetRegistryExports;
10215
+ validateZipEntries(zip, tempDir);
10216
+
10217
+ // 4. Find and parse .dashboard.json
10218
+ const entries = zip.getEntries();
10219
+ const configEntry = entries.find((e) =>
10220
+ e.entryName.endsWith(".dashboard.json"),
10045
10221
  );
10046
10222
 
10047
- return {
10048
- success: true,
10049
- workspace,
10050
- summary: {
10051
- name: dashboardConfig.name,
10052
- description: dashboardConfig.description || "",
10053
- author: dashboardConfig.author,
10054
- widgets: installSummary,
10055
- eventsWired: eventWiringSummary,
10056
- providersRequired: providerSummary,
10223
+ if (!configEntry) {
10224
+ return {
10225
+ success: false,
10226
+ error: "No .dashboard.json file found in downloaded archive",
10227
+ };
10228
+ }
10229
+
10230
+ const configJson = configEntry.getData().toString("utf-8");
10231
+ let dashboardConfig;
10232
+ try {
10233
+ dashboardConfig = JSON.parse(configJson);
10234
+ } catch (parseError) {
10235
+ return {
10236
+ success: false,
10237
+ error: `Invalid JSON in dashboard config: ${parseError.message}`,
10238
+ };
10239
+ }
10240
+
10241
+ // 5. Validate against schema
10242
+ const validation = validateDashboardConfig(dashboardConfig);
10243
+ if (!validation.valid) {
10244
+ return {
10245
+ success: false,
10246
+ error: `Invalid dashboard config: ${validation.errors.join(", ")}`,
10247
+ };
10248
+ }
10249
+
10250
+ dashboardConfig = applyDefaults(dashboardConfig);
10251
+
10252
+ // 6. Delegate to shared import pipeline
10253
+ return await processDashboardConfig(
10254
+ win,
10255
+ appId,
10256
+ dashboardConfig,
10257
+ widgetRegistry,
10258
+ {
10259
+ source: "registry",
10057
10260
  },
10058
- };
10261
+ );
10059
10262
  } catch (error) {
10060
10263
  console.error(
10061
- "[DashboardConfigController] Error importing dashboard:",
10264
+ "[DashboardConfigController] Error installing dashboard from registry:",
10062
10265
  error,
10063
10266
  );
10064
10267
  return {
@@ -10071,6 +10274,7 @@ async function importDashboardConfig$1(win, appId, widgetRegistry = null) {
10071
10274
  var dashboardConfigController$1 = {
10072
10275
  exportDashboardConfig: exportDashboardConfig$1,
10073
10276
  importDashboardConfig: importDashboardConfig$1,
10277
+ installDashboardFromRegistry: installDashboardFromRegistry$1,
10074
10278
  };
10075
10279
 
10076
10280
  /**
@@ -10162,6 +10366,7 @@ const { install: pluginInstall } = pluginController_1;
10162
10366
  const {
10163
10367
  exportDashboardConfig,
10164
10368
  importDashboardConfig,
10369
+ installDashboardFromRegistry,
10165
10370
  } = dashboardConfigController$1;
10166
10371
 
10167
10372
  var controller = {
@@ -10207,6 +10412,7 @@ var controller = {
10207
10412
  searchIndex,
10208
10413
  exportDashboardConfig,
10209
10414
  importDashboardConfig,
10415
+ installDashboardFromRegistry,
10210
10416
  };
10211
10417
 
10212
10418
  const { ipcRenderer: ipcRenderer$i } = require$$0$1;
@@ -11182,6 +11388,25 @@ const registryApi$2 = {
11182
11388
  throw error;
11183
11389
  }
11184
11390
  },
11391
+
11392
+ /**
11393
+ * Search the registry for dashboard packages only
11394
+ * @param {string} query - Search query
11395
+ * @param {Object} filters - Optional filters { category, author, tag, compatibleWidgets }
11396
+ * @returns {Promise<Object>} { packages: [...], totalWidgets: number }
11397
+ */
11398
+ searchDashboards: async (query = "", filters = {}) => {
11399
+ try {
11400
+ return await ipcRenderer$9.invoke(
11401
+ "registry:search-dashboards",
11402
+ query,
11403
+ filters,
11404
+ );
11405
+ } catch (error) {
11406
+ console.error("[RegistryApi] Error searching dashboards:", error);
11407
+ throw error;
11408
+ }
11409
+ },
11185
11410
  };
11186
11411
 
11187
11412
  var registryApi_1 = registryApi$2;
@@ -11564,36 +11789,52 @@ var clientCacheApi_1 = clientCacheApi$2;
11564
11789
 
11565
11790
  const { ipcRenderer: ipcRenderer$1 } = require$$0$1;
11566
11791
  const {
11567
- DASHBOARD_CONFIG_EXPORT,
11568
- DASHBOARD_CONFIG_IMPORT,
11792
+ DASHBOARD_CONFIG_EXPORT,
11793
+ DASHBOARD_CONFIG_IMPORT,
11794
+ DASHBOARD_CONFIG_INSTALL,
11569
11795
  } = events$8;
11570
11796
 
11571
11797
  const dashboardConfigApi$2 = {
11572
- /**
11573
- * Export a workspace as a dashboard config ZIP file.
11574
- *
11575
- * @param {string} appId - Application identifier
11576
- * @param {number|string} workspaceId - ID of the workspace to export
11577
- * @param {Object} options - Export options (authorName, authorId, description, tags, icon)
11578
- * @returns {Promise<Object>} Result with success, filePath, and config
11579
- */
11580
- exportDashboardConfig: (appId, workspaceId, options = {}) =>
11581
- ipcRenderer$1.invoke(DASHBOARD_CONFIG_EXPORT, {
11582
- appId,
11583
- workspaceId,
11584
- options,
11585
- }),
11586
-
11587
- /**
11588
- * Import a dashboard config from a ZIP file.
11589
- * Shows a file picker, validates the config, installs missing widgets,
11590
- * creates the workspace, and applies event wiring.
11591
- *
11592
- * @param {string} appId - Application identifier
11593
- * @returns {Promise<Object>} Result with success, workspace, and summary
11594
- */
11595
- importDashboardConfig: (appId) =>
11596
- ipcRenderer$1.invoke(DASHBOARD_CONFIG_IMPORT, { appId }),
11798
+ /**
11799
+ * Export a workspace as a dashboard config ZIP file.
11800
+ *
11801
+ * @param {string} appId - Application identifier
11802
+ * @param {number|string} workspaceId - ID of the workspace to export
11803
+ * @param {Object} options - Export options (authorName, authorId, description, tags, icon)
11804
+ * @returns {Promise<Object>} Result with success, filePath, and config
11805
+ */
11806
+ exportDashboardConfig: (appId, workspaceId, options = {}) =>
11807
+ ipcRenderer$1.invoke(DASHBOARD_CONFIG_EXPORT, {
11808
+ appId,
11809
+ workspaceId,
11810
+ options,
11811
+ }),
11812
+
11813
+ /**
11814
+ * Import a dashboard config from a ZIP file.
11815
+ * Shows a file picker, validates the config, installs missing widgets,
11816
+ * creates the workspace, and applies event wiring.
11817
+ *
11818
+ * @param {string} appId - Application identifier
11819
+ * @returns {Promise<Object>} Result with success, workspace, and summary
11820
+ */
11821
+ importDashboardConfig: (appId) =>
11822
+ ipcRenderer$1.invoke(DASHBOARD_CONFIG_IMPORT, { appId }),
11823
+
11824
+ /**
11825
+ * Install a dashboard from the registry by package name.
11826
+ * Fetches the dashboard ZIP, validates config, installs widgets,
11827
+ * creates workspace, and applies event wiring.
11828
+ *
11829
+ * @param {string} appId - Application identifier
11830
+ * @param {string} packageName - Registry package name
11831
+ * @returns {Promise<Object>} Result with success, workspace, and summary
11832
+ */
11833
+ installDashboardFromRegistry: (appId, packageName) =>
11834
+ ipcRenderer$1.invoke(DASHBOARD_CONFIG_INSTALL, {
11835
+ appId,
11836
+ packageName,
11837
+ }),
11597
11838
  };
11598
11839
 
11599
11840
  var dashboardConfigApi_1 = dashboardConfigApi$2;