kandown 0.21.7 → 0.23.0
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/CHANGELOG.md +17 -0
- package/README.md +1 -1
- package/bin/kandown.js +80 -20
- package/dist/index.html +174 -124
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.23.0 — 2026-07-20 — "Theme Switcher & Auto Update"
|
|
4
|
+
|
|
5
|
+
- **Added**: **Animated Theme Switcher** — added a shadcn-style three-option theme switcher under `src/components/ui/theme-switcher-1.tsx` with system, light, and dark choices, lucide icons, and Motion layout animation.
|
|
6
|
+
- **Changed**: **Theme Mode UX** — replaced the Appearance settings theme dropdown and the old header two-state toggle with the new animated switcher. The component keeps Kandown's existing `auto`, `light`, and `dark` config values so existing `.kandown/kandown.json` files remain compatible.
|
|
7
|
+
- **Changed**: **Framework Integration** — adapted the provided `next-themes` component to Kandown's Vite/Zustand theme pipeline instead of adding a Next.js-only provider. Theme changes continue to persist through the existing project config and apply via the local CSS token engine.
|
|
8
|
+
- **Fixed**: **Auto-Updater Retry Blocking** — failed auto-update installs are no longer cached as a successful update check. Previously, if npm answered the registry but the install failed, Kandown could suppress retries for 24 hours, making auto-update feel blocked across open projects.
|
|
9
|
+
- **Fixed**: **Multi-Project Auto-Update Refresh** — after a successful in-place global update, Kandown now scans active local daemons and refreshes `kandown.html` for every open project, so two simultaneously open boards do not remain stuck on different web UI bundles.
|
|
10
|
+
- **Fixed**: **Installed Version Verification** — the auto-updater now verifies the installed Kandown CLI version instead of only checking the npm registry version, making successful restart decisions based on the actual local binary.
|
|
11
|
+
|
|
12
|
+
## 0.22.0 — 2026-07-20 — "Sectioned List View"
|
|
13
|
+
|
|
14
|
+
- **Added**: **Sectioned List View** — rebuilt the web List view so board columns such as Backlog, Todo, In Progress, Review, and Done are displayed as horizontal task sections stacked vertically. This keeps the dense list/table workflow while making the List view match the mental model of the Kanban board in the opposite orientation.
|
|
15
|
+
- **Added**: **Vertical Section Drag-and-Drop** — added gripper-based drag-and-drop for List view sections. Reordering sections persists through the existing `reorderColumns` store action, so the same column order is shared by Board view, List view, config, and future reloads.
|
|
16
|
+
- **Added**: **Task Drag-and-Drop Between List Sections** — task rows in List view can now be dragged between sections to change their status. Drops reuse the existing `moveTask` persistence path, including dependency gates, optimistic updates, rollback behavior, and markdown frontmatter writes.
|
|
17
|
+
- **Changed**: **List View Layout** — replaced the previous single flat table with per-column section headers, task counts, repeated metadata headers, section drop guides, and empty-section drop targets. Search, filters, due-date summaries, metadata columns, drawer opening, and search-preview snippets continue to work inside the new sectioned layout.
|
|
18
|
+
- **Fixed**: **Scripted CLI Commands Exit Cleanly** — one-shot commands such as `kandown doctor` and `kandown list --json` now terminate explicitly after their handler finishes. This prevents passive bundled dependency handles from keeping scripted commands alive after successful output, preserving reliable CI and agent pipeline behavior.
|
|
19
|
+
|
|
3
20
|
## 0.21.7 — 2026-07-20 — "Web UI Vector Logo & CLI Changelog Fix"
|
|
4
21
|
|
|
5
22
|
- **Added**: **Vector Brand Logo Component (`LogoSvg.tsx`)** — unified header and empty state UI components to render the official multi-color brand vector logo from `logo.svg`.
|
package/README.md
CHANGED
|
@@ -169,7 +169,7 @@ Interactive runs of `kandown` check npm for updates at most once every 24 hours
|
|
|
169
169
|
| Feature | Description |
|
|
170
170
|
|---|---|
|
|
171
171
|
| Board view | Horizontal kanban with drag-and-drop |
|
|
172
|
-
| List view |
|
|
172
|
+
| List view | Sectioned vertical list with filters, search, and drag-and-drop between statuses |
|
|
173
173
|
| Content search | Search titles, body, subtasks, tags, assignee, priority |
|
|
174
174
|
| Command palette | `⌘K` / `Ctrl+K` for quick actions |
|
|
175
175
|
| Custom columns | Add, rename, delete columns freely |
|
package/bin/kandown.js
CHANGED
|
@@ -145,6 +145,32 @@ function resolveKandownBin() {
|
|
|
145
145
|
return null;
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
+
async function readInstalledKandownVersion(targetVersion) {
|
|
149
|
+
const localVersion = getCurrentVersion();
|
|
150
|
+
if (localVersion && semverGt(localVersion, targetVersion) >= 0) return localVersion;
|
|
151
|
+
|
|
152
|
+
const bin = resolveKandownBin();
|
|
153
|
+
if (!bin) return localVersion;
|
|
154
|
+
|
|
155
|
+
return await new Promise((resolveVersion) => {
|
|
156
|
+
const child = spawn(bin, ['--version'], {
|
|
157
|
+
timeout: 5000,
|
|
158
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
159
|
+
env: { ...process.env, KANDOWN_NO_UPDATE: '1' },
|
|
160
|
+
detached: false,
|
|
161
|
+
});
|
|
162
|
+
let stdout = '';
|
|
163
|
+
child.stdout.on('data', (d) => { stdout += d; });
|
|
164
|
+
child.stderr.on('data', () => {});
|
|
165
|
+
child.on('error', () => resolveVersion(localVersion));
|
|
166
|
+
child.on('close', (code) => {
|
|
167
|
+
if (code !== 0) return resolveVersion(localVersion);
|
|
168
|
+
const match = stdout.trim().match(/v?(\d+\.\d+\.\d+(?:-[\w.-]+)?)/);
|
|
169
|
+
resolveVersion(match ? match[1] : localVersion);
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
148
174
|
/**
|
|
149
175
|
* 📖 Compares two semver strings (major.minor.patch, optional -prerelease).
|
|
150
176
|
* Prerelease-safe: "0.18.0-beta.1" no longer parses as NaN — the numeric
|
|
@@ -260,11 +286,15 @@ async function checkForUpdate(argv = process.argv) {
|
|
|
260
286
|
});
|
|
261
287
|
});
|
|
262
288
|
|
|
263
|
-
|
|
264
|
-
// Offline / registry-down does NOT — we retry on the next interactive run.
|
|
265
|
-
if (latest) rememberUpdateCheck();
|
|
289
|
+
if (!latest) return; // offline / registry-down — retry on the next interactive run
|
|
266
290
|
|
|
267
|
-
if (
|
|
291
|
+
if (semverGt(current, latest) >= 0) {
|
|
292
|
+
// 📖 Up-to-date registry answers can be throttled. Failed update attempts
|
|
293
|
+
// below are intentionally NOT cached, otherwise one broken install would
|
|
294
|
+
// silence auto-update retries for 24h across every open project.
|
|
295
|
+
rememberUpdateCheck();
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
268
298
|
|
|
269
299
|
tuiDone('⚡', `Update available: ${c.dim}kandown ${current}${c.reset} → ${c.green}${latest}${c.reset}`);
|
|
270
300
|
|
|
@@ -305,22 +335,9 @@ async function checkForUpdate(argv = process.argv) {
|
|
|
305
335
|
return;
|
|
306
336
|
}
|
|
307
337
|
|
|
308
|
-
// 📖 Step 4: Verify the
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
timeout: 5000,
|
|
312
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
313
|
-
detached: false,
|
|
314
|
-
});
|
|
315
|
-
let stdout = '';
|
|
316
|
-
child.stdout.on('data', (d) => { stdout += d; });
|
|
317
|
-
child.stderr.on('data', () => {});
|
|
318
|
-
child.on('error', () => resolve(null));
|
|
319
|
-
child.on('close', (code) => {
|
|
320
|
-
if (code !== 0) return resolve(null);
|
|
321
|
-
resolve(stdout.trim().replace(/^"|"$/g, '') || null);
|
|
322
|
-
});
|
|
323
|
-
});
|
|
338
|
+
// 📖 Step 4: Verify the installed CLI, not just the npm registry, because
|
|
339
|
+
// `npm view kandown version` only proves a version exists remotely.
|
|
340
|
+
const postVersion = await readInstalledKandownVersion(latest);
|
|
324
341
|
|
|
325
342
|
if (!postVersion || semverGt(postVersion, latest) < 0) {
|
|
326
343
|
tuiDone('✗', `${c.yellow}Update did not apply${c.reset} — continuing with current version`);
|
|
@@ -329,6 +346,18 @@ async function checkForUpdate(argv = process.argv) {
|
|
|
329
346
|
return;
|
|
330
347
|
}
|
|
331
348
|
|
|
349
|
+
rememberUpdateCheck();
|
|
350
|
+
|
|
351
|
+
// 📖 If the running package directory has been replaced in-place (normal
|
|
352
|
+
// global install), refresh every currently open project daemon, not only the
|
|
353
|
+
// project that triggered the update. This keeps two simultaneously open
|
|
354
|
+
// Kandown boards from getting stuck on different kandown.html versions.
|
|
355
|
+
const localVersionAfterInstall = getCurrentVersion();
|
|
356
|
+
if (localVersionAfterInstall && semverGt(localVersionAfterInstall, latest) >= 0) {
|
|
357
|
+
const refreshed = await refreshRunningProjectHtml();
|
|
358
|
+
if (refreshed > 0) info(`Refreshed ${refreshed} open project${refreshed === 1 ? '' : 's'}`);
|
|
359
|
+
}
|
|
360
|
+
|
|
332
361
|
tuiDone('✓', `${c.green}Updated to v${postVersion}${c.reset} — restarting…`);
|
|
333
362
|
printVersionChangelog(postVersion);
|
|
334
363
|
printBreakingChangeNotices(current, postVersion);
|
|
@@ -1785,6 +1814,28 @@ function refreshKandownHtml(kandownDir) {
|
|
|
1785
1814
|
return false;
|
|
1786
1815
|
}
|
|
1787
1816
|
|
|
1817
|
+
async function refreshRunningProjectHtml() {
|
|
1818
|
+
const ports = [];
|
|
1819
|
+
for (let port = START_PORT_RANGE; port <= END_PORT_RANGE; port++) {
|
|
1820
|
+
if (!isBrowserUnsafePort(port)) ports.push(port);
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1823
|
+
const daemons = await Promise.all(ports.map(port => fetchDaemonInfo(port)));
|
|
1824
|
+
const kandownDirs = new Set(
|
|
1825
|
+
daemons
|
|
1826
|
+
.map(daemon => daemon?.kandownDir)
|
|
1827
|
+
.filter(dir => typeof dir === 'string' && dir.length > 0)
|
|
1828
|
+
);
|
|
1829
|
+
|
|
1830
|
+
let refreshed = 0;
|
|
1831
|
+
for (const kandownDir of kandownDirs) {
|
|
1832
|
+
try {
|
|
1833
|
+
if (refreshKandownHtml(kandownDir)) refreshed++;
|
|
1834
|
+
} catch { /* best-effort: one locked project must not block restart */ }
|
|
1835
|
+
}
|
|
1836
|
+
return refreshed;
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1788
1839
|
async function waitForDaemon(kandownDir, timeoutMs = 8000) {
|
|
1789
1840
|
// 📖 Detect daemon startup via TCP probe + process liveness rather than HTTP
|
|
1790
1841
|
// fetch. The freshly spawned child is known to be ours, so once its process
|
|
@@ -3228,3 +3279,12 @@ switch (cmd) {
|
|
|
3228
3279
|
help();
|
|
3229
3280
|
process.exit(1);
|
|
3230
3281
|
}
|
|
3282
|
+
|
|
3283
|
+
// 📖 Some bundled/imported dependencies can leave passive handles alive even
|
|
3284
|
+
// after a one-shot CLI command has finished its synchronous work. Scripted
|
|
3285
|
+
// commands must be pipeline-safe, so terminate explicitly once their handler
|
|
3286
|
+
// returns. The daemon runner, MCP server, and interactive TUI paths are the
|
|
3287
|
+
// long-lived exceptions.
|
|
3288
|
+
if (SCRIPTED_COMMANDS.has(cmd) && !(cmd === 'daemon' && rest[0] === 'run')) {
|
|
3289
|
+
process.exit(0);
|
|
3290
|
+
}
|