kanban-lite 1.0.21 → 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 +168 -102
- package/dist/extension.js +178 -104
- package/dist/mcp-server.js +145 -95
- package/dist/sdk/index.cjs +126 -93
- package/dist/sdk/index.mjs +126 -93
- 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 +58 -58
- package/dist/standalone-webview/index.js.map +1 -1
- package/dist/standalone-webview/style.css +1 -1
- package/dist/standalone.js +606 -364
- package/dist/webview/index.js +57 -57
- package/dist/webview/index.js.map +1 -1
- package/dist/webview/style.css +1 -1
- package/docs/plans/2026-02-26-settings-tabs-design.md +40 -0
- package/docs/plans/2026-02-26-settings-tabs.md +166 -0
- 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 +3 -1
- package/src/sdk/__tests__/multi-board.test.ts +2 -0
- package/src/sdk/parser.ts +50 -83
- package/src/shared/config.ts +14 -2
- package/src/shared/types.ts +4 -0
- package/src/standalone/__tests__/server.integration.test.ts +2 -2
- package/src/standalone/index.ts +7 -4
- 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 +35 -3
- package/src/webview/components/KanbanColumn.tsx +40 -4
- package/src/webview/components/SettingsPanel.tsx +179 -77
- package/src/webview/components/Toolbar.tsx +127 -32
- package/src/webview/store/index.ts +26 -28
package/dist/sdk/index.mjs
CHANGED
|
@@ -270,6 +270,8 @@ var DEFAULT_CONFIG = {
|
|
|
270
270
|
compactMode: false,
|
|
271
271
|
markdownEditorMode: false,
|
|
272
272
|
showDeletedColumn: false,
|
|
273
|
+
boardZoom: 100,
|
|
274
|
+
cardZoom: 100,
|
|
273
275
|
port: 3e3,
|
|
274
276
|
labels: {}
|
|
275
277
|
};
|
|
@@ -320,6 +322,8 @@ function migrateConfigV1ToV2(raw) {
|
|
|
320
322
|
compactMode: v1.compactMode,
|
|
321
323
|
markdownEditorMode: v1.markdownEditorMode,
|
|
322
324
|
showDeletedColumn: false,
|
|
325
|
+
boardZoom: 100,
|
|
326
|
+
cardZoom: 100,
|
|
323
327
|
port: 3e3
|
|
324
328
|
};
|
|
325
329
|
}
|
|
@@ -397,7 +401,9 @@ function configToSettings(config) {
|
|
|
397
401
|
markdownEditorMode: config.markdownEditorMode,
|
|
398
402
|
showDeletedColumn: config.showDeletedColumn,
|
|
399
403
|
defaultPriority: config.defaultPriority,
|
|
400
|
-
defaultStatus: config.defaultStatus
|
|
404
|
+
defaultStatus: config.defaultStatus,
|
|
405
|
+
boardZoom: config.boardZoom ?? 100,
|
|
406
|
+
cardZoom: config.cardZoom ?? 100
|
|
401
407
|
};
|
|
402
408
|
}
|
|
403
409
|
function settingsToConfig(config, settings) {
|
|
@@ -411,7 +417,9 @@ function settingsToConfig(config, settings) {
|
|
|
411
417
|
compactMode: settings.compactMode,
|
|
412
418
|
showDeletedColumn: settings.showDeletedColumn,
|
|
413
419
|
defaultPriority: settings.defaultPriority,
|
|
414
|
-
defaultStatus: settings.defaultStatus
|
|
420
|
+
defaultStatus: settings.defaultStatus,
|
|
421
|
+
boardZoom: settings.boardZoom,
|
|
422
|
+
cardZoom: settings.cardZoom
|
|
415
423
|
};
|
|
416
424
|
}
|
|
417
425
|
|
|
@@ -3071,6 +3079,7 @@ function renamed(from, to) {
|
|
|
3071
3079
|
throw new Error("Function yaml." + from + " is removed in js-yaml 4. Use yaml." + to + " instead, which is now safe by default.");
|
|
3072
3080
|
};
|
|
3073
3081
|
}
|
|
3082
|
+
var JSON_SCHEMA = json;
|
|
3074
3083
|
var load = loader.load;
|
|
3075
3084
|
var loadAll = loader.loadAll;
|
|
3076
3085
|
var dump = dumper.dump;
|
|
@@ -3109,48 +3118,26 @@ function parseFeatureFile(content, filePath) {
|
|
|
3109
3118
|
return null;
|
|
3110
3119
|
const frontmatter = frontmatterMatch[1];
|
|
3111
3120
|
const rest = frontmatterMatch[2] || "";
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3121
|
+
let parsed;
|
|
3122
|
+
try {
|
|
3123
|
+
const loaded = load(frontmatter, { schema: JSON_SCHEMA });
|
|
3124
|
+
if (!loaded || typeof loaded !== "object" || Array.isArray(loaded))
|
|
3125
|
+
return null;
|
|
3126
|
+
parsed = loaded;
|
|
3127
|
+
} catch {
|
|
3128
|
+
return null;
|
|
3129
|
+
}
|
|
3130
|
+
const str2 = (key) => {
|
|
3131
|
+
const val = parsed[key];
|
|
3132
|
+
if (val == null)
|
|
3115
3133
|
return "";
|
|
3116
|
-
|
|
3117
|
-
return value === "null" ? "" : value;
|
|
3134
|
+
return String(val);
|
|
3118
3135
|
};
|
|
3119
|
-
const
|
|
3120
|
-
const
|
|
3121
|
-
if (!
|
|
3136
|
+
const arr = (key) => {
|
|
3137
|
+
const val = parsed[key];
|
|
3138
|
+
if (!Array.isArray(val))
|
|
3122
3139
|
return [];
|
|
3123
|
-
return
|
|
3124
|
-
};
|
|
3125
|
-
const getMetadata = () => {
|
|
3126
|
-
const lines = frontmatter.split("\n");
|
|
3127
|
-
let metaStart = -1;
|
|
3128
|
-
for (let j = 0; j < lines.length; j++) {
|
|
3129
|
-
if (/^metadata:\s*$/.test(lines[j])) {
|
|
3130
|
-
metaStart = j + 1;
|
|
3131
|
-
break;
|
|
3132
|
-
}
|
|
3133
|
-
}
|
|
3134
|
-
if (metaStart === -1)
|
|
3135
|
-
return void 0;
|
|
3136
|
-
const indentedLines = [];
|
|
3137
|
-
for (let j = metaStart; j < lines.length; j++) {
|
|
3138
|
-
if (/^\s/.test(lines[j]) || lines[j].trim() === "") {
|
|
3139
|
-
indentedLines.push(lines[j]);
|
|
3140
|
-
} else {
|
|
3141
|
-
break;
|
|
3142
|
-
}
|
|
3143
|
-
}
|
|
3144
|
-
if (indentedLines.length === 0)
|
|
3145
|
-
return void 0;
|
|
3146
|
-
try {
|
|
3147
|
-
const parsed = load(indentedLines.join("\n"));
|
|
3148
|
-
if (parsed && typeof parsed === "object")
|
|
3149
|
-
return parsed;
|
|
3150
|
-
return void 0;
|
|
3151
|
-
} catch {
|
|
3152
|
-
return void 0;
|
|
3153
|
-
}
|
|
3140
|
+
return val.filter((v) => v != null).map(String);
|
|
3154
3141
|
};
|
|
3155
3142
|
const sections = rest.split(/\n---\n/);
|
|
3156
3143
|
let body = sections[0] || "";
|
|
@@ -3171,22 +3158,23 @@ ${section}`;
|
|
|
3171
3158
|
i += 1;
|
|
3172
3159
|
}
|
|
3173
3160
|
}
|
|
3174
|
-
const
|
|
3175
|
-
const
|
|
3161
|
+
const actions = arr("actions");
|
|
3162
|
+
const rawMeta = parsed.metadata;
|
|
3163
|
+
const meta = rawMeta != null && typeof rawMeta === "object" && !Array.isArray(rawMeta) ? rawMeta : void 0;
|
|
3176
3164
|
return {
|
|
3177
|
-
version: parseInt(
|
|
3178
|
-
id:
|
|
3179
|
-
status:
|
|
3180
|
-
priority:
|
|
3181
|
-
assignee:
|
|
3182
|
-
dueDate:
|
|
3183
|
-
created:
|
|
3184
|
-
modified:
|
|
3185
|
-
completedAt:
|
|
3186
|
-
labels:
|
|
3187
|
-
attachments:
|
|
3165
|
+
version: typeof parsed.version === "number" ? parsed.version : parseInt(str2("version"), 10) || 0,
|
|
3166
|
+
id: str2("id") || extractIdFromFilename(filePath),
|
|
3167
|
+
status: str2("status") || "backlog",
|
|
3168
|
+
priority: str2("priority") || "medium",
|
|
3169
|
+
assignee: parsed.assignee != null ? String(parsed.assignee) : null,
|
|
3170
|
+
dueDate: parsed.dueDate != null ? String(parsed.dueDate) : null,
|
|
3171
|
+
created: str2("created") || (/* @__PURE__ */ new Date()).toISOString(),
|
|
3172
|
+
modified: str2("modified") || (/* @__PURE__ */ new Date()).toISOString(),
|
|
3173
|
+
completedAt: parsed.completedAt != null ? String(parsed.completedAt) : null,
|
|
3174
|
+
labels: arr("labels"),
|
|
3175
|
+
attachments: arr("attachments"),
|
|
3188
3176
|
comments,
|
|
3189
|
-
order:
|
|
3177
|
+
order: str2("order") || "a0",
|
|
3190
3178
|
content: body.trim(),
|
|
3191
3179
|
...meta ? { metadata: meta } : {},
|
|
3192
3180
|
...actions.length > 0 ? { actions } : {},
|
|
@@ -3194,37 +3182,28 @@ ${section}`;
|
|
|
3194
3182
|
};
|
|
3195
3183
|
}
|
|
3196
3184
|
function serializeFeature(feature) {
|
|
3197
|
-
const
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
lines.push(" " + line);
|
|
3220
|
-
}
|
|
3221
|
-
}
|
|
3222
|
-
lines.push("---");
|
|
3223
|
-
lines.push("");
|
|
3224
|
-
const frontmatter = lines.join("\n");
|
|
3225
|
-
let result = frontmatter + feature.content;
|
|
3226
|
-
const comments = feature.comments || [];
|
|
3227
|
-
for (const comment of comments) {
|
|
3185
|
+
const frontmatterObj = {
|
|
3186
|
+
version: feature.version ?? CARD_FORMAT_VERSION,
|
|
3187
|
+
id: feature.id,
|
|
3188
|
+
status: feature.status,
|
|
3189
|
+
priority: feature.priority,
|
|
3190
|
+
assignee: feature.assignee ?? null,
|
|
3191
|
+
dueDate: feature.dueDate ?? null,
|
|
3192
|
+
created: feature.created,
|
|
3193
|
+
modified: feature.modified,
|
|
3194
|
+
completedAt: feature.completedAt ?? null,
|
|
3195
|
+
labels: feature.labels,
|
|
3196
|
+
attachments: feature.attachments || [],
|
|
3197
|
+
order: feature.order,
|
|
3198
|
+
...feature.actions?.length ? { actions: feature.actions } : {},
|
|
3199
|
+
...feature.metadata && Object.keys(feature.metadata).length > 0 ? { metadata: feature.metadata } : {}
|
|
3200
|
+
};
|
|
3201
|
+
const yamlStr = dump(frontmatterObj, { lineWidth: -1, quotingType: '"', forceQuotes: true });
|
|
3202
|
+
let result = `---
|
|
3203
|
+
${yamlStr}---
|
|
3204
|
+
|
|
3205
|
+
${feature.content}`;
|
|
3206
|
+
for (const comment of feature.comments || []) {
|
|
3228
3207
|
result += "\n\n---\n";
|
|
3229
3208
|
result += `comment: true
|
|
3230
3209
|
`;
|
|
@@ -4217,25 +4196,28 @@ var KanbanSDK = class {
|
|
|
4217
4196
|
writeConfig(this.workspaceRoot, config);
|
|
4218
4197
|
}
|
|
4219
4198
|
/**
|
|
4220
|
-
* Removes a label definition from the workspace configuration
|
|
4221
|
-
*
|
|
4222
|
-
* This only removes the color/group definition — cards that use this
|
|
4223
|
-
* label keep their label strings. Those labels will render with default
|
|
4224
|
-
* gray styling in the UI.
|
|
4199
|
+
* Removes a label definition from the workspace configuration and cascades
|
|
4200
|
+
* the deletion to all cards by removing the label from their `labels` array.
|
|
4225
4201
|
*
|
|
4226
4202
|
* @param name - The label name to remove.
|
|
4227
4203
|
*
|
|
4228
4204
|
* @example
|
|
4229
4205
|
* ```ts
|
|
4230
|
-
* sdk.deleteLabel('bug')
|
|
4206
|
+
* await sdk.deleteLabel('bug')
|
|
4231
4207
|
* ```
|
|
4232
4208
|
*/
|
|
4233
|
-
deleteLabel(name) {
|
|
4209
|
+
async deleteLabel(name) {
|
|
4234
4210
|
const config = readConfig(this.workspaceRoot);
|
|
4235
4211
|
if (config.labels) {
|
|
4236
4212
|
delete config.labels[name];
|
|
4237
4213
|
writeConfig(this.workspaceRoot, config);
|
|
4238
4214
|
}
|
|
4215
|
+
const cards = await this.listCards();
|
|
4216
|
+
for (const card of cards) {
|
|
4217
|
+
if (card.labels.includes(name)) {
|
|
4218
|
+
await this.updateCard(card.id, { labels: card.labels.filter((l) => l !== name) });
|
|
4219
|
+
}
|
|
4220
|
+
}
|
|
4239
4221
|
}
|
|
4240
4222
|
/**
|
|
4241
4223
|
* Renames a label in the configuration and cascades the change to all cards.
|
|
@@ -4660,6 +4642,57 @@ var KanbanSDK = class {
|
|
|
4660
4642
|
this.emitEvent("column.deleted", removed);
|
|
4661
4643
|
return board.columns;
|
|
4662
4644
|
}
|
|
4645
|
+
/**
|
|
4646
|
+
* Moves all cards in the specified column to the `deleted` (soft-delete) column.
|
|
4647
|
+
*
|
|
4648
|
+
* This is a non-destructive operation — cards are moved to the reserved
|
|
4649
|
+
* `deleted` status and can be restored or permanently deleted later.
|
|
4650
|
+
* The column itself is not removed.
|
|
4651
|
+
*
|
|
4652
|
+
* @param columnId - The ID of the column whose cards should be moved to `deleted`.
|
|
4653
|
+
* @param boardId - Optional board ID. Defaults to the workspace's default board.
|
|
4654
|
+
* @returns A promise resolving to the number of cards that were moved.
|
|
4655
|
+
* @throws {Error} If the column is `'deleted'` (no-op protection).
|
|
4656
|
+
*
|
|
4657
|
+
* @example
|
|
4658
|
+
* ```ts
|
|
4659
|
+
* const moved = await sdk.cleanupColumn('blocked')
|
|
4660
|
+
* console.log(`Moved ${moved} cards to deleted`)
|
|
4661
|
+
* ```
|
|
4662
|
+
*/
|
|
4663
|
+
async cleanupColumn(columnId, boardId) {
|
|
4664
|
+
if (columnId === DELETED_STATUS_ID)
|
|
4665
|
+
return 0;
|
|
4666
|
+
const cards = await this.listCards(void 0, boardId);
|
|
4667
|
+
const cardsToMove = cards.filter((c) => c.status === columnId);
|
|
4668
|
+
for (const card of cardsToMove) {
|
|
4669
|
+
await this.moveCard(card.id, DELETED_STATUS_ID, 0, boardId);
|
|
4670
|
+
}
|
|
4671
|
+
return cardsToMove.length;
|
|
4672
|
+
}
|
|
4673
|
+
/**
|
|
4674
|
+
* Permanently deletes all cards currently in the `deleted` column.
|
|
4675
|
+
*
|
|
4676
|
+
* This is equivalent to "empty trash". All soft-deleted cards are
|
|
4677
|
+
* removed from disk. This operation cannot be undone.
|
|
4678
|
+
*
|
|
4679
|
+
* @param boardId - Optional board ID. Defaults to the workspace's default board.
|
|
4680
|
+
* @returns A promise resolving to the number of cards that were permanently deleted.
|
|
4681
|
+
*
|
|
4682
|
+
* @example
|
|
4683
|
+
* ```ts
|
|
4684
|
+
* const count = await sdk.purgeDeletedCards()
|
|
4685
|
+
* console.log(`Permanently deleted ${count} cards`)
|
|
4686
|
+
* ```
|
|
4687
|
+
*/
|
|
4688
|
+
async purgeDeletedCards(boardId) {
|
|
4689
|
+
const cards = await this.listCards(void 0, boardId);
|
|
4690
|
+
const deleted = cards.filter((c) => c.status === DELETED_STATUS_ID);
|
|
4691
|
+
for (const card of deleted) {
|
|
4692
|
+
await this.permanentlyDeleteCard(card.id, boardId);
|
|
4693
|
+
}
|
|
4694
|
+
return deleted.length;
|
|
4695
|
+
}
|
|
4663
4696
|
/**
|
|
4664
4697
|
* Reorders the columns of a board.
|
|
4665
4698
|
*
|
|
@@ -484,20 +484,17 @@ export declare class KanbanSDK {
|
|
|
484
484
|
*/
|
|
485
485
|
setLabel(name: string, definition: LabelDefinition): void;
|
|
486
486
|
/**
|
|
487
|
-
* Removes a label definition from the workspace configuration
|
|
488
|
-
*
|
|
489
|
-
* This only removes the color/group definition — cards that use this
|
|
490
|
-
* label keep their label strings. Those labels will render with default
|
|
491
|
-
* gray styling in the UI.
|
|
487
|
+
* Removes a label definition from the workspace configuration and cascades
|
|
488
|
+
* the deletion to all cards by removing the label from their `labels` array.
|
|
492
489
|
*
|
|
493
490
|
* @param name - The label name to remove.
|
|
494
491
|
*
|
|
495
492
|
* @example
|
|
496
493
|
* ```ts
|
|
497
|
-
* sdk.deleteLabel('bug')
|
|
494
|
+
* await sdk.deleteLabel('bug')
|
|
498
495
|
* ```
|
|
499
496
|
*/
|
|
500
|
-
deleteLabel(name: string): void
|
|
497
|
+
deleteLabel(name: string): Promise<void>;
|
|
501
498
|
/**
|
|
502
499
|
* Renames a label in the configuration and cascades the change to all cards.
|
|
503
500
|
*
|
|
@@ -754,6 +751,41 @@ export declare class KanbanSDK {
|
|
|
754
751
|
* ```
|
|
755
752
|
*/
|
|
756
753
|
removeColumn(columnId: string, boardId?: string): Promise<KanbanColumn[]>;
|
|
754
|
+
/**
|
|
755
|
+
* Moves all cards in the specified column to the `deleted` (soft-delete) column.
|
|
756
|
+
*
|
|
757
|
+
* This is a non-destructive operation — cards are moved to the reserved
|
|
758
|
+
* `deleted` status and can be restored or permanently deleted later.
|
|
759
|
+
* The column itself is not removed.
|
|
760
|
+
*
|
|
761
|
+
* @param columnId - The ID of the column whose cards should be moved to `deleted`.
|
|
762
|
+
* @param boardId - Optional board ID. Defaults to the workspace's default board.
|
|
763
|
+
* @returns A promise resolving to the number of cards that were moved.
|
|
764
|
+
* @throws {Error} If the column is `'deleted'` (no-op protection).
|
|
765
|
+
*
|
|
766
|
+
* @example
|
|
767
|
+
* ```ts
|
|
768
|
+
* const moved = await sdk.cleanupColumn('blocked')
|
|
769
|
+
* console.log(`Moved ${moved} cards to deleted`)
|
|
770
|
+
* ```
|
|
771
|
+
*/
|
|
772
|
+
cleanupColumn(columnId: string, boardId?: string): Promise<number>;
|
|
773
|
+
/**
|
|
774
|
+
* Permanently deletes all cards currently in the `deleted` column.
|
|
775
|
+
*
|
|
776
|
+
* This is equivalent to "empty trash". All soft-deleted cards are
|
|
777
|
+
* removed from disk. This operation cannot be undone.
|
|
778
|
+
*
|
|
779
|
+
* @param boardId - Optional board ID. Defaults to the workspace's default board.
|
|
780
|
+
* @returns A promise resolving to the number of cards that were permanently deleted.
|
|
781
|
+
*
|
|
782
|
+
* @example
|
|
783
|
+
* ```ts
|
|
784
|
+
* const count = await sdk.purgeDeletedCards()
|
|
785
|
+
* console.log(`Permanently deleted ${count} cards`)
|
|
786
|
+
* ```
|
|
787
|
+
*/
|
|
788
|
+
purgeDeletedCards(boardId?: string): Promise<number>;
|
|
757
789
|
/**
|
|
758
790
|
* Reorders the columns of a board.
|
|
759
791
|
*
|
|
@@ -78,6 +78,10 @@ export interface KanbanConfig {
|
|
|
78
78
|
markdownEditorMode: boolean;
|
|
79
79
|
/** Whether to show the deleted column in the UI. */
|
|
80
80
|
showDeletedColumn: boolean;
|
|
81
|
+
/** Zoom level for the board view (75–150). */
|
|
82
|
+
boardZoom: number;
|
|
83
|
+
/** Zoom level for the card detail panel (75–150). */
|
|
84
|
+
cardZoom: number;
|
|
81
85
|
/** Port number for the standalone HTTP server. */
|
|
82
86
|
port: number;
|
|
83
87
|
/** Registered webhook endpoints for event notifications. */
|
|
@@ -209,6 +209,10 @@ export interface CardDisplaySettings {
|
|
|
209
209
|
defaultPriority: Priority;
|
|
210
210
|
/** The default column/status assigned to newly created cards. */
|
|
211
211
|
defaultStatus: string;
|
|
212
|
+
/** Zoom level for the board view as a percentage (75–150). Default 100. */
|
|
213
|
+
boardZoom: number;
|
|
214
|
+
/** Zoom level for the card detail panel as a percentage (75–150). Default 100. */
|
|
215
|
+
cardZoom: number;
|
|
212
216
|
}
|
|
213
217
|
export interface LabelDefinition {
|
|
214
218
|
color: string;
|