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.
- package/{CLAUDE.md → AGENTS.md} +13 -0
- package/CHANGELOG.md +68 -0
- package/README.md +10 -0
- package/dist/cli.js +112 -17
- package/dist/extension.js +123 -20
- package/dist/mcp-server.js +90 -11
- package/dist/sdk/index.cjs +71 -9
- package/dist/sdk/index.mjs +71 -9
- package/dist/sdk/sdk/KanbanSDK.d.ts +39 -7
- package/dist/sdk/shared/config.d.ts +4 -0
- package/dist/sdk/shared/types.d.ts +4 -0
- package/dist/standalone-webview/index.js +45 -45
- package/dist/standalone-webview/index.js.map +1 -1
- package/dist/standalone-webview/style.css +1 -1
- package/dist/standalone.js +155 -99
- package/dist/webview/index.js +45 -45
- package/dist/webview/index.js.map +1 -1
- package/dist/webview/style.css +1 -1
- package/docs/plans/2026-02-27-zoom-settings-design.md +82 -0
- package/docs/plans/2026-02-27-zoom-settings.md +395 -0
- package/docs/sdk.md +3 -6
- package/package.json +1 -1
- package/src/cli/index.ts +12 -2
- package/src/extension/KanbanPanel.ts +25 -5
- package/src/mcp-server/index.ts +20 -2
- package/src/sdk/KanbanSDK.ts +64 -7
- package/src/sdk/__tests__/KanbanSDK.test.ts +17 -1
- package/src/sdk/__tests__/metadata.test.ts +2 -0
- package/src/sdk/__tests__/multi-board.test.ts +2 -0
- package/src/shared/config.ts +14 -2
- package/src/shared/types.ts +4 -0
- package/src/standalone/server.ts +31 -6
- package/src/webview/App.tsx +42 -3
- package/src/webview/assets/main.css +31 -2
- package/src/webview/components/KanbanBoard.tsx +28 -3
- package/src/webview/components/KanbanColumn.tsx +24 -13
- package/src/webview/components/SettingsPanel.tsx +68 -0
- package/src/webview/components/Toolbar.tsx +126 -16
- package/src/webview/store/index.ts +13 -20
package/{CLAUDE.md → AGENTS.md}
RENAMED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|