kanban-lite 1.0.22 → 1.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/{CLAUDE.md → AGENTS.md} +13 -0
  2. package/CHANGELOG.md +68 -0
  3. package/README.md +10 -0
  4. package/dist/cli.js +112 -17
  5. package/dist/extension.js +123 -20
  6. package/dist/mcp-server.js +90 -11
  7. package/dist/sdk/index.cjs +71 -9
  8. package/dist/sdk/index.mjs +71 -9
  9. package/dist/sdk/sdk/KanbanSDK.d.ts +39 -7
  10. package/dist/sdk/shared/config.d.ts +4 -0
  11. package/dist/sdk/shared/types.d.ts +4 -0
  12. package/dist/standalone-webview/index.js +45 -45
  13. package/dist/standalone-webview/index.js.map +1 -1
  14. package/dist/standalone-webview/style.css +1 -1
  15. package/dist/standalone.js +155 -99
  16. package/dist/webview/index.js +45 -45
  17. package/dist/webview/index.js.map +1 -1
  18. package/dist/webview/style.css +1 -1
  19. package/docs/plans/2026-02-27-zoom-settings-design.md +82 -0
  20. package/docs/plans/2026-02-27-zoom-settings.md +395 -0
  21. package/docs/sdk.md +3 -6
  22. package/package.json +1 -1
  23. package/src/cli/index.ts +12 -2
  24. package/src/extension/KanbanPanel.ts +25 -5
  25. package/src/mcp-server/index.ts +20 -2
  26. package/src/sdk/KanbanSDK.ts +64 -7
  27. package/src/sdk/__tests__/KanbanSDK.test.ts +17 -1
  28. package/src/sdk/__tests__/metadata.test.ts +2 -0
  29. package/src/sdk/__tests__/multi-board.test.ts +2 -0
  30. package/src/shared/config.ts +14 -2
  31. package/src/shared/types.ts +4 -0
  32. package/src/standalone/server.ts +31 -6
  33. package/src/webview/App.tsx +42 -3
  34. package/src/webview/assets/main.css +31 -2
  35. package/src/webview/components/KanbanBoard.tsx +28 -3
  36. package/src/webview/components/KanbanColumn.tsx +24 -13
  37. package/src/webview/components/SettingsPanel.tsx +68 -0
  38. package/src/webview/components/Toolbar.tsx +126 -16
  39. package/src/webview/store/index.ts +13 -20
@@ -41,6 +41,17 @@ npm test # Run tests (vitest)
41
41
 
42
42
  All three interfaces (API, CLI, MCP) support the same operations: cards CRUD, columns CRUD, settings get/update, webhooks CRUD, workspace info. When adding new functionality, add it to all three.
43
43
 
44
+ ## Implementation Order
45
+
46
+ Always follow this order when adding or modifying any feature:
47
+
48
+ 1. **SDK first** — implement the core logic in `src/sdk/KanbanSDK.ts` (or supporting SDK files) with full JSDoc comments before touching any interface layer.
49
+ 2. **API** — add or update the corresponding REST endpoint(s) in `src/standalone/server.ts`.
50
+ 3. **CLI** — add or update the command in `src/cli/index.ts`.
51
+ 4. **MCP** — add or update the tool definition in `src/mcp-server/index.ts`.
52
+
53
+ Never implement a feature directly in an interface layer without the SDK method existing first. The SDK is the single source of truth for all business logic.
54
+
44
55
  ## Data Storage
45
56
 
46
57
  - Cards: `.kanban/{status}/card-name-YYYY-MM-DD.md` (markdown with YAML frontmatter)
@@ -73,6 +84,8 @@ metadata:
73
84
 
74
85
  - **JSDoc:** Always update JSDoc comments when changing any logic, parameters, return types, or behavior of functions, methods, interfaces, or types. JSDoc is the source of truth for `docs/sdk.md`.
75
86
  - **Generated docs:** `docs/api.md`, `docs/sdk.md`, and `docs/webhooks.md` are autogenerated. Never edit them manually. Update the source (JSDoc for SDK, route metadata in `scripts/generate-api-docs.ts` for API, `scripts/generate-webhooks-docs.ts` for webhooks) and run `npm run docs` to regenerate.
87
+ - **README.md:** Always update `README.md` when adding or modifying user-facing features. Update the relevant Features section, configuration examples, CLI usage examples, REST API tables, and MCP tool table as appropriate.
88
+ - **CHANGELOG.md:** Always add an entry to `CHANGELOG.md` for every new feature, behaviour change, or bug fix. Place it under an `## [Unreleased]` section at the top (or the current in-progress version block). Follow the existing format: `### Added`, `### Changed`, `### Fixed` subsections with concise bullet points.
76
89
 
