@trops/dash-core 0.1.287 → 0.1.289

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.
@@ -31578,6 +31578,77 @@ var schedulerController_1 = schedulerController$2;
31578
31578
  } catch (error) {
31579
31579
  console.error("[WidgetRegistry] Error loading registry:", error);
31580
31580
  }
31581
+
31582
+ // Reconcile: re-register orphaned widget packages found on disk
31583
+ this.reconcileWithDisk();
31584
+ }
31585
+
31586
+ /**
31587
+ * Scan the widgets directory for packages that exist on disk but are
31588
+ * missing from the registry (e.g. because registry.json was manually edited).
31589
+ * Re-registers them so they can be properly managed (listed, uninstalled).
31590
+ */
31591
+ reconcileWithDisk() {
31592
+ try {
31593
+ if (!WIDGETS_CACHE_DIR || !fs.existsSync(WIDGETS_CACHE_DIR)) return;
31594
+
31595
+ const registeredPaths = new Set(
31596
+ Array.from(this.widgets.values()).map((w) => w.path),
31597
+ );
31598
+ let reconciled = false;
31599
+
31600
+ const entries = fs.readdirSync(WIDGETS_CACHE_DIR, {
31601
+ withFileTypes: true,
31602
+ });
31603
+ for (const entry of entries) {
31604
+ if (!entry.isDirectory()) continue;
31605
+
31606
+ if (entry.name.startsWith("@")) {
31607
+ // Scoped packages: @scope/name
31608
+ const scopeDir = path.join(WIDGETS_CACHE_DIR, entry.name);
31609
+ const pkgs = fs.readdirSync(scopeDir, { withFileTypes: true });
31610
+ for (const pkg of pkgs) {
31611
+ if (!pkg.isDirectory()) continue;
31612
+ const pkgPath = path.join(scopeDir, pkg.name);
31613
+ const pkgId = `${entry.name}/${pkg.name}`;
31614
+ if (!registeredPaths.has(pkgPath) && !this.widgets.has(pkgId)) {
31615
+ this._reregisterOrphan(pkgId, pkgPath);
31616
+ reconciled = true;
31617
+ }
31618
+ }
31619
+ } else if (entry.name !== "registry.json") {
31620
+ // Bare-name packages
31621
+ const pkgPath = path.join(WIDGETS_CACHE_DIR, entry.name);
31622
+ if (!registeredPaths.has(pkgPath) && !this.widgets.has(entry.name)) {
31623
+ this._reregisterOrphan(entry.name, pkgPath);
31624
+ reconciled = true;
31625
+ }
31626
+ }
31627
+ }
31628
+
31629
+ if (reconciled) {
31630
+ this.saveRegistry();
31631
+ console.log("[WidgetRegistry] Disk reconciliation complete");
31632
+ }
31633
+ } catch (err) {
31634
+ console.warn("[WidgetRegistry] Reconciliation error:", err.message);
31635
+ }
31636
+ }
31637
+
31638
+ /**
31639
+ * Re-register an orphaned widget package found on disk.
31640
+ */
31641
+ _reregisterOrphan(pkgId, pkgPath) {
31642
+ console.log(`[WidgetRegistry] Re-registering orphaned widget: ${pkgId}`);
31643
+ const { scope } = parsePackageId(pkgId);
31644
+ this.widgets.set(pkgId, {
31645
+ name: pkgId,
31646
+ packageId: pkgId,
31647
+ scope: scope || null,
31648
+ path: pkgPath,
31649
+ version: null,
31650
+ orphaned: true,
31651
+ });
31581
31652
  }
31582
31653
 
31583
31654
  /**
@@ -32171,10 +32242,23 @@ var schedulerController_1 = schedulerController$2;
32171
32242
  * @param {string} widgetName - Name of the widget to remove
32172
32243
  */
