kandown 0.28.1 → 0.30.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 +27 -0
- package/README.md +1 -0
- package/bin/kandown.js +63 -20
- package/dist/index.html +170 -150
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.30.0 — 2026-07-21 — "Fable UI Themes"
|
|
4
|
+
|
|
5
|
+
- **Added**: **Customizable JSON Theme Engine (`KandownTheme`)** — upgraded Kandown's skin system from 5 hardcoded HSL presets to an open, JSON-driven theme engine with dynamic inheritance (`base`), fallback safety, and runtime custom theme registration (`registerCustomThemes`).
|
|
6
|
+
- **Added**: **8 Curated Built-in Presets** — introduced 8 design presets in `src/lib/theme.ts`:
|
|
7
|
+
- ⚡ **Vercel**: Monochrome contrast, tight typography, 6px radius, sharp 1px borders, zero shadows.
|
|
8
|
+
- 📐 **Linear**: Dark-first (`#08090A`), electric violet (`#5E6AD2`), 8px radius, glass header, soft popover shadows.
|
|
9
|
+
- 🧡 **Claude**: Editorial warmth, oat crème (`#F5F1EA`), terracotta accent (`#D97757`), Charter serif display font, 12px radius, soft diffuse shadows.
|
|
10
|
+
- 🍎 **Apple**: Translucent materials (`rgba(255,255,255,0.72)` + backdrop blur), SF system font, 14px squircle radius, multi-layer elevated shadows.
|
|
11
|
+
- 💳 **Stripe**: Indigo night (`#0A0A23`), blurple accent (`#635BFF`), 8px radius, soft colored glow shadows.
|
|
12
|
+
- 📄 **Paper**: Notion-style calm studio, `#F7F6F3` light, `#191919` dark, pastel tags, 4px radius, no shadows.
|
|
13
|
+
- 🧛 **Catppuccin**: Official Mocha (`#1E1E2E` mauve `#CBA6F7`) and Latte palettes.
|
|
14
|
+
- 🖥️ **Terminal**: CRT homage to TUI, `#0C0C0C` background, `#33FF66` phosphor green text, SF Mono everywhere, 0px radius.
|
|
15
|
+
- **Added**: **Theme Gallery with Live Mini-Board Previews** — replaced static color swatches in Settings with an interactive gallery of `ThemePreviewCard` components rendering isolated live 3-column kanban board previews using each theme's unique HSL tokens, radius, fonts, and shadows.
|
|
16
|
+
- **Added**: **Visual Theme Editor Modal (`ThemeCustomizerModal`)** — added a visual customization drawer for creating and tweaking custom JSON themes with sliders for border radius (0–24px), shadow elevation levels (`none`, `soft`, `elevated`, `dramatic`), density, motion scale, glassmorphism, and live HSL token pickers.
|
|
17
|
+
- **Added**: **Live WCAG 2.1 Contrast Ratio Calculator** — added real-time relative luminance calculation in the theme editor showing an active WCAG AA contrast ratio badge (warning when ratio < 4.5:1).
|
|
18
|
+
- **Added**: **JSON Import & Export** — added direct theme JSON view/edit panel with 1-click clipboard copy for sharing and storing custom themes.
|
|
19
|
+
- **Added**: **`ui.customThemes` Config Support** — custom user themes are automatically persisted in `kandown.json` under `ui.customThemes` and loaded seamlessly on startup.
|
|
20
|
+
- **Changed**: **Appearance CSS Tokens & Design Refinements** — introduced `--radius`, `--shadow-*`, `--font-display`, `--motion-scale`, `--card-blur` variables into `:root` and mapped them to `tailwind.config.js` (`borderRadius`, `fontFamily.display`). Refined base body typography scale to 15px, unified focus rings (`:focus-visible`), and added smooth light/dark root transitions.
|
|
21
|
+
|
|
22
|
+
## 0.29.0 — 2026-07-20 — "Global Daemon Refresh"
|
|
23
|
+
|
|
24
|
+
- **Added**: **Global daemon refresh command** — added `kandown daemon refresh-all`, which scans every active local Kandown daemon across the configured port range, refreshes each open project's `.kandown/kandown.html`, and restarts any daemon running an older CLI version.
|
|
25
|
+
- **Added**: **Forced daemon reboot aliases** — added `kandown daemon restart-all` and `kandown daemon refresh-all --force` for cases where every open Kandown daemon should be restarted, even when versions already match.
|
|
26
|
+
- **Changed**: **Safer post-update refresh flow** — refactored the existing auto-update refresh path to reuse the same daemon scan/restart helper, keeping automatic package-update behavior aligned with the new explicit global command.
|
|
27
|
+
- **Changed**: **Daemon CLI documentation** — updated CLI help and the README command table to surface `daemon refresh-all` as the recommended fix when old project sessions remain open after a global update.
|
|
28
|
+
- **Fixed**: **Outdated open project daemons after global updates** — users no longer need to manually find and restart each project when a global Kandown update succeeds but already-running daemons continue serving older code in memory.
|
|
29
|
+
|
|
3
30
|
## 0.28.1 — 2026-07-20 — "Robust Global Auto-Updater"
|
|
4
31
|
|
|
5
32
|
- **Fixed**: **Global package manager auto-update commands** — updated `checkForUpdate()` and `cmdUpdate()` to run `pnpm add -g kandown@latest` (instead of `pnpm install -g kandown`), with fallback chain across `pnpm`, `npm`, `yarn`, and `bun`.
|
package/README.md
CHANGED
|
@@ -91,6 +91,7 @@ The web daemon stays alive after you quit the TUI so the browser keeps working.
|
|
|
91
91
|
| `kandown daemon status` | Show this project's web daemon status |
|
|
92
92
|
| `kandown daemon start` | Start/reconnect this project's web daemon |
|
|
93
93
|
| `kandown daemon stop` | Stop this project's web daemon |
|
|
94
|
+
| `kandown daemon refresh-all` | Refresh open projects and restart outdated daemons with the current CLI version |
|
|
94
95
|
| `kandown update` | Update `kandown.html` to latest |
|
|
95
96
|
| `kandown list` \| `show` \| `create` \| `move` \| `assign` \| `commit` | One-shot task commands (see below) |
|
|
96
97
|
| `kandown tasks` | Full help for the one-shot task commands |
|
package/bin/kandown.js
CHANGED
|
@@ -574,7 +574,7 @@ ${c.bold}Commands:${c.reset}
|
|
|
574
574
|
${c.cyan}board${c.reset} Open the interactive kanban board in the terminal
|
|
575
575
|
${c.cyan}init${c.reset} Initialize .kandown/ in the current directory
|
|
576
576
|
${c.cyan}settings${c.reset} Open the settings TUI
|
|
577
|
-
${c.cyan}daemon${c.reset} Manage
|
|
577
|
+
${c.cyan}daemon${c.reset} Manage web daemons (start|stop|status|refresh-all)
|
|
578
578
|
${c.cyan}update${c.reset} Update kandown.html to the latest version
|
|
579
579
|
${c.cyan}list${c.reset} List tasks ${c.dim}(-s status, -a assignee, -t tag, -p priority, --json)${c.reset}
|
|
580
580
|
${c.cyan}show${c.reset} Print a task's raw file content
|
|
@@ -594,6 +594,7 @@ ${c.bold}Examples:${c.reset}
|
|
|
594
594
|
${c.dim}$${c.reset} npx kandown --port 3000 ${c.dim}# use a specific web UI port${c.reset}
|
|
595
595
|
${c.dim}$${c.reset} npx kandown board ${c.dim}# board TUI only${c.reset}
|
|
596
596
|
${c.dim}$${c.reset} npx kandown daemon stop ${c.dim}# stop this project's web daemon${c.reset}
|
|
597
|
+
${c.dim}$${c.reset} npx kandown daemon refresh-all ${c.dim}# refresh/restart outdated open daemons${c.reset}
|
|
597
598
|
${c.dim}$${c.reset} npx kandown init
|
|
598
599
|
${c.dim}$${c.reset} npx kandown init --path docs/kanban
|
|
599
600
|
${c.dim}$${c.reset} npx kandown init --no-agents
|
|
@@ -1946,35 +1947,62 @@ function refreshKandownHtml(kandownDir) {
|
|
|
1946
1947
|
return false;
|
|
1947
1948
|
}
|
|
1948
1949
|
|
|
1949
|
-
|
|
1950
|
+
function daemonScanPorts() {
|
|
1950
1951
|
const ports = [];
|
|
1951
1952
|
for (let port = START_PORT_RANGE; port <= END_PORT_RANGE; port++) {
|
|
1952
1953
|
if (!isBrowserUnsafePort(port)) ports.push(port);
|
|
1953
1954
|
}
|
|
1955
|
+
return ports;
|
|
1956
|
+
}
|
|
1954
1957
|
|
|
1955
|
-
|
|
1956
|
-
const
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1958
|
+
async function listRunningDaemons() {
|
|
1959
|
+
const daemons = await Promise.all(daemonScanPorts().map(port => fetchDaemonInfo(port)));
|
|
1960
|
+
const byDir = new Map();
|
|
1961
|
+
for (const daemon of daemons) {
|
|
1962
|
+
if (!daemon || typeof daemon.kandownDir !== 'string' || daemon.kandownDir.length === 0) continue;
|
|
1963
|
+
if (!byDir.has(daemon.kandownDir)) byDir.set(daemon.kandownDir, daemon);
|
|
1964
|
+
}
|
|
1965
|
+
return [...byDir.values()];
|
|
1966
|
+
}
|
|
1961
1967
|
|
|
1968
|
+
async function refreshRunningDaemons({ forceRestart = false } = {}) {
|
|
1969
|
+
const daemons = await listRunningDaemons();
|
|
1962
1970
|
const currentVersion = getCurrentVersion();
|
|
1963
|
-
|
|
1964
|
-
|
|
1971
|
+
const results = {
|
|
1972
|
+
found: daemons.length,
|
|
1973
|
+
refreshed: 0,
|
|
1974
|
+
restarted: 0,
|
|
1975
|
+
skipped: 0,
|
|
1976
|
+
failed: [],
|
|
1977
|
+
};
|
|
1978
|
+
|
|
1979
|
+
for (const daemon of daemons) {
|
|
1980
|
+
const kandownDir = daemon.kandownDir;
|
|
1965
1981
|
try {
|
|
1966
|
-
if (refreshKandownHtml(kandownDir)) refreshed++;
|
|
1982
|
+
if (refreshKandownHtml(kandownDir)) results.refreshed++;
|
|
1967
1983
|
const current = await getDaemonStatus(kandownDir);
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
}
|
|
1984
|
+
const daemonVersion = daemon.version || current.metadata?.version || null;
|
|
1985
|
+
const shouldRestart = forceRestart || !daemonVersion || (currentVersion && semverGt(currentVersion, daemonVersion) > 0);
|
|
1986
|
+
if (!shouldRestart) {
|
|
1987
|
+
results.skipped++;
|
|
1988
|
+
continue;
|
|
1974
1989
|
}
|
|
1975
|
-
|
|
1990
|
+
const preferredPort = daemon.port || current.metadata?.port || null;
|
|
1991
|
+
await stopDaemon(kandownDir);
|
|
1992
|
+
const restarted = await startDaemon(kandownDir, preferredPort);
|
|
1993
|
+
if (!restarted.running) throw new Error('daemon failed to restart');
|
|
1994
|
+
results.restarted++;
|
|
1995
|
+
} catch (e) {
|
|
1996
|
+
results.failed.push({ kandownDir, error: e.message });
|
|
1997
|
+
}
|
|
1976
1998
|
}
|
|
1977
|
-
|
|
1999
|
+
|
|
2000
|
+
return results;
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
async function refreshRunningProjectHtml() {
|
|
2004
|
+
const results = await refreshRunningDaemons({ forceRestart: false });
|
|
2005
|
+
return results.refreshed;
|
|
1978
2006
|
}
|
|
1979
2007
|
|
|
1980
2008
|
async function waitForDaemon(kandownDir, timeoutMs = 8000) {
|
|
@@ -2978,6 +3006,21 @@ function detectStaleKandown(port, currentKandownDir) {
|
|
|
2978
3006
|
|
|
2979
3007
|
async function cmdDaemon(rawArgs) {
|
|
2980
3008
|
const [subcommand = 'status', ...rest] = rawArgs;
|
|
3009
|
+
|
|
3010
|
+
if (subcommand === 'refresh-all' || subcommand === 'restart-all') {
|
|
3011
|
+
const forceRestart = rest.includes('--force') || subcommand === 'restart-all';
|
|
3012
|
+
const results = await refreshRunningDaemons({ forceRestart });
|
|
3013
|
+
success(`Scanned ${results.found} running daemon${results.found === 1 ? '' : 's'}`);
|
|
3014
|
+
info(`Refreshed ${results.refreshed} kandown.html bundle${results.refreshed === 1 ? '' : 's'}`);
|
|
3015
|
+
info(`${forceRestart ? 'Restarted' : 'Restarted outdated'} ${results.restarted} daemon${results.restarted === 1 ? '' : 's'}`);
|
|
3016
|
+
if (!forceRestart && results.skipped > 0) info(`Skipped ${results.skipped} already-current daemon${results.skipped === 1 ? '' : 's'}`);
|
|
3017
|
+
if (results.failed.length > 0) {
|
|
3018
|
+
for (const failure of results.failed) warn(`${failure.kandownDir}: ${failure.error}`);
|
|
3019
|
+
process.exit(1);
|
|
3020
|
+
}
|
|
3021
|
+
return;
|
|
3022
|
+
}
|
|
3023
|
+
|
|
2981
3024
|
const { kandownDir } = ensureKandownDir(rest);
|
|
2982
3025
|
const preferredPort = parsePort(parseArgs(rest).port);
|
|
2983
3026
|
|
|
@@ -3039,7 +3082,7 @@ async function cmdDaemon(rawArgs) {
|
|
|
3039
3082
|
}
|
|
3040
3083
|
|
|
3041
3084
|
err(`Unknown daemon command: ${subcommand}`);
|
|
3042
|
-
log(` Use ${c.cyan}kandown daemon start|stop|status${c.reset}`);
|
|
3085
|
+
log(` Use ${c.cyan}kandown daemon start|stop|status|refresh-all${c.reset}`);
|
|
3043
3086
|
process.exit(1);
|
|
3044
3087
|
}
|
|
3045
3088
|
|