77
90
  ## Conventions
78
91
 
package/CHANGELOG.md CHANGED
@@ -5,6 +5,74 @@ All notable changes to the Kanban Lite extension will be documented in this file
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [2.1.0] - 2026-02-27
9
+
10
+ ### Added
11
+ - Board and card detail zoom settings with slider UI (75–150%) stored in `.kanban.json`
12
+ - Keyboard shortcuts for adjusting board/card zoom level (Ctrl/Cmd `+`/`-`)
13
+ - CSS custom properties (`--board-zoom`, `--card-zoom`) with `calc()` multipliers for smooth font scaling
14
+ - Smooth scrolling to the selected feature card in the kanban board
15
+ - Sorting options in the column context menu
16
+ - Default zoom level configuration for both board and card detail views
17
+
18
+ ## [2.0.0] - 2026-02-26
19
+
20
+ ### Added
21
+ - Per-card actions (named string labels) that trigger a global `actionWebhookUrl` via `POST` on demand
22
+ - Run Actions dropdown in the card editor and action input in CreateFeatureDialog
23
+ - `triggerAction` method in KanbanSDK with full support across REST API, WebSocket, MCP (`trigger_action` tool), and CLI (`--actions` flag)
24
+ - Comment editor component with Write / Preview tabs and a markdown formatting toolbar
25
+ - GitHub-style comment editing using the new CommentEditor in CommentsSection
26
+ - Settings panel split into three tabs: **General**, **Defaults**, and **Labels**
27
+ - `version` field on card frontmatter schema for format tracking
28
+ - Metadata filtering for card list/search operations across all interfaces
29
+ - Creation and modification date display with hover tooltips on FeatureCard and FeatureEditor
30
+ - Sort order filter for card queries
31
+
32
+ ### Fixed
33
+ - `version` field now included in all FeatureFrontmatter constructions in the server
34
+
35
+ ## [1.9.0] - 2026-02-25
36
+
37
+ ### Added
38
+ - Card metadata support — arbitrary key-value data stored as a native YAML block in frontmatter (`metadata` field)
39
+ - Metadata UI: key-count chip `{N}` on card grid and collapsible tree view in the card detail panel
40
+ - Label definitions with color picker in the Settings panel (create, rename, delete labels)
41
+ - Colored labels rendered on cards, in the editor, create dialog, and toolbar
42
+ - Label group filtering across SDK (`filterCardsByLabelGroup`), CLI (`--label-group`), REST API, and MCP tools
43
+ - SDK label management methods: `getLabels`, `setLabel`, `renameLabel`, `deleteLabel`
44
+ - Soft-delete support: hidden **Deleted** column with per-card restore or permanent delete
45
+ - Purge deleted cards functionality to permanently remove all soft-deleted cards
46
+ - `--metadata` flag for CLI `create` and `edit` commands (accepts JSON string)
47
+ - Metadata support in MCP `create_card` and `update_card` tools
48
+ - Metadata support in REST API create/update routes
49
+ - Workspace info section in the Settings panel showing project path and `.kanban.json` parameters
50
+ - `js-yaml` dependency for robust YAML metadata parsing
51
+
52
+ ### Fixed
53
+ - Comment parser no longer breaks on horizontal rules (`---`) inside comment blocks
54
+ - Blank lines in metadata YAML parsed correctly; scalar edge cases handled
55
+
56
+ ## [1.8.0] - 2026-02-24
57
+
58
+ ### Added
59
+ - Multi-board support: board selector dropdown to switch between boards and create new boards
60
+ - Card transfer between boards via a StatusDropdown with a nested board-and-column tree
61
+ - `transferCard` message type and `BoardInfo.columns` field in the extension/standalone protocol
62
+ - Webhooks system: CRUD operations (`create`, `get`, `update`, `delete`, `list`) stored in `.kanban-webhooks.json`
63
+ - Webhook event delivery on card create/update/delete/move with configurable `url`, `events`, and `secret`
64
+ - Webhook management commands in CLI and MCP server
65
+ - Comments functionality: add, edit, and delete comments on feature cards
66
+ - Markdown rendering for comment content
67
+ - Auto-generated SDK docs (`docs/sdk.md`) and REST API docs (`docs/api.md`) from JSDoc / route metadata
68
+ - `npm run docs` script to regenerate all documentation
69
+ - Theme toggle (light / dark) in the board toolbar
70
+ - Release scripts for versioning, changelog generation, and GitHub release creation
71
+
72
+ ### Fixed
73
+ - SDK export paths updated to support both CommonJS and ESM module formats
74
+ - SDK import paths corrected; server feature loading logic improved
75
+
8
76
  ## [1.7.0] - 2026-02-20