32173
32244
  uninstallWidget(widgetName) {
32174
- const widget = this.widgets.get(widgetName);
32245
+ let widget = this.widgets.get(widgetName);
32246
+
32247
+ // Fallback: widget not in registry but might exist on disk
32175
32248
  if (!widget) {
32176
- console.warn(`[WidgetRegistry] Widget not found: ${widgetName}`);
32177
- return false;
32249
+ const candidatePath = path.join(
32250
+ WIDGETS_CACHE_DIR,
32251
+ ...widgetName.split("/"),
32252
+ );
32253
+ if (fs.existsSync(candidatePath)) {
32254
+ widget = { path: candidatePath };
32255
+ console.log(
32256
+ `[WidgetRegistry] Widget ${widgetName} not in registry, removing from disk`,
32257
+ );
32258
+ } else {
32259
+ console.warn(`[WidgetRegistry] Widget not found: ${widgetName}`);
32260
+ return false;
32261
+ }
32178
32262
  }
32179
32263
 
32180
32264
  try {
@@ -32347,7 +32431,13 @@ var schedulerController_1 = schedulerController$2;
32347
32431
  ipcMain.handle("widget:uninstall", (event, widgetName) => {
32348
32432
  const schedulerController = schedulerController_1;
32349
32433
  schedulerController.cleanupWidget(widgetName);
32350
- return getWidgetRegistry().uninstallWidget(widgetName);
32434
+ const success = getWidgetRegistry().uninstallWidget(widgetName);
32435
+ if (success) {
32436
+ BrowserWindow.getAllWindows().forEach((win) => {
32437
+ win.webContents.send("widget:uninstalled", { widgetName });
32438
+ });
32439
+ }
32440
+ return success;
32351
32441
  });
32352
32442
 
32353
32443
  ipcMain.handle("widget:cache-path", () => getWidgetRegistry().getCachePath());
@@ -67484,7 +67574,7 @@ const registeredPrompts = [];
67484
67574
  * Register a tool to be exposed via the MCP server.
67485
67575
  * Call this before starting the server (or restart after registering).
67486
67576
  */
67487
- function registerTool$5(toolDef) {
67577
+ function registerTool$6(toolDef) {
67488
67578
  registeredTools.push(toolDef);
67489
67579
  }
67490
67580
 
@@ -67847,7 +67937,7 @@ const mcpDashServerController$4 = {
67847
67937
  },
67848
67938
 
67849
67939
  // Expose registration functions for other controllers
67850
- registerTool: registerTool$5,
67940
+ registerTool: registerTool$6,
67851
67941
  registerResource: registerResource$1,
67852
67942
  registerPrompt: registerPrompt$1,
67853
67943
  getServerContext,
@@ -68929,6 +69019,23 @@ const widgetApi$2 = {
68929
69019
  });
68930
69020
  },
68931
69021
 
69022
+ /**
69023
+ * Listen for widget uninstallation events
69024
+ * Useful for cleaning up ComponentManager entries and refreshing UI
69025
+ *
69026
+ * @param {Function} callback - Function called when a widget is uninstalled
69027
+ *
69028
+ * @example
69029
+ * mainApi.widgets.onUninstalled(({ widgetName }) => {
69030
+ * console.log(`Widget ${widgetName} was uninstalled!`);
69031
+ * });
69032
+ */
69033
+ onUninstalled: (callback) => {
69034
+ ipcRenderer$k.on("widget:uninstalled", (event, data) => {
69035
+ callback(data);
69036
+ });
69037
+ },
69038
+
68932
69039
  /**
68933
69040
  * Listen for batch widget loading events
68934
69041
  * Useful for updating UI when multiple widgets are loaded at once
@@ -68956,6 +69063,15 @@ const widgetApi$2 = {
68956
69063
  ipcRenderer$k.removeListener("widget:installed", callback);
68957
69064
  },
68958
69065
 
69066
+ /**
69067
+ * Remove listener for widget uninstallation events
69068
+ *
69069
+ * @param {Function} callback - The callback to remove
69070
+ */
69071
+ removeUninstalledListener: (callback) => {
69072
+ ipcRenderer$k.removeListener("widget:uninstalled", callback);
69073
+ },
69074
+
68959
69075
  /**
68960
69076
  * Remove listener for batch widget loading events
68961
69077
  *
@@ -70437,7 +70553,7 @@ const dashboardTools$1 = [
70437
70553
  {
70438
70554
  name: "create_dashboard",
70439
70555
  description:
70440
- "Create a new empty dashboard with the given name. Returns the dashboard ID. After creating, use search_widgets or list_widgets to find widgets, then add_widget to populate the dashboard.",
70556
+ "Create a new dashboard with the given name. Optionally provide a layout to create a grid dashboard. Returns the dashboard ID. After creating, use search_widgets or list_widgets to find widgets, then add_widget to populate the dashboard.",
70441
70557
  inputSchema: {
70442
70558
  type: "object",
70443
70559
  properties: {
@@ -70445,6 +70561,32 @@ const dashboardTools$1 = [
70445
70561
  type: "string",
70446
70562
  description: "Display name for the new dashboard",
70447
70563
  },
70564
+ layout: {
70565
+ type: "object",
70566
+ description:
70567
+ "Optional grid layout configuration. When provided, creates a grid dashboard instead of a simple container.",
70568
+ properties: {
70569
+ rows: {
70570
+ type: "number",
70571
+ description: "Number of rows (1-10)",
70572
+ },
70573
+ cols: {
70574
+ type: "number",
70575
+ description: "Number of columns (1-10)",
70576
+ },
70577
+ gap: {
70578
+ type: "string",
70579
+ description:
70580
+ "Tailwind gap class (e.g. 'gap-2', 'gap-4'). Defaults to 'gap-2'.",
70581
+ },
70582
+ colModes: {
70583
+ type: "object",
70584
+ description:
70585
+ "Per-row column sizing. Keys are row numbers (as strings), values are mode strings: 'equal', '1/4', '1/3', '1/2', '2/3'.",
70586
+ },
70587
+ },
70588
+ required: ["rows", "cols"],
70589
+ },
70448
70590
  },
70449
70591
  required: ["name"],
70450
70592
  },
@@ -70480,7 +70622,7 @@ const widgetTools$1 = [
70480
70622
  {
70481
70623
  name: "add_widget",
70482
70624
  description:
70483
- "Add a widget to a dashboard by component name. Call list_widgets or search_widgets first to discover available widget names. Can be called multiple times to add multiple widgets. Returns the widget instance ID for use with configure_widget.",
70625
+ "Add a widget to a dashboard by component name. Call list_widgets or search_widgets first to discover available widget names. Can be called multiple times to add multiple widgets. Returns the widget instance ID for use with configure_widget. If the dashboard has a grid layout, you can specify row/col for explicit placement, or omit them to auto-place in the next empty cell.",
70484
70626
  inputSchema: {
70485
70627
  type: "object",
70486
70628
  properties: {
@@ -70494,6 +70636,16 @@ const widgetTools$1 = [
70494
70636
  description:
70495
70637
  "Component name of the widget to add (e.g. 'Clock', 'WeatherWidget')",
70496
70638
  },
70639
+ row: {
70640
+ type: "number",
70641
+ description:
70642
+ "Grid row to place the widget in (1-indexed). Must be used together with col. Requires a grid layout on the dashboard.",
70643
+ },
70644
+ col: {
70645
+ type: "number",
70646
+ description:
70647
+ "Grid column to place the widget in (1-indexed). Must be used together with row.",
70648
+ },
70497
70649
  },
70498
70650
  required: ["widgetName"],
70499
70651
  },
@@ -70744,12 +70896,108 @@ const providerTools$1 = [
70744
70896
  },
70745
70897
  ];
70746
70898
 
70899
+ const layoutTools$1 = [
70900
+ {
70901
+ name: "set_layout",
70902
+ description:
70903
+ "Set or replace the grid layout on a dashboard. Creates a LayoutGridContainer with the specified dimensions. Existing widgets in cells that fit the new grid are preserved; widgets outside the new bounds are orphaned (kept but unassigned). Use this to add a grid to an existing dashboard or to resize the grid.",
70904
+ inputSchema: {
70905
+ type: "object",
70906
+ properties: {
70907
+ dashboardId: {
70908
+ type: "string",
70909
+ description: "Dashboard ID. Omit to use the active dashboard.",
70910
+ },
70911
+ rows: {
70912
+ type: "number",
70913
+ description: "Number of rows (1-10)",
70914
+ },
70915
+ cols: {
70916
+ type: "number",
70917
+ description: "Number of columns (1-10)",
70918
+ },
70919
+ gap: {
70920
+ type: "string",
70921
+ description:
70922
+ "Tailwind gap class (e.g. 'gap-2', 'gap-4'). Defaults to 'gap-2'.",
70923
+ },
70924
+ colModes: {
70925
+ type: "object",
70926
+ description:
70927
+ "Per-row column sizing. Keys are row numbers (as strings), values are mode strings: 'equal', '1/4', '1/3', '1/2', '2/3'.",
70928
+ },
70929
+ },
70930
+ required: ["rows", "cols"],
70931
+ },
70932
+ },
70933
+ {
70934
+ name: "update_layout",
70935
+ description:
70936
+ "Partially update the grid layout. Only specified properties change — omitted properties keep their current values. colModes is merged (not replaced). Widgets in removed rows/columns are orphaned. Dashboard must already have a grid layout.",
70937
+ inputSchema: {
70938
+ type: "object",
70939
+ properties: {
70940
+ dashboardId: {
70941
+ type: "string",
70942
+ description: "Dashboard ID. Omit to use the active dashboard.",
70943
+ },
70944
+ rows: {
70945
+ type: "number",
70946
+ description: "New number of rows (1-10). Omit to keep current.",
70947
+ },
70948
+ cols: {
70949
+ type: "number",
70950
+ description: "New number of columns (1-10). Omit to keep current.",
70951
+ },
70952
+ gap: {
70953
+ type: "string",
70954
+ description: "Tailwind gap class. Omit to keep current.",
70955
+ },
70956
+ colModes: {
70957
+ type: "object",
70958
+ description:
70959
+ "Column sizing modes to merge. Set a key to null to reset that row to default.",
70960
+ },
70961
+ },
70962
+ required: [],
70963
+ },
70964
+ },
70965
+ {
70966
+ name: "move_widget",
70967
+ description:
70968
+ "Move a widget to a different grid cell. If the target cell is occupied, the two widgets are swapped. The widget must already be placed in a grid cell. Use get_dashboard to find widget IDs and current positions.",
70969
+ inputSchema: {
70970
+ type: "object",
70971
+ properties: {
70972
+ dashboardId: {
70973
+ type: "string",
70974
+ description: "Dashboard ID. Omit to use the active dashboard.",
70975
+ },
70976
+ widgetId: {
70977
+ type: "string",
70978
+ description: "ID of the widget to move",
70979
+ },
70980
+ row: {
70981
+ type: "number",
70982
+ description: "Target row (1-indexed)",
70983
+ },
70984
+ col: {
70985
+ type: "number",
70986
+ description: "Target column (1-indexed)",
70987
+ },
70988
+ },
70989
+ required: ["widgetId", "row", "col"],
70990
+ },
70991
+ },
70992
+ ];
70993
+
70747
70994
  var toolDefinitions = {
70748
70995
  dashboardTools: dashboardTools$1,
70749
70996
  widgetTools: widgetTools$1,
70750
70997
  themeTools: themeTools$1,
70751
70998
  providerTools: providerTools$1,
70752
70999
  guideTools: guideTools$1,
71000
+ layoutTools: layoutTools$1,
70753
71001
  };
70754
71002
 
70755
71003
  /**
@@ -70905,7 +71153,7 @@ async function handleGetDashboard$1({ dashboardId }) {
70905
71153
  /**
70906
71154
  * create_dashboard — Creates a new workspace with the given name.
70907
71155
  */
70908
- async function handleCreateDashboard$1({ name }) {
71156
+ async function handleCreateDashboard$1({ name, layout }) {
70909
71157
  if (!name || typeof name !== "string" || !name.trim()) {
70910
71158
  return {
70911
71159
  content: [
@@ -70920,8 +71168,86 @@ async function handleCreateDashboard$1({ name }) {
70920
71168
  };
70921
71169
  }
70922
71170
 
71171
+ // Validate optional layout parameter
71172
+ if (layout !== undefined && layout !== null) {
71173
+ if (typeof layout !== "object" || Array.isArray(layout)) {
71174
+ return {
71175
+ content: [
71176
+ {
71177
+ type: "text",
71178
+ text: JSON.stringify({
71179
+ error: "layout must be an object with rows and cols",
71180
+ }),
71181
+ },
71182
+ ],
71183
+ isError: true,
71184
+ };
71185
+ }
71186
+ const { rows, cols } = layout;
71187
+ if (
71188
+ rows === undefined ||
71189
+ cols === undefined ||
71190
+ typeof rows !== "number" ||
71191
+ typeof cols !== "number"
71192
+ ) {
71193
+ return {
71194
+ content: [
71195
+ {
71196
+ type: "text",
71197
+ text: JSON.stringify({
71198
+ error: "layout.rows and layout.cols are required numbers",
71199
+ }),
71200
+ },
71201
+ ],
71202
+ isError: true,
71203
+ };
71204
+ }
71205
+ if (rows < 1 || rows > 10 || cols < 1 || cols > 10) {
71206
+ return {
71207
+ content: [
71208
+ {
71209
+ type: "text",
71210
+ text: JSON.stringify({
71211
+ error: "rows and cols must be between 1 and 10",
71212
+ }),
71213
+ },
71214
+ ],
71215
+ isError: true,
71216
+ };
71217
+ }
71218
+ }
71219
+
70923
71220
  const { win, appId } = requireContext$1();
70924
71221
 
71222
+ // Build root layout node — grid or plain container
71223
+ const rootNode =
71224
+ layout && layout.rows && layout.cols
71225
+ ? {
71226
+ id: 1,
71227
+ order: 1,
71228
+ component: "LayoutGridContainer",
71229
+ type: "grid",
71230
+ parent: 0,
71231
+ hasChildren: 1,
71232
+ scrollable: false,
71233
+ width: "w-full",
71234
+ height: "h-full",
71235
+ workspace: "layout",
71236
+ grid: buildEmptyGrid(
71237
+ layout.rows,
71238
+ layout.cols,
71239
+ layout.gap,
71240
+ layout.colModes,
71241
+ ),
71242
+ }
71243
+ : {
71244
+ id: 1,
71245
+ order: 1,
71246
+ component: "Container",
71247
+ parent: 0,
71248
+ items: [],
71249
+ };
71250
+
70925
71251
  const newWorkspace = {
70926
71252
  id: Date.now(),
70927
71253
  name: name.trim(),
@@ -70929,15 +71255,7 @@ async function handleCreateDashboard$1({ name }) {
70929
71255
  type: "workspace",
70930
71256
  version: 1,
70931
71257
  menuId: 1,
70932
- layout: [
70933
- {
70934
- id: 1,
70935
- order: 1,
70936
- component: "Container",
70937
- parentId: 0,
70938
- items: [],
70939
- },
70940
- ],
71258
+ layout: [rootNode],
70941
71259
  };
70942
71260
 
70943
71261
  const result = workspaceController$2.saveWorkspaceForApplication(
@@ -70958,15 +71276,16 @@ async function handleCreateDashboard$1({ name }) {
70958
71276
  };
70959
71277
  }
70960
71278
 
71279
+ const response = { id: String(newWorkspace.id), name: newWorkspace.name };
71280
+ if (layout && layout.rows && layout.cols) {
71281
+ response.layout = { rows: layout.rows, cols: layout.cols };
71282
+ }
71283
+
70961
71284
  return {
70962
71285
  content: [
70963
71286
  {
70964
71287
  type: "text",
70965
- text: JSON.stringify(
70966
- { id: String(newWorkspace.id), name: newWorkspace.name },
70967
- null,
70968
- 2,
70969
- ),
71288
+ text: JSON.stringify(response, null, 2),
70970
71289
  },
70971
71290
  ],
70972
71291
  };
@@ -71157,10 +71476,65 @@ function nextLayoutId(layout) {
71157
71476
  return maxId + 1;
71158
71477
  }
71159
71478
 
71479
+ /**
71480
+ * Helper: find the LayoutGridContainer node in a layout array.
71481
+ */
71482
+ function findGridNode(layout) {
71483
+ if (!Array.isArray(layout)) return null;
71484
+ return (
71485
+ layout.find(
71486
+ (item) => item.component === "LayoutGridContainer" && item.grid,
71487
+ ) || null
71488
+ );
71489
+ }
71490
+
71491
+ /**
71492
+ * Helper: build an empty grid object with cell slots for all row.col positions.
71493
+ */
71494
+ function buildEmptyGrid(rows, cols, gap, colModes) {
71495
+ const grid = {
71496
+ rows,
71497
+ cols,
71498
+ gap: gap || "gap-2",
71499
+ };
71500
+ if (colModes && Object.keys(colModes).length > 0) {
71501
+ grid.colModes = colModes;
71502
+ }
71503
+ for (let r = 1; r <= rows; r++) {
71504
+ for (let c = 1; c <= cols; c++) {
71505
+ grid[`${r}.${c}`] = { component: null, hide: false };
71506
+ }
71507
+ }
71508
+ return grid;
71509
+ }
71510
+
71511
+ /**
71512
+ * Helper: find the next empty cell scanning left-to-right, top-to-bottom.
71513
+ */
71514
+ function findNextEmptyCell(grid) {
71515
+ for (let r = 1; r <= grid.rows; r++) {
71516
+ for (let c = 1; c <= grid.cols; c++) {
71517
+ const key = `${r}.${c}`;
71518
+ const cell = grid[key];
71519
+ if (!cell || (cell.component === null && !cell.hide)) {
71520
+ return { row: r, col: c };
71521
+ }
71522
+ }
71523
+ }
71524
+ return null;
71525
+ }
71526
+
71527
+ /**
71528
+ * Helper: check if a row/col position is within grid bounds.
71529
+ */
71530
+ function isValidCell(grid, row, col) {
71531
+ return row >= 1 && row <= grid.rows && col >= 1 && col <= grid.cols;
71532
+ }
71533
+
71160
71534
  /**
71161
71535
  * add_widget — Add a widget to a dashboard by component name.
71162
71536
  */
71163
- async function handleAddWidget$1({ dashboardId, widgetName }) {
71537
+ async function handleAddWidget$1({ dashboardId, widgetName, row, col }) {
71164
71538
  if (!widgetName || typeof widgetName !== "string" || !widgetName.trim()) {
71165
71539
  return {
71166
71540
  content: [
@@ -71175,6 +71549,25 @@ async function handleAddWidget$1({ dashboardId, widgetName }) {
71175
71549
  };
71176
71550
  }
71177
71551
 
71552
+ // Validate row/col pairing
71553
+ if (
71554
+ (row !== undefined && col === undefined) ||
71555
+ (row === undefined && col !== undefined)
71556
+ ) {
71557
+ return {
71558
+ content: [
71559
+ {
71560
+ type: "text",
71561
+ text: JSON.stringify({
71562
+ error:
71563
+ "Both row and col must be provided together, or both omitted",
71564
+ }),
71565
+ },
71566
+ ],
71567
+ isError: true,
71568
+ };
71569
+ }
71570
+
71178
71571
  const { win, appId } = requireContext$1();
71179
71572
  const result = workspaceController$2.listWorkspacesForApplication(win, appId);
71180
71573
  if (result.error) {
@@ -71199,7 +71592,88 @@ async function handleAddWidget$1({ dashboardId, widgetName }) {
71199
71592
  item.component === "LayoutContainer" ||
71200
71593
  item.component === "LayoutGridContainer",
71201
71594
  );
71202
- const parentId = container ? container.id : 0;
71595
+
71596
+ const gridNode = findGridNode(layout);
71597
+
71598
+ // Determine grid placement
71599
+ let targetRow = null;
71600
+ let targetCol = null;
71601
+
71602
+ if (row !== undefined && col !== undefined) {
71603
+ // Explicit placement requested
71604
+ if (!gridNode) {
71605
+ return {
71606
+ content: [
71607
+ {
71608
+ type: "text",
71609
+ text: JSON.stringify({
71610
+ error:
71611
+ "Cannot specify row/col: dashboard has no grid layout. Use create_dashboard with layout or set_layout first.",
71612
+ }),
71613
+ },
71614
+ ],
71615
+ isError: true,
71616
+ };
71617
+ }
71618
+ const r = Number(row);
71619
+ const c = Number(col);
71620
+ if (!isValidCell(gridNode.grid, r, c)) {
71621
+ return {
71622
+ content: [
71623
+ {
71624
+ type: "text",
71625
+ text: JSON.stringify({
71626
+ error: `Cell ${r}.${c} is out of bounds (grid is ${gridNode.grid.rows}x${gridNode.grid.cols})`,
71627
+ }),
71628
+ },
71629
+ ],
71630
+ isError: true,
71631
+ };
71632
+ }
71633
+ const cellKey = `${r}.${c}`;
71634
+ const cell = gridNode.grid[cellKey];
71635
+ if (cell && cell.hide) {
71636
+ return {
71637
+ content: [
71638
+ {
71639
+ type: "text",
71640
+ text: JSON.stringify({
71641
+ error: `Cell ${r}.${c} is hidden`,
71642
+ }),
71643
+ },
71644
+ ],
71645
+ isError: true,
71646
+ };
71647
+ }
71648
+ if (cell && cell.component !== null) {
71649
+ return {
71650
+ content: [
71651
+ {
71652
+ type: "text",
71653
+ text: JSON.stringify({
71654
+ error: `Cell ${r}.${c} is already occupied by widget ${cell.component}`,
71655
+ }),
71656
+ },
71657
+ ],
71658
+ isError: true,
71659
+ };
71660
+ }
71661
+ targetRow = r;
71662
+ targetCol = c;
71663
+ } else if (gridNode) {
71664
+ // No row/col specified but grid exists — auto-place in next empty cell
71665
+ const empty = findNextEmptyCell(gridNode.grid);
71666
+ if (empty) {
71667
+ targetRow = empty.row;
71668
+ targetCol = empty.col;
71669
+ }
71670
+ }
71671
+
71672
+ const parentContainerId = gridNode
71673
+ ? gridNode.id
71674
+ : container
71675
+ ? container.id
71676
+ : 0;
71203
71677
 
71204
71678
  const newId = nextLayoutId(layout);
71205
71679
  const maxOrder = layout.reduce(
@@ -71211,12 +71685,22 @@ async function handleAddWidget$1({ dashboardId, widgetName }) {
71211
71685
  id: newId,
71212
71686
  order: maxOrder + 1,
71213
71687
  component: widgetName.trim(),
71214
- parentId,
71688
+ parent: parentContainerId,
71215
71689
  config: {},
71216
71690
  };
71217
71691
 
71218
71692
  workspace.layout = [...layout, newItem];
71219
71693
 
71694
+ // If placing in grid, update the cell assignment
71695
+ if (gridNode && targetRow !== null && targetCol !== null) {
71696
+ const cellKey = `${targetRow}.${targetCol}`;
71697
+ gridNode.grid[cellKey] = {
71698
+ ...(gridNode.grid[cellKey] || {}),
71699
+ component: newId,
71700
+ hide: false,
71701
+ };
71702
+ }
71703
+
71220
71704
  const saveResult = workspaceController$2.saveWorkspaceForApplication(
71221
71705
  win,
71222
71706
  appId,
@@ -71234,19 +71718,20 @@ async function handleAddWidget$1({ dashboardId, widgetName }) {
71234
71718
  };
71235
71719
  }
71236
71720
 
71721
+ const response = {
71722
+ widgetId: String(newId),
71723
+ name: widgetName.trim(),
71724
+ dashboardId: String(workspace.id),
71725
+ };
71726
+ if (targetRow !== null && targetCol !== null) {
71727
+ response.cell = { row: targetRow, col: targetCol };
71728
+ }
71729
+
71237
71730
  return {
71238
71731
  content: [
71239
71732
  {
71240
71733
  type: "text",
71241
- text: JSON.stringify(
71242
- {
71243
- widgetId: String(newId),
71244
- name: widgetName.trim(),
71245
- dashboardId: String(workspace.id),
71246
- },
71247
- null,
71248
- 2,
71249
- ),
71734
+ text: JSON.stringify(response, null, 2),
71250
71735
  },
71251
71736
  ],
71252
71737
  };
@@ -71302,6 +71787,21 @@ async function handleRemoveWidget$1({ dashboardId, widgetId }) {
71302
71787
 
71303
71788
  workspace.layout = layout.filter((item) => String(item.id) !== widgetId);
71304
71789
 
71790
+ // Clean up grid cell assignments that reference this widget
71791
+ const numericId = Number(widgetId);
71792
+ for (const item of workspace.layout) {
71793
+ if (item.grid) {
71794
+ for (const key of Object.keys(item.grid)) {
71795
+ if (/^\d+\.\d+$/.test(key)) {
71796
+ const cell = item.grid[key];
71797
+ if (cell && cell.component === numericId) {
71798
+ cell.component = null;
71799
+ }
71800
+ }
71801
+ }
71802
+ }
71803
+ }
71804
+
71305
71805
  const saveResult = workspaceController$2.saveWorkspaceForApplication(
71306
71806
  win,
71307
71807
  appId,
@@ -72463,6 +72963,492 @@ async function handleGetSetupGuide$1({ topic }) {
72463
72963
  };
72464
72964
  }
72465
72965
 
72966
+ // --- Layout Tool Handlers ---
72967
+
72968
+ /**
72969
+ * set_layout — Set or replace the grid layout on a dashboard.
72970
+ */
72971
+ async function handleSetLayout$1({ dashboardId, rows, cols, gap, colModes }) {
72972
+ if (
72973
+ rows === undefined ||
72974
+ cols === undefined ||
72975
+ typeof rows !== "number" ||
72976
+ typeof cols !== "number"
72977
+ ) {
72978
+ return {
72979
+ content: [
72980
+ {
72981
+ type: "text",
72982
+ text: JSON.stringify({
72983
+ error: "rows and cols are required numbers",
72984
+ }),
72985
+ },
72986
+ ],
72987
+ isError: true,
72988
+ };
72989
+ }
72990
+ if (rows < 1 || rows > 10 || cols < 1 || cols > 10) {
72991
+ return {
72992
+ content: [
72993
+ {
72994
+ type: "text",
72995
+ text: JSON.stringify({
72996
+ error: "rows and cols must be between 1 and 10",
72997
+ }),
72998
+ },
72999
+ ],
73000
+ isError: true,
73001
+ };
73002
+ }
73003
+
73004
+ const { win, appId } = requireContext$1();
73005
+ const result = workspaceController$2.listWorkspacesForApplication(win, appId);
73006
+ if (result.error) {
73007
+ return {
73008
+ content: [
73009
+ { type: "text", text: JSON.stringify({ error: result.message }) },
73010
+ ],
73011
+ isError: true,
73012
+ };
73013
+ }
73014
+
73015
+ const found = findWorkspace(result.workspaces || [], dashboardId);
73016
+ if (found.error) return found.response;
73017
+
73018
+ const workspace = found.workspace;
73019
+ const layout = workspace.layout || [];
73020
+
73021
+ const newGrid = buildEmptyGrid(rows, cols, gap, colModes);
73022
+ const existingGridNode = findGridNode(layout);
73023
+ const orphanedWidgetIds = [];
73024
+
73025
+ if (existingGridNode) {
73026
+ // Preserve cell assignments that still fit in the new dimensions
73027
+ const oldGrid = existingGridNode.grid;
73028
+ for (const key of Object.keys(oldGrid)) {
73029
+ if (
73030
+ /^\d+\.\d+$/.test(key) &&
73031
+ oldGrid[key] &&
73032
+ oldGrid[key].component !== null
73033
+ ) {
73034
+ const parts = key.split(".");
73035
+ const r = Number(parts[0]);
73036
+ const c = Number(parts[1]);
73037
+ if (r <= rows && c <= cols) {
73038
+ newGrid[key] = { ...newGrid[key], component: oldGrid[key].component };
73039
+ } else {
73040
+ orphanedWidgetIds.push(oldGrid[key].component);
73041
+ }
73042
+ }
73043
+ }
73044
+ existingGridNode.grid = newGrid;
73045
+ } else {
73046
+ // Replace root Container with a LayoutGridContainer
73047
+ const rootIdx = layout.findIndex(
73048
+ (item) =>
73049
+ item.parent === 0 &&
73050
+ (item.component === "Container" ||
73051
+ item.component === "LayoutContainer"),
73052
+ );
73053
+
73054
+ const newGridNode = {
73055
+ id: rootIdx >= 0 ? layout[rootIdx].id : nextLayoutId(layout),
73056
+ order: 1,
73057
+ component: "LayoutGridContainer",
73058
+ type: "grid",
73059
+ parent: 0,
73060
+ hasChildren: 1,
73061
+ scrollable: false,
73062
+ width: "w-full",
73063
+ height: "h-full",
73064
+ workspace: "layout",
73065
+ grid: newGrid,
73066
+ };
73067
+
73068
+ if (rootIdx >= 0) {
73069
+ // Reparent existing widgets to the new grid node
73070
+ const oldRootId = layout[rootIdx].id;
73071
+ for (const item of layout) {
73072
+ if (item.parent === oldRootId && item.id !== oldRootId) {
73073
+ item.parent = newGridNode.id;
73074
+ }
73075
+ }
73076
+ workspace.layout[rootIdx] = newGridNode;
73077
+ } else {
73078
+ workspace.layout = [newGridNode, ...layout];
73079
+ }
73080
+ }
73081
+
73082
+ const saveResult = workspaceController$2.saveWorkspaceForApplication(
73083
+ win,
73084
+ appId,
73085
+ workspace,
73086
+ );
73087
+ if (saveResult.error) {
73088
+ return {
73089
+ content: [
73090
+ { type: "text", text: JSON.stringify({ error: saveResult.message }) },
73091
+ ],
73092
+ isError: true,
73093
+ };
73094
+ }
73095
+
73096
+ return {
73097
+ content: [
73098
+ {
73099
+ type: "text",
73100
+ text: JSON.stringify(
73101
+ {
73102
+ dashboardId: String(workspace.id),
73103
+ grid: { rows, cols },
73104
+ orphanedWidgets: orphanedWidgetIds.map(String),
73105
+ },
73106
+ null,
73107
+ 2,
73108
+ ),
73109
+ },
73110
+ ],
73111
+ };
73112
+ }
73113
+
73114
+ /**
73115
+ * update_layout — Partially update grid layout properties.
73116
+ */
73117
+ async function handleUpdateLayout$1({ dashboardId, rows, cols, gap, colModes }) {
73118
+ if (
73119
+ rows === undefined &&
73120
+ cols === undefined &&
73121
+ gap === undefined &&
73122
+ colModes === undefined
73123
+ ) {
73124
+ return {
73125
+ content: [
73126
+ {
73127
+ type: "text",
73128
+ text: JSON.stringify({
73129
+ error:
73130
+ "At least one of rows, cols, gap, or colModes must be provided",
73131
+ }),
73132
+ },
73133
+ ],
73134
+ isError: true,
73135
+ };
73136
+ }
73137
+
73138
+ const { win, appId } = requireContext$1();
73139
+ const result = workspaceController$2.listWorkspacesForApplication(win, appId);
73140
+ if (result.error) {
73141
+ return {
73142
+ content: [
73143
+ { type: "text", text: JSON.stringify({ error: result.message }) },
73144
+ ],
73145
+ isError: true,
73146
+ };
73147
+ }
73148
+
73149
+ const found = findWorkspace(result.workspaces || [], dashboardId);
73150
+ if (found.error) return found.response;
73151
+
73152
+ const workspace = found.workspace;
73153
+ const gridNode = findGridNode(workspace.layout || []);
73154
+
73155
+ if (!gridNode) {
73156
+ return {
73157
+ content: [
73158
+ {
73159
+ type: "text",
73160
+ text: JSON.stringify({
73161
+ error:
73162
+ "Dashboard has no grid layout. Use set_layout or create_dashboard with layout first.",
73163
+ }),
73164
+ },
73165
+ ],
73166
+ isError: true,
73167
+ };
73168
+ }
73169
+
73170
+ const grid = gridNode.grid;
73171
+ const oldRows = grid.rows;
73172
+ const oldCols = grid.cols;
73173
+ const newRows = rows !== undefined ? rows : oldRows;
73174
+ const newCols = cols !== undefined ? cols : oldCols;
73175
+
73176
+ if (newRows < 1 || newRows > 10 || newCols < 1 || newCols > 10) {
73177
+ return {
73178
+ content: [
73179
+ {
73180
+ type: "text",
73181
+ text: JSON.stringify({
73182
+ error: "rows and cols must be between 1 and 10",
73183
+ }),
73184
+ },
73185
+ ],
73186
+ isError: true,
73187
+ };
73188
+ }
73189
+
73190
+ // Update gap if specified
73191
+ if (gap !== undefined) {
73192
+ grid.gap = gap;
73193
+ }
73194
+
73195
+ // Merge colModes if specified
73196
+ if (colModes !== undefined) {
73197
+ grid.colModes = { ...(grid.colModes || {}), ...colModes };
73198
+ // Remove entries set to null
73199
+ for (const k of Object.keys(grid.colModes)) {
73200
+ if (grid.colModes[k] === null) {
73201
+ delete grid.colModes[k];
73202
+ }
73203
+ }
73204
+ if (Object.keys(grid.colModes).length === 0) {
73205
+ delete grid.colModes;
73206
+ }
73207
+ }
73208
+
73209
+ // Handle dimension changes
73210
+ const orphanedWidgetIds = [];
73211
+ if (newRows !== oldRows || newCols !== oldCols) {
73212
+ // Add new cells
73213
+ for (let r = 1; r <= newRows; r++) {
73214
+ for (let c = 1; c <= newCols; c++) {
73215
+ const key = `${r}.${c}`;
73216
+ if (!grid[key]) {
73217
+ grid[key] = { component: null, hide: false };
73218
+ }
73219
+ }
73220
+ }
73221
+ // Collect orphaned widgets from removed cells
73222
+ for (const key of Object.keys(grid)) {
73223
+ if (/^\d+\.\d+$/.test(key)) {
73224
+ const parts = key.split(".");
73225
+ const r = Number(parts[0]);
73226
+ const c = Number(parts[1]);
73227
+ if (r > newRows || c > newCols) {
73228
+ if (grid[key] && grid[key].component !== null) {
73229
+ orphanedWidgetIds.push(grid[key].component);
73230
+ }
73231
+ delete grid[key];
73232
+ }
73233
+ }
73234
+ }
73235
+ grid.rows = newRows;
73236
+ grid.cols = newCols;
73237
+ }
73238
+
73239
+ const saveResult = workspaceController$2.saveWorkspaceForApplication(
73240
+ win,
73241
+ appId,
73242
+ workspace,
73243
+ );
73244
+ if (saveResult.error) {
73245
+ return {
73246
+ content: [
73247
+ { type: "text", text: JSON.stringify({ error: saveResult.message }) },
73248
+ ],
73249
+ isError: true,
73250
+ };
73251
+ }
73252
+
73253
+ return {
73254
+ content: [
73255
+ {
73256
+ type: "text",
73257
+ text: JSON.stringify(
73258
+ {
73259
+ dashboardId: String(workspace.id),
73260
+ grid: { rows: newRows, cols: newCols, gap: grid.gap },
73261
+ orphanedWidgets: orphanedWidgetIds.map(String),
73262
+ },
73263
+ null,
73264
+ 2,
73265
+ ),
73266
+ },
73267
+ ],
73268
+ };
73269
+ }
73270
+
73271
+ /**
73272
+ * move_widget — Move a widget to a different grid cell (swap if occupied).
73273
+ */
73274
+ async function handleMoveWidget$1({ dashboardId, widgetId, row, col }) {
73275
+ if (!widgetId || typeof widgetId !== "string") {
73276
+ return {
73277
+ content: [
73278
+ {
73279
+ type: "text",
73280
+ text: JSON.stringify({ error: "widgetId is required" }),
73281
+ },
73282
+ ],
73283
+ isError: true,
73284
+ };
73285
+ }
73286
+ if (row === undefined || col === undefined) {
73287
+ return {
73288
+ content: [
73289
+ {
73290
+ type: "text",
73291
+ text: JSON.stringify({ error: "row and col are required" }),
73292
+ },
73293
+ ],
73294
+ isError: true,
73295
+ };
73296
+ }
73297
+
73298
+ const { win, appId } = requireContext$1();
73299
+ const result = workspaceController$2.listWorkspacesForApplication(win, appId);
73300
+ if (result.error) {
73301
+ return {
73302
+ content: [
73303
+ { type: "text", text: JSON.stringify({ error: result.message }) },
73304
+ ],
73305
+ isError: true,
73306
+ };
73307
+ }
73308
+
73309
+ const found = findWorkspace(result.workspaces || [], dashboardId);
73310
+ if (found.error) return found.response;
73311
+
73312
+ const workspace = found.workspace;
73313
+ const gridNode = findGridNode(workspace.layout || []);
73314
+
73315
+ if (!gridNode) {
73316
+ return {
73317
+ content: [
73318
+ {
73319
+ type: "text",
73320
+ text: JSON.stringify({
73321
+ error: "Dashboard has no grid layout",
73322
+ }),
73323
+ },
73324
+ ],
73325
+ isError: true,
73326
+ };
73327
+ }
73328
+
73329
+ const r = Number(row);
73330
+ const c = Number(col);
73331
+ if (!isValidCell(gridNode.grid, r, c)) {
73332
+ return {
73333
+ content: [
73334
+ {
73335
+ type: "text",
73336
+ text: JSON.stringify({
73337
+ error: `Cell ${r}.${c} is out of bounds (grid is ${gridNode.grid.rows}x${gridNode.grid.cols})`,
73338
+ }),
73339
+ },
73340
+ ],
73341
+ isError: true,
73342
+ };
73343
+ }
73344
+
73345
+ const targetKey = `${r}.${c}`;
73346
+ const targetCell = gridNode.grid[targetKey];
73347
+ if (targetCell && targetCell.hide) {
73348
+ return {
73349
+ content: [
73350
+ {
73351
+ type: "text",
73352
+ text: JSON.stringify({
73353
+ error: `Cell ${r}.${c} is hidden`,
73354
+ }),
73355
+ },
73356
+ ],
73357
+ isError: true,
73358
+ };
73359
+ }
73360
+
73361
+ // Find the widget's current cell
73362
+ const numericWidgetId = Number(widgetId);
73363
+ let sourceKey = null;
73364
+ for (const key of Object.keys(gridNode.grid)) {
73365
+ if (
73366
+ /^\d+\.\d+$/.test(key) &&
73367
+ gridNode.grid[key] &&
73368
+ gridNode.grid[key].component === numericWidgetId
73369
+ ) {
73370
+ sourceKey = key;
73371
+ break;
73372
+ }
73373
+ }
73374
+
73375
+ if (!sourceKey) {
73376
+ return {
73377
+ content: [
73378
+ {
73379
+ type: "text",
73380
+ text: JSON.stringify({
73381
+ error: `Widget ${widgetId} not found in any grid cell`,
73382
+ }),
73383
+ },
73384
+ ],
73385
+ isError: true,
73386
+ };
73387
+ }
73388
+
73389
+ if (sourceKey === targetKey) {
73390
+ return {
73391
+ content: [
73392
+ {
73393
+ type: "text",
73394
+ text: JSON.stringify(
73395
+ {
73396
+ widgetId,
73397
+ cell: { row: r, col: c },
73398
+ swapped: false,
73399
+ },
73400
+ null,
73401
+ 2,
73402
+ ),
73403
+ },
73404
+ ],
73405
+ };
73406
+ }
73407
+
73408
+ // Perform the move (swap if target occupied)
73409
+ const targetComponentId = targetCell ? targetCell.component : null;
73410
+ gridNode.grid[targetKey] = {
73411
+ ...(gridNode.grid[targetKey] || {}),
73412
+ component: numericWidgetId,
73413
+ };
73414
+ gridNode.grid[sourceKey] = {
73415
+ ...(gridNode.grid[sourceKey] || {}),
73416
+ component: targetComponentId,
73417
+ };
73418
+
73419
+ const saveResult = workspaceController$2.saveWorkspaceForApplication(
73420
+ win,
73421
+ appId,
73422
+ workspace,
73423
+ );
73424
+ if (saveResult.error) {
73425
+ return {
73426
+ content: [
73427
+ { type: "text", text: JSON.stringify({ error: saveResult.message }) },
73428
+ ],
73429
+ isError: true,
73430
+ };
73431
+ }
73432
+
73433
+ const response = {
73434
+ widgetId,
73435
+ cell: { row: r, col: c },
73436
+ swapped: targetComponentId !== null,
73437
+ };
73438
+ if (targetComponentId !== null) {
73439
+ response.swappedWidgetId = String(targetComponentId);
73440
+ }
73441
+
73442
+ return {
73443
+ content: [
73444
+ {
73445
+ type: "text",
73446
+ text: JSON.stringify(response, null, 2),
73447
+ },
73448
+ ],
73449
+ };
73450
+ }
73451
+
72466
73452
  var toolHandlers = {
72467
73453
  handleListDashboards: handleListDashboards$1,
72468
73454
  handleGetDashboard: handleGetDashboard$1,
@@ -72483,6 +73469,14 @@ var toolHandlers = {
72483
73469
  handleAddProvider: handleAddProvider$1,
72484
73470
  handleRemoveProvider: handleRemoveProvider$1,
72485
73471
  handleGetSetupGuide: handleGetSetupGuide$1,
73472
+ handleSetLayout: handleSetLayout$1,
73473
+ handleUpdateLayout: handleUpdateLayout$1,
73474
+ handleMoveWidget: handleMoveWidget$1,
73475
+ // Helpers (exported for testing)
73476
+ findGridNode,
73477
+ buildEmptyGrid,
73478
+ findNextEmptyCell,
73479
+ isValidCell,
72486
73480
  };
72487
73481
 
72488
73482
  /**
@@ -72492,7 +73486,7 @@ var toolHandlers = {
72492
73486
  * Call registerDashboardTools() during app startup (before or after server start).
72493
73487
  */
72494
73488
 
72495
- const { registerTool: registerTool$4 } = mcpDashServerController_1;
73489
+ const { registerTool: registerTool$5 } = mcpDashServerController_1;
72496
73490
  const { dashboardTools } = toolDefinitions;
72497
73491
  const {
72498
73492
  handleListDashboards,
@@ -72503,7 +73497,7 @@ const {
72503
73497
  } = toolHandlers;
72504
73498
 
72505
73499
  // Map tool names to handler functions
72506
- const handlerMap$6 = {
73500
+ const handlerMap$7 = {
72507
73501
  list_dashboards: handleListDashboards,
72508
73502
  get_dashboard: handleGetDashboard,
72509
73503
  create_dashboard: handleCreateDashboard,
@@ -72516,12 +73510,12 @@ const handlerMap$6 = {
72516
73510
  */
72517
73511
  function registerDashboardTools$1() {
72518
73512
  for (const tool of dashboardTools) {
72519
- const handler = handlerMap$6[tool.name];
73513
+ const handler = handlerMap$7[tool.name];
72520
73514
  if (!handler) {
72521
73515
  console.warn(`[dashboardTools] No handler found for tool: ${tool.name}`);
72522
73516
  continue;
72523
73517
  }
72524
- registerTool$4({
73518
+ registerTool$5({
72525
73519
  name: tool.name,
72526
73520
  description: tool.description,
72527
73521
  inputSchema: tool.inputSchema,
@@ -72542,7 +73536,7 @@ var dashboardTools_1 = { registerDashboardTools: registerDashboardTools$1 };
72542
73536
  * Call registerWidgetTools() during app startup (before or after server start).
72543
73537
  */
72544
73538
 
72545
- const { registerTool: registerTool$3 } = mcpDashServerController_1;
73539
+ const { registerTool: registerTool$4 } = mcpDashServerController_1;
72546
73540
  const { widgetTools } = toolDefinitions;
72547
73541
  const {
72548
73542
  handleAddWidget,
@@ -72553,7 +73547,7 @@ const {
72553
73547
  } = toolHandlers;
72554
73548
 
72555
73549
  // Map tool names to handler functions
72556
- const handlerMap$5 = {
73550
+ const handlerMap$6 = {
72557
73551
  add_widget: handleAddWidget,
72558
73552
  remove_widget: handleRemoveWidget,
72559
73553
  configure_widget: handleConfigureWidget,
@@ -72566,12 +73560,12 @@ const handlerMap$5 = {
72566
73560
  */
72567
73561
  function registerWidgetTools$1() {
72568
73562
  for (const tool of widgetTools) {
72569
- const handler = handlerMap$5[tool.name];
73563
+ const handler = handlerMap$6[tool.name];
72570
73564
  if (!handler) {
72571
73565
  console.warn(`[widgetTools] No handler found for tool: ${tool.name}`);
72572
73566
  continue;
72573
73567
  }
72574
- registerTool$3({
73568
+ registerTool$4({
72575
73569
  name: tool.name,
72576
73570
  description: tool.description,
72577
73571
  inputSchema: tool.inputSchema,
@@ -72590,7 +73584,7 @@ var widgetTools_1 = { registerWidgetTools: registerWidgetTools$1 };
72590
73584
  * Call registerThemeTools() during app startup (before or after server start).
72591
73585
  */
72592
73586
 
72593
- const { registerTool: registerTool$2 } = mcpDashServerController_1;
73587
+ const { registerTool: registerTool$3 } = mcpDashServerController_1;
72594
73588
  const { themeTools } = toolDefinitions;
72595
73589
  const {
72596
73590
  handleListThemes,
@@ -72601,7 +73595,7 @@ const {
72601
73595
  } = toolHandlers;
72602
73596
 
72603
73597
  // Map tool names to handler functions
72604
- const handlerMap$4 = {
73598
+ const handlerMap$5 = {
72605
73599
  list_themes: handleListThemes,
72606
73600
  get_theme: handleGetTheme,
72607
73601
  create_theme: handleCreateTheme,
@@ -72614,12 +73608,12 @@ const handlerMap$4 = {
72614
73608
  */
72615
73609
  function registerThemeTools$1() {
72616
73610
  for (const tool of themeTools) {
72617
- const handler = handlerMap$4[tool.name];
73611
+ const handler = handlerMap$5[tool.name];
72618
73612
  if (!handler) {
72619
73613
  console.warn(`[themeTools] No handler found for tool: ${tool.name}`);
72620
73614
  continue;
72621
73615
  }
72622
- registerTool$2({
73616
+ registerTool$3({
72623
73617
  name: tool.name,
72624
73618
  description: tool.description,
72625
73619
  inputSchema: tool.inputSchema,
@@ -72638,7 +73632,7 @@ var themeTools_1 = { registerThemeTools: registerThemeTools$1 };
72638
73632
  * Call registerProviderTools() during app startup (before or after server start).
72639
73633
  */
72640
73634
 
72641
- const { registerTool: registerTool$1 } = mcpDashServerController_1;
73635
+ const { registerTool: registerTool$2 } = mcpDashServerController_1;
72642
73636
  const { providerTools } = toolDefinitions;
72643
73637
  const {
72644
73638
  handleListProviders,
@@ -72647,7 +73641,7 @@ const {
72647
73641
  } = toolHandlers;
72648
73642
 
72649
73643
  // Map tool names to handler functions
72650
- const handlerMap$3 = {
73644
+ const handlerMap$4 = {
72651
73645
  list_providers: handleListProviders,
72652
73646
  add_provider: handleAddProvider,
72653
73647
  remove_provider: handleRemoveProvider,
@@ -72658,12 +73652,12 @@ const handlerMap$3 = {
72658
73652
  */
72659
73653
  function registerProviderTools$1() {
72660
73654
  for (const tool of providerTools) {
72661
- const handler = handlerMap$3[tool.name];
73655
+ const handler = handlerMap$4[tool.name];
72662
73656
  if (!handler) {
72663
73657
  console.warn(`[providerTools] No handler found for tool: ${tool.name}`);
72664
73658
  continue;
72665
73659
  }
72666
- registerTool$1({
73660
+ registerTool$2({
72667
73661
  name: tool.name,
72668
73662
  description: tool.description,
72669
73663
  inputSchema: tool.inputSchema,
@@ -72684,22 +73678,22 @@ var providerTools_1 = { registerProviderTools: registerProviderTools$1 };
72684
73678
  * Call registerGuideTools() during app startup.
72685
73679
  */
72686
73680
 
72687
- const { registerTool } = mcpDashServerController_1;
73681
+ const { registerTool: registerTool$1 } = mcpDashServerController_1;
72688
73682
  const { guideTools } = toolDefinitions;
72689
73683
  const { handleGetSetupGuide } = toolHandlers;
72690
73684
 
72691
- const handlerMap$2 = {
73685
+ const handlerMap$3 = {
72692
73686
  get_setup_guide: handleGetSetupGuide,
72693
73687
  };
72694
73688
 
72695
73689
  function registerGuideTools$1() {
72696
73690
  for (const tool of guideTools) {
72697
- const handler = handlerMap$2[tool.name];
73691
+ const handler = handlerMap$3[tool.name];
72698
73692
  if (!handler) {
72699
73693
  console.warn(`[guideTools] No handler found for tool: ${tool.name}`);
72700
73694
  continue;
72701
73695
  }
72702
- registerTool({
73696
+ registerTool$1({
72703
73697
  name: tool.name,
72704
73698
  description: tool.description,
72705
73699
  inputSchema: tool.inputSchema,
@@ -72711,6 +73705,49 @@ function registerGuideTools$1() {
72711
73705
 
72712
73706
  var guideTools_1 = { registerGuideTools: registerGuideTools$1 };
72713
73707
 
73708
+ /**
73709
+ * layoutTools.js
73710
+ *
73711
+ * Registers layout/grid MCP tools with the MCP Dash server.
73712
+ * Call registerLayoutTools() during app startup (before or after server start).
73713
+ */
73714
+
73715
+ const { registerTool } = mcpDashServerController_1;
73716
+ const { layoutTools } = toolDefinitions;
73717
+ const {
73718
+ handleSetLayout,
73719
+ handleUpdateLayout,
73720
+ handleMoveWidget,
73721
+ } = toolHandlers;
73722
+
73723
+ const handlerMap$2 = {
73724
+ set_layout: handleSetLayout,
73725
+ update_layout: handleUpdateLayout,
73726
+ move_widget: handleMoveWidget,
73727
+ };
73728
+
73729
+ /**
73730
+ * Register all layout tools with the MCP server controller.
73731
+ */
73732
+ function registerLayoutTools$1() {
73733
+ for (const tool of layoutTools) {
73734
+ const handler = handlerMap$2[tool.name];
73735
+ if (!handler) {
73736
+ console.warn(`[layoutTools] No handler found for tool: ${tool.name}`);
73737
+ continue;
73738
+ }
73739
+ registerTool({
73740
+ name: tool.name,
73741
+ description: tool.description,
73742
+ inputSchema: tool.inputSchema,
73743
+ handler,
73744
+ });
73745
+ }
73746
+ console.log(`[layoutTools] Registered ${layoutTools.length} layout tools`);
73747
+ }
73748
+
73749
+ var layoutTools_1 = { registerLayoutTools: registerLayoutTools$1 };
73750
+
72714
73751
  /**
72715
73752
  * resourceDefinitions.js
72716
73753
  *
@@ -73679,6 +74716,7 @@ const { registerWidgetTools } = widgetTools_1;
73679
74716
  const { registerThemeTools } = themeTools_1;
73680
74717
  const { registerProviderTools } = providerTools_1;
73681
74718
  const { registerGuideTools } = guideTools_1;
74719
+ const { registerLayoutTools } = layoutTools_1;
73682
74720
  const { registerResources } = resources;
73683
74721
  const { registerPrompts } = promptRegistration;
73684
74722
  registerDashboardTools();
@@ -73686,6 +74724,7 @@ registerWidgetTools();
73686
74724
  registerThemeTools();
73687
74725
  registerProviderTools();
73688
74726
  registerGuideTools();
74727
+ registerLayoutTools();
73689
74728
  registerResources();
73690
74729
  registerPrompts();
73691
74730
 
@@ -73787,6 +74826,7 @@ var electron = {
73787
74826
  registerWidgetTools,
73788
74827
  registerThemeTools,
73789
74828
  registerProviderTools,
74829
+ registerLayoutTools,
73790
74830
  registerResources,
73791
74831
  };
73792
74832