9
77
 
10
78
  ### Added
package/README.md CHANGED
@@ -44,10 +44,16 @@ kl add --title "My first task" --priority high
44
44
  - **Layout toggle**: Switch between horizontal and vertical board layouts
45
45
  - **Real-time updates**: WebSocket-powered live sync across clients
46
46
  - **Light & dark mode** support
47
+ - **Tabbed settings panel**: Settings organized into **General**, **Defaults**, and **Labels** tabs
48
+ - **Zoom controls**: Scale the board view and card detail panel independently between 75–150% via settings sliders or keyboard shortcuts
49
+ - **Column sorting**: Sort cards within a column by priority, due date, or creation date from the column menu
50
+ - **Smooth scroll to selection**: Board automatically scrolls to the selected card when switching features
47
51
  - **Keyboard shortcuts**:
48
52
  - `N` - Create new feature
49
53
  - `Esc` - Close dialogs
50
54
  - `Cmd/Ctrl + Enter` - Submit create dialog
55
+ - `Ctrl/Cmd + =` / `Ctrl/Cmd + -` - Zoom board view in / out
56
+ - `Ctrl/Cmd + Shift + =` / `Ctrl/Cmd + Shift + -` - Zoom card detail in / out
51
57
 
52
58
  ### Feature Cards
53
59
 
@@ -628,12 +634,16 @@ Board configuration is stored in `.kanban.json` at your project root. It support
628
634
  "showDueDate": true,
629
635
  "showLabels": true,
630
636
  "compactMode": false,
637
+ "boardZoom": 100,
638
+ "cardZoom": 100,
631
639
  "actionWebhookUrl": "https://example.com/kanban-actions"
632
640
  }
633
641
  ```
634
642
 
635
643
  Columns are fully customizable per board — add, remove, rename, or recolor them from the web UI, CLI, or REST API.
636
644
 
645
+ `boardZoom` and `cardZoom` set the default zoom percentage (75–150) for the board view and card detail panel respectively. They can also be adjusted live in the Settings panel or with `Ctrl/Cmd + =` / `Ctrl/Cmd + -` keyboard shortcuts.
646
+
637
647
  ## AI Agent Integration
638
648
  - **Claude Code**: Default, Plan, Auto-edit, and Full Auto modes
639
649
  - **Codex**: Suggest, Auto-edit, and Full Auto modes
package/dist/cli.js CHANGED
@@ -330,6 +330,8 @@ function migrateConfigV1ToV2(raw) {
330
330
  compactMode: v1.compactMode,
331
331
  markdownEditorMode: v1.markdownEditorMode,
332
332
  showDeletedColumn: false,
333
+ boardZoom: 100,
334
+ cardZoom: 100,
333
335
  port: 3e3
334
336
  };
335
337
  }
@@ -403,7 +405,9 @@ function configToSettings(config) {
403
405
  markdownEditorMode: config.markdownEditorMode,
404
406
  showDeletedColumn: config.showDeletedColumn,
405
407
  defaultPriority: config.defaultPriority,
406
- defaultStatus: config.defaultStatus
408
+ defaultStatus: config.defaultStatus,
409
+ boardZoom: config.boardZoom ?? 100,
410
+ cardZoom: config.cardZoom ?? 100
407
411
  };
408
412
  }
409
413
  function settingsToConfig(config, settings) {
@@ -417,7 +421,9 @@ function settingsToConfig(config, settings) {
417
421
  compactMode: settings.compactMode,
418
422
  showDeletedColumn: settings.showDeletedColumn,
419
423
  defaultPriority: settings.defaultPriority,
420
- defaultStatus: settings.defaultStatus
424
+ defaultStatus: settings.defaultStatus,
425
+ boardZoom: settings.boardZoom,
426
+ cardZoom: settings.cardZoom
421
427
  };
422
428
  }
423
429
  var fs, path, DEFAULT_BOARD_CONFIG, DEFAULT_CONFIG, CONFIG_FILENAME;
@@ -453,6 +459,8 @@ var init_config = __esm({
453
459
  compactMode: false,
454
460
  markdownEditorMode: false,
455
461
  showDeletedColumn: false,
462
+ boardZoom: 100,
463
+ cardZoom: 100,
456
464
  port: 3e3,
457
465
  labels: {}
458
466
  };
@@ -4279,25 +4287,28 @@ var init_KanbanSDK = __esm({
4279
4287
  writeConfig(this.workspaceRoot, config);
4280
4288
  }
4281
4289
  /**
4282
- * Removes a label definition from the workspace configuration.
4283
- *
4284
- * This only removes the color/group definition — cards that use this
4285
- * label keep their label strings. Those labels will render with default
4286
- * gray styling in the UI.
4290
+ * Removes a label definition from the workspace configuration and cascades
4291
+ * the deletion to all cards by removing the label from their `labels` array.
4287
4292
  *
4288
4293
  * @param name - The label name to remove.
4289
4294
  *
4290
4295
  * @example
4291
4296
  * ```ts
4292
- * sdk.deleteLabel('bug')
4297
+ * await sdk.deleteLabel('bug')
4293
4298
  * ```
4294
4299
  */
4295
- deleteLabel(name) {
4300
+ async deleteLabel(name) {
4296
4301
  const config = readConfig(this.workspaceRoot);
4297
4302
  if (config.labels) {
4298
4303
  delete config.labels[name];
4299
4304
  writeConfig(this.workspaceRoot, config);
4300
4305
  }
4306
+ const cards = await this.listCards();
4307
+ for (const card of cards) {
4308
+ if (card.labels.includes(name)) {
4309
+ await this.updateCard(card.id, { labels: card.labels.filter((l) => l !== name) });
4310
+ }
4311
+ }
4301
4312
  }
4302
4313
  /**
4303
4314
  * Renames a label in the configuration and cascades the change to all cards.
@@ -4722,6 +4733,57 @@ var init_KanbanSDK = __esm({
4722
4733
  this.emitEvent("column.deleted", removed);
4723
4734
  return board.columns;
4724
4735
  }
4736
+ /**
4737
+ * Moves all cards in the specified column to the `deleted` (soft-delete) column.
4738
+ *
4739
+ * This is a non-destructive operation — cards are moved to the reserved
4740
+ * `deleted` status and can be restored or permanently deleted later.
4741
+ * The column itself is not removed.
4742
+ *
4743
+ * @param columnId - The ID of the column whose cards should be moved to `deleted`.
4744
+ * @param boardId - Optional board ID. Defaults to the workspace's default board.
4745
+ * @returns A promise resolving to the number of cards that were moved.
4746
+ * @throws {Error} If the column is `'deleted'` (no-op protection).
4747
+ *
4748
+ * @example
4749
+ * ```ts
4750
+ * const moved = await sdk.cleanupColumn('blocked')
4751
+ * console.log(`Moved ${moved} cards to deleted`)
4752
+ * ```
4753
+ */
4754
+ async cleanupColumn(columnId, boardId) {
4755
+ if (columnId === DELETED_STATUS_ID)
4756
+ return 0;
4757
+ const cards = await this.listCards(void 0, boardId);
4758
+ const cardsToMove = cards.filter((c) => c.status === columnId);
4759
+ for (const card of cardsToMove) {
4760
+ await this.moveCard(card.id, DELETED_STATUS_ID, 0, boardId);
4761
+ }
4762
+ return cardsToMove.length;
4763
+ }
4764
+ /**
4765
+ * Permanently deletes all cards currently in the `deleted` column.
4766
+ *
4767
+ * This is equivalent to "empty trash". All soft-deleted cards are
4768
+ * removed from disk. This operation cannot be undone.
4769
+ *
4770
+ * @param boardId - Optional board ID. Defaults to the workspace's default board.
4771
+ * @returns A promise resolving to the number of cards that were permanently deleted.
4772
+ *
4773
+ * @example
4774
+ * ```ts
4775
+ * const count = await sdk.purgeDeletedCards()
4776
+ * console.log(`Permanently deleted ${count} cards`)
4777
+ * ```
4778
+ */
4779
+ async purgeDeletedCards(boardId) {
4780
+ const cards = await this.listCards(void 0, boardId);
4781
+ const deleted = cards.filter((c) => c.status === DELETED_STATUS_ID);
4782
+ for (const card of deleted) {
4783
+ await this.permanentlyDeleteCard(card.id, boardId);
4784
+ }
4785
+ return deleted.length;
4786
+ }
4725
4787
  /**
4726
4788
  * Reorders the columns of a board.
4727
4789
  *
@@ -10558,10 +10620,7 @@ function startServer(featuresDir, port, webviewDir) {
10558
10620
  }
10559
10621
  async function doPurgeDeletedCards() {
10560
10622
  try {
10561
- const deletedCards = features.filter((f) => f.status === "deleted");
10562
- for (const card of deletedCards) {
10563
- await sdk.permanentlyDeleteCard(card.id, currentBoardId);
10564
- }
10623
+ await sdk.purgeDeletedCards(currentBoardId);
10565
10624
  await loadFeatures();
10566
10625
  broadcast(buildInitMessage());
10567
10626
  return true;
@@ -10608,6 +10667,20 @@ function startServer(featuresDir, port, webviewDir) {
10608
10667
  return { removed: false, error: String(err) };
10609
10668
  }
10610
10669
  }
10670
+ async function doCleanupColumn(columnId) {
10671
+ try {
10672
+ migrating = true;
10673
+ await sdk.cleanupColumn(columnId, currentBoardId);
10674
+ await loadFeatures();
10675
+ broadcast(buildInitMessage());
10676
+ return true;
10677
+ } catch (err) {
10678
+ console.error("Failed to cleanup column:", err);
10679
+ return false;
10680
+ } finally {
10681
+ migrating = false;
10682
+ }
10683
+ }
10611
10684
  function doSaveSettings(newSettings) {
10612
10685
  sdk.updateSettings(newSettings);
10613
10686
  broadcast(buildInitMessage());
@@ -10794,6 +10867,9 @@ function startServer(featuresDir, port, webviewDir) {
10794
10867
  case "removeColumn":
10795
10868
  await doRemoveColumn(msg.columnId);
10796
10869
  break;
10870
+ case "cleanupColumn":
10871
+ await doCleanupColumn(msg.columnId);
10872
+ break;
10797
10873
  case "removeAttachment": {
10798
10874
  const featureId = msg.featureId;
10799
10875
  const feature = await doRemoveAttachment(featureId, msg.attachment);
@@ -10942,13 +11018,16 @@ function startServer(featuresDir, port, webviewDir) {
10942
11018
  }
10943
11019
  case "renameLabel": {
10944
11020
  await sdk.renameLabel(msg.oldName, msg.newName);
11021
+ await loadFeatures();
10945
11022
  broadcast({ type: "labelsUpdated", labels: sdk.getLabels() });
10946
11023
  broadcast(buildInitMessage());
10947
11024
  break;
10948
11025
  }
10949
11026
  case "deleteLabel": {
10950
- sdk.deleteLabel(msg.name);
11027
+ await sdk.deleteLabel(msg.name);
11028
+ await loadFeatures();
10951
11029
  broadcast({ type: "labelsUpdated", labels: sdk.getLabels() });
11030
+ broadcast(buildInitMessage());
10952
11031
  break;
10953
11032
  }
10954
11033
  case "triggerAction": {
@@ -11560,6 +11639,9 @@ function startServer(featuresDir, port, webviewDir) {
11560
11639
  if (!newName)
11561
11640
  return jsonError(res, 400, "newName is required");
11562
11641
  await sdk.renameLabel(name, newName);
11642
+ await loadFeatures();
11643
+ broadcast({ type: "labelsUpdated", labels: sdk.getLabels() });
11644
+ broadcast(buildInitMessage());
11563
11645
  return jsonOk(res, sdk.getLabels());
11564
11646
  } catch (err) {
11565
11647
  return jsonError(res, 400, String(err));
@@ -11569,7 +11651,10 @@ function startServer(featuresDir, port, webviewDir) {
11569
11651
  if (params) {
11570
11652
  try {
11571
11653
  const name = decodeURIComponent(params.name);
11572
- sdk.deleteLabel(name);
11654
+ await sdk.deleteLabel(name);
11655
+ await loadFeatures();
11656
+ broadcast({ type: "labelsUpdated", labels: sdk.getLabels() });
11657
+ broadcast(buildInitMessage());
11573
11658
  return jsonOk(res, { success: true });
11574
11659
  } catch (err) {
11575
11660
  return jsonError(res, 400, String(err));
@@ -13113,9 +13198,19 @@ async function cmdColumns(sdk, positional, flags) {
13113
13198
  console.log(JSON.stringify(columns, null, 2));
13114
13199
  break;
13115
13200
  }
13201
+ case "cleanup": {
13202
+ const columnId = positional[1];
13203
+ if (!columnId) {
13204
+ console.error(red("Usage: kl columns cleanup <id>"));
13205
+ process.exit(1);
13206
+ }
13207
+ const moved = await sdk.cleanupColumn(columnId, boardId);
13208
+ console.log(green(`Moved ${moved} card${moved === 1 ? "" : "s"} from "${columnId}" to deleted`));
13209
+ break;
13210
+ }
13116
13211
  default:
13117
13212
  console.error(red(`Unknown columns subcommand: ${subcommand}`));
13118
- console.error("Available: list, add, update, remove");
13213
+ console.error("Available: list, add, update, remove, cleanup");
13119
13214
  process.exit(1);
13120
13215
  }
13121
13216
  }
@@ -13189,7 +13284,7 @@ async function cmdLabels(sdk, positional, flags) {
13189
13284
  console.error(red("Usage: kl labels delete <name>"));
13190
13285
  process.exit(1);
13191
13286
  }
13192
- sdk.deleteLabel(name);
13287
+ await sdk.deleteLabel(name);
13193
13288
  if (flags.json) {
13194
13289
  console.log(JSON.stringify({ deleted: name }, null, 2));
13195
13290
  } else {
package/dist/extension.js CHANGED
@@ -3845,6 +3845,8 @@ var DEFAULT_CONFIG = {
3845
3845
  compactMode: false,
3846
3846
  markdownEditorMode: false,
3847
3847
  showDeletedColumn: false,
3848
+ boardZoom: 100,
3849
+ cardZoom: 100,
3848
3850
  port: 3e3,
3849
3851
  labels: {}
3850
3852
  };
@@ -3895,6 +3897,8 @@ function migrateConfigV1ToV2(raw) {
3895
3897
  compactMode: v1.compactMode,
3896
3898
  markdownEditorMode: v1.markdownEditorMode,
3897
3899
  showDeletedColumn: false,
3900
+ boardZoom: 100,
3901
+ cardZoom: 100,
3898
3902
  port: 3e3
3899
3903
  };
3900
3904
  }
@@ -3968,7 +3972,9 @@ function configToSettings(config) {
3968
3972
  markdownEditorMode: config.markdownEditorMode,
3969
3973
  showDeletedColumn: config.showDeletedColumn,
3970
3974
  defaultPriority: config.defaultPriority,
3971
- defaultStatus: config.defaultStatus
3975
+ defaultStatus: config.defaultStatus,
3976
+ boardZoom: config.boardZoom ?? 100,
3977
+ cardZoom: config.cardZoom ?? 100
3972
3978
  };
3973
3979
  }
3974
3980
  function settingsToConfig(config, settings) {
@@ -3982,7 +3988,9 @@ function settingsToConfig(config, settings) {
3982
3988
  compactMode: settings.compactMode,
3983
3989
  showDeletedColumn: settings.showDeletedColumn,
3984
3990
  defaultPriority: settings.defaultPriority,
3985
- defaultStatus: settings.defaultStatus
3991
+ defaultStatus: settings.defaultStatus,
3992
+ boardZoom: settings.boardZoom,
3993
+ cardZoom: settings.cardZoom
3986
3994
  };
3987
3995
  }
3988
3996
 
@@ -7977,25 +7985,28 @@ var KanbanSDK = class {
7977
7985
  writeConfig(this.workspaceRoot, config);
7978
7986
  }
7979
7987
  /**
7980
- * Removes a label definition from the workspace configuration.
7981
- *
7982
- * This only removes the color/group definition — cards that use this
7983
- * label keep their label strings. Those labels will render with default
7984
- * gray styling in the UI.
7988
+ * Removes a label definition from the workspace configuration and cascades
7989
+ * the deletion to all cards by removing the label from their `labels` array.
7985
7990
  *
7986
7991
  * @param name - The label name to remove.
7987
7992
  *
7988
7993
  * @example
7989
7994
  * ```ts
7990
- * sdk.deleteLabel('bug')
7995
+ * await sdk.deleteLabel('bug')
7991
7996
  * ```
7992
7997
  */
7993
- deleteLabel(name) {
7998
+ async deleteLabel(name) {
7994
7999
  const config = readConfig(this.workspaceRoot);
7995
8000
  if (config.labels) {
7996
8001
  delete config.labels[name];
7997
8002
  writeConfig(this.workspaceRoot, config);
7998
8003
  }
8004
+ const cards = await this.listCards();
8005
+ for (const card of cards) {
8006
+ if (card.labels.includes(name)) {
8007
+ await this.updateCard(card.id, { labels: card.labels.filter((l) => l !== name) });
8008
+ }
8009
+ }
7999
8010
  }
8000
8011
  /**
8001
8012
  * Renames a label in the configuration and cascades the change to all cards.
@@ -8420,6 +8431,57 @@ var KanbanSDK = class {
8420
8431
  this.emitEvent("column.deleted", removed);
8421
8432
  return board.columns;
8422
8433
  }
8434
+ /**
8435
+ * Moves all cards in the specified column to the `deleted` (soft-delete) column.
8436
+ *
8437
+ * This is a non-destructive operation — cards are moved to the reserved
8438
+ * `deleted` status and can be restored or permanently deleted later.
8439
+ * The column itself is not removed.
8440
+ *
8441
+ * @param columnId - The ID of the column whose cards should be moved to `deleted`.
8442
+ * @param boardId - Optional board ID. Defaults to the workspace's default board.
8443
+ * @returns A promise resolving to the number of cards that were moved.
8444
+ * @throws {Error} If the column is `'deleted'` (no-op protection).
8445
+ *
8446
+ * @example
8447
+ * ```ts
8448
+ * const moved = await sdk.cleanupColumn('blocked')
8449
+ * console.log(`Moved ${moved} cards to deleted`)
8450
+ * ```
8451
+ */
8452
+ async cleanupColumn(columnId, boardId) {
8453
+ if (columnId === DELETED_STATUS_ID)
8454
+ return 0;
8455
+ const cards = await this.listCards(void 0, boardId);
8456
+ const cardsToMove = cards.filter((c) => c.status === columnId);
8457
+ for (const card of cardsToMove) {
8458
+ await this.moveCard(card.id, DELETED_STATUS_ID, 0, boardId);
8459
+ }
8460
+ return cardsToMove.length;
8461
+ }
8462
+ /**
8463
+ * Permanently deletes all cards currently in the `deleted` column.
8464
+ *
8465
+ * This is equivalent to "empty trash". All soft-deleted cards are
8466
+ * removed from disk. This operation cannot be undone.
8467
+ *
8468
+ * @param boardId - Optional board ID. Defaults to the workspace's default board.
8469
+ * @returns A promise resolving to the number of cards that were permanently deleted.
8470
+ *
8471
+ * @example
8472
+ * ```ts
8473
+ * const count = await sdk.purgeDeletedCards()
8474
+ * console.log(`Permanently deleted ${count} cards`)
8475
+ * ```
8476
+ */
8477
+ async purgeDeletedCards(boardId) {
8478
+ const cards = await this.listCards(void 0, boardId);
8479
+ const deleted = cards.filter((c) => c.status === DELETED_STATUS_ID);
8480
+ for (const card of deleted) {
8481
+ await this.permanentlyDeleteCard(card.id, boardId);
8482
+ }
8483
+ return deleted.length;
8484
+ }
8423
8485
  /**
8424
8486
  * Reorders the columns of a board.
8425
8487
  *
@@ -10532,10 +10594,7 @@ function startServer(featuresDir, port, webviewDir) {
10532
10594
  }
10533
10595
  async function doPurgeDeletedCards() {
10534
10596
  try {
10535
- const deletedCards = features.filter((f) => f.status === "deleted");
10536
- for (const card of deletedCards) {
10537
- await sdk.permanentlyDeleteCard(card.id, currentBoardId);
10538
- }
10597
+ await sdk.purgeDeletedCards(currentBoardId);
10539
10598
  await loadFeatures();
10540
10599
  broadcast(buildInitMessage());
10541
10600
  return true;
@@ -10582,6 +10641,20 @@ function startServer(featuresDir, port, webviewDir) {
10582
10641
  return { removed: false, error: String(err) };
10583
10642
  }
10584
10643
  }
10644
+ async function doCleanupColumn(columnId) {
10645
+ try {
10646
+ migrating = true;
10647
+ await sdk.cleanupColumn(columnId, currentBoardId);
10648
+ await loadFeatures();
10649
+ broadcast(buildInitMessage());
10650
+ return true;
10651
+ } catch (err) {
10652
+ console.error("Failed to cleanup column:", err);
10653
+ return false;
10654
+ } finally {
10655
+ migrating = false;
10656
+ }
10657
+ }
10585
10658
  function doSaveSettings(newSettings) {
10586
10659
  sdk.updateSettings(newSettings);
10587
10660
  broadcast(buildInitMessage());
@@ -10768,6 +10841,9 @@ function startServer(featuresDir, port, webviewDir) {
10768
10841
  case "removeColumn":
10769
10842
  await doRemoveColumn(msg.columnId);
10770
10843
  break;
10844
+ case "cleanupColumn":
10845
+ await doCleanupColumn(msg.columnId);
10846
+ break;
10771
10847
  case "removeAttachment": {
10772
10848
  const featureId = msg.featureId;
10773
10849
  const feature = await doRemoveAttachment(featureId, msg.attachment);
@@ -10916,13 +10992,16 @@ function startServer(featuresDir, port, webviewDir) {
10916
10992
  }
10917
10993
  case "renameLabel": {
10918
10994
  await sdk.renameLabel(msg.oldName, msg.newName);
10995
+ await loadFeatures();
10919
10996
  broadcast({ type: "labelsUpdated", labels: sdk.getLabels() });
10920
10997
  broadcast(buildInitMessage());
10921
10998
  break;
10922
10999
  }
10923
11000
  case "deleteLabel": {
10924
- sdk.deleteLabel(msg.name);
11001
+ await sdk.deleteLabel(msg.name);
11002
+ await loadFeatures();
10925
11003
  broadcast({ type: "labelsUpdated", labels: sdk.getLabels() });
11004
+ broadcast(buildInitMessage());
10926
11005
  break;
10927
11006
  }
10928
11007
  case "triggerAction": {
@@ -11534,6 +11613,9 @@ function startServer(featuresDir, port, webviewDir) {
11534
11613
  if (!newName)
11535
11614
  return jsonError(res, 400, "newName is required");
11536
11615
  await sdk.renameLabel(name, newName);
11616
+ await loadFeatures();
11617
+ broadcast({ type: "labelsUpdated", labels: sdk.getLabels() });
11618
+ broadcast(buildInitMessage());
11537
11619
  return jsonOk(res, sdk.getLabels());
11538
11620
  } catch (err) {
11539
11621
  return jsonError(res, 400, String(err));
@@ -11543,7 +11625,10 @@ function startServer(featuresDir, port, webviewDir) {
11543
11625
  if (params) {
11544
11626
  try {
11545
11627
  const name = decodeURIComponent(params.name);
11546
- sdk.deleteLabel(name);
11628
+ await sdk.deleteLabel(name);
11629
+ await loadFeatures();
11630
+ broadcast({ type: "labelsUpdated", labels: sdk.getLabels() });
11631
+ broadcast(buildInitMessage());
11547
11632
  return jsonOk(res, { success: true });
11548
11633
  } catch (err) {
11549
11634
  return jsonError(res, 400, String(err));
@@ -11856,6 +11941,9 @@ var KanbanPanel = class _KanbanPanel {
11856
11941
  case "removeColumn":
11857
11942
  await this._removeColumn(message.columnId);
11858
11943
  break;
11944
+ case "cleanupColumn":
11945
+ await this._cleanupColumn(message.columnId);
11946
+ break;
11859
11947
  case "transferCard": {
11860
11948
  const sdk = this._getSDK();
11861
11949
  if (!sdk || !this._currentBoardId)
@@ -11922,7 +12010,8 @@ var KanbanPanel = class _KanbanPanel {
11922
12010
  const sdk = this._getSDK();
11923
12011
  if (!sdk)
11924
12012
  break;
11925
- sdk.deleteLabel(message.name);
12013
+ await sdk.deleteLabel(message.name);
12014
+ await this._loadFeatures();
11926
12015
  this._sendFeaturesToWebview();
11927
12016
  this._panel.webview.postMessage({ type: "labelsUpdated", labels: sdk.getLabels() });
11928
12017
  break;
@@ -12211,10 +12300,7 @@ var KanbanPanel = class _KanbanPanel {
12211
12300
  if (!sdk)
12212
12301
  return;
12213
12302
  try {
12214
- const deletedCards = this._features.filter((f) => f.status === "deleted");
12215
- for (const card of deletedCards) {
12216
- await sdk.permanentlyDeleteCard(card.id, this._currentBoardId);
12217
- }
12303
+ await sdk.purgeDeletedCards(this._currentBoardId);
12218
12304
  this._features = this._features.filter((f) => f.status !== "deleted");
12219
12305
  this._sendFeaturesToWebview();
12220
12306
  } catch (err) {
@@ -12553,6 +12639,23 @@ var KanbanPanel = class _KanbanPanel {
12553
12639
  sdk.removeColumn(columnId, this._currentBoardId);
12554
12640
  this._sendFeaturesToWebview();
12555
12641
  }
12642
+ async _cleanupColumn(columnId) {
12643
+ const sdk = this._getSDK();
12644
+ if (!sdk)
12645
+ return;
12646
+ this._migrating = true;
12647
+ try {
12648
+ await sdk.cleanupColumn(columnId, this._currentBoardId);
12649
+ this._features = this._features.map(
12650
+ (f) => f.status === columnId ? { ...f, status: "deleted" } : f
12651
+ );
12652
+ this._sendFeaturesToWebview();
12653
+ } catch (err) {
12654
+ vscode.window.showErrorMessage(`Failed to cleanup list: ${err}`);
12655
+ } finally {
12656
+ this._migrating = false;
12657
+ }
12658
+ }
12556
12659
  };
12557
12660
 
12558
12661
  // src/extension/SidebarViewProvider.ts