claude-code-kanban 2.2.1-rc.1 → 2.3.2
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/package.json +1 -1
- package/public/app.js +79 -2
- package/public/index.html +1 -0
- package/public/style.css +16 -0
- package/server.js +19 -0
package/package.json
CHANGED
package/public/app.js
CHANGED
|
@@ -25,6 +25,7 @@ let agentPollInterval = null;
|
|
|
25
25
|
let selectedTaskId = null;
|
|
26
26
|
let selectedSessionId = null;
|
|
27
27
|
let focusZone = 'board'; // 'board' | 'sidebar'
|
|
28
|
+
let appConfig = { marketplaceUrl: null };
|
|
28
29
|
let selectedSessionIdx = -1;
|
|
29
30
|
let selectedSessionKbId = null;
|
|
30
31
|
let sessionJustSelected = false;
|
|
@@ -36,6 +37,7 @@ let msgUserScrolledUp = false;
|
|
|
36
37
|
const MSG_MAX_LOADED = 200;
|
|
37
38
|
let currentProjectPath = null;
|
|
38
39
|
let currentProjectSessionIds = [];
|
|
40
|
+
const dismissedSessionIds = new Set();
|
|
39
41
|
|
|
40
42
|
function resetMessageScrollState() {
|
|
41
43
|
msgUserScrolledUp = false;
|
|
@@ -1320,6 +1322,8 @@ function _renderPinToDetail(pin) {
|
|
|
1320
1322
|
}
|
|
1321
1323
|
|
|
1322
1324
|
const SESSION_PIN_SVG = PIN_SVG.replace('width="14" height="14"', 'width="12" height="12"');
|
|
1325
|
+
const MARKETPLACE_SVG =
|
|
1326
|
+
'<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 2L3 6v14a2 2 0 002 2h14a2 2 0 002-2V6l-3-4z"/><line x1="3" y1="6" x2="21" y2="6"/><path d="M16 10a4 4 0 01-8 0"/></svg>';
|
|
1323
1327
|
|
|
1324
1328
|
//#endregion
|
|
1325
1329
|
|
|
@@ -1541,8 +1545,15 @@ function formatTaskToolDetail(params) {
|
|
|
1541
1545
|
}
|
|
1542
1546
|
function getToolDetail(tool, params, detail) {
|
|
1543
1547
|
if (TASK_TOOLS.has(tool)) return formatTaskToolDetail(params);
|
|
1544
|
-
if (detail) return
|
|
1545
|
-
|
|
1548
|
+
if (!detail) return '';
|
|
1549
|
+
let extra = '';
|
|
1550
|
+
if (tool === 'Read' && params) {
|
|
1551
|
+
const parts = [];
|
|
1552
|
+
if (params.offset) parts.push(`L${params.offset}`);
|
|
1553
|
+
if (params.limit) parts.push(`+${params.limit}`);
|
|
1554
|
+
if (parts.length) extra = ` <span style="color:var(--text-muted);opacity:.7">${parts.join(' ')}</span>`;
|
|
1555
|
+
}
|
|
1556
|
+
return ` <span style="color:var(--text-muted)">${escapeHtml(detail)}</span>${extra}`;
|
|
1546
1557
|
}
|
|
1547
1558
|
function renderTaskResult(toolResult) {
|
|
1548
1559
|
if (!toolResult) return '';
|
|
@@ -2096,6 +2107,7 @@ function renderSessions() {
|
|
|
2096
2107
|
const now = Date.now();
|
|
2097
2108
|
const activeSessionIds = new Set();
|
|
2098
2109
|
filteredSessions = filteredSessions.filter((s) => {
|
|
2110
|
+
if (dismissedSessionIds.has(s.id)) return false;
|
|
2099
2111
|
const isActive =
|
|
2100
2112
|
s.hasMessages &&
|
|
2101
2113
|
((!s.sharedTaskList && (s.pending > 0 || s.inProgress > 0)) ||
|
|
@@ -2224,6 +2236,7 @@ function renderSessions() {
|
|
|
2224
2236
|
${session.hasRunningAgents ? '<span class="agent-badge" title="Active agents">🤖</span>' : ''}
|
|
2225
2237
|
${session.planSourceSessionId ? `<span class="plan-indicator" title="Implements plan — click to reveal plan session" onclick="event.stopPropagation(); revealPlanSession('${escapeHtml(session.planSourceSessionId)}')">📋</span>` : ''}
|
|
2226
2238
|
${session.hasWaitingForUser ? '<span class="agent-badge" title="Waiting for user">❓</span>' : ''}
|
|
2239
|
+
${(window.__HUB__?.enabled || appConfig.marketplaceUrl) && session.project ? `<span class="marketplace-btn" data-project-path="${escapeHtml(session.project)}" onclick="event.stopPropagation(); openMarketplace(this.dataset.projectPath)" title="Open in Marketplace">${MARKETPLACE_SVG}</span>` : ''}
|
|
2227
2240
|
${isLive ? '<span class="pulse"></span>' : ''}
|
|
2228
2241
|
</span>
|
|
2229
2242
|
<div class="progress-bar"><div class="progress-fill" style="width: ${percent}%"></div></div>
|
|
@@ -4924,6 +4937,7 @@ function showInfoModal(session, teamConfig, tasks, planContent) {
|
|
|
4924
4937
|
bodyEl.innerHTML = html;
|
|
4925
4938
|
_infoModalSessionId = session.id;
|
|
4926
4939
|
updateStickyBtnState();
|
|
4940
|
+
updateDismissBtnState();
|
|
4927
4941
|
modal.classList.add('visible');
|
|
4928
4942
|
|
|
4929
4943
|
const keyHandler = (e) => {
|
|
@@ -4941,6 +4955,25 @@ function closeTeamModal() {
|
|
|
4941
4955
|
document.getElementById('team-modal').classList.remove('visible');
|
|
4942
4956
|
}
|
|
4943
4957
|
|
|
4958
|
+
// biome-ignore lint/correctness/noUnusedVariables: used in HTML
|
|
4959
|
+
function toggleDismissSession(sessionId) {
|
|
4960
|
+
if (dismissedSessionIds.has(sessionId)) {
|
|
4961
|
+
dismissedSessionIds.delete(sessionId);
|
|
4962
|
+
} else {
|
|
4963
|
+
dismissedSessionIds.add(sessionId);
|
|
4964
|
+
}
|
|
4965
|
+
updateDismissBtnState();
|
|
4966
|
+
renderSessions();
|
|
4967
|
+
}
|
|
4968
|
+
|
|
4969
|
+
function updateDismissBtnState() {
|
|
4970
|
+
const btn = document.getElementById('session-info-dismiss-btn');
|
|
4971
|
+
if (!btn || !_infoModalSessionId) return;
|
|
4972
|
+
const isDismissed = dismissedSessionIds.has(_infoModalSessionId);
|
|
4973
|
+
btn.textContent = isDismissed ? 'Restore' : 'Dismiss';
|
|
4974
|
+
btn.title = isDismissed ? 'Restore — show in active list again' : 'Dismiss — hide from active list';
|
|
4975
|
+
}
|
|
4976
|
+
|
|
4944
4977
|
let _planSessionId = null;
|
|
4945
4978
|
|
|
4946
4979
|
//#endregion
|
|
@@ -5007,6 +5040,18 @@ function openFolderInEditor(folder, file) {
|
|
|
5007
5040
|
postAndToast('/api/open-folder', body, 'folder');
|
|
5008
5041
|
}
|
|
5009
5042
|
|
|
5043
|
+
// biome-ignore lint/correctness/noUnusedVariables: used in HTML
|
|
5044
|
+
function openMarketplace(projectPath) {
|
|
5045
|
+
const params = new URLSearchParams({ project: projectPath });
|
|
5046
|
+
if (window.__HUB__?.enabled) {
|
|
5047
|
+
hubNavigate('marketplace', `?${params}`);
|
|
5048
|
+
} else if (appConfig.marketplaceUrl) {
|
|
5049
|
+
const url = new URL(appConfig.marketplaceUrl);
|
|
5050
|
+
url.search = params.toString();
|
|
5051
|
+
window.open(url.toString(), '_blank');
|
|
5052
|
+
}
|
|
5053
|
+
}
|
|
5054
|
+
|
|
5010
5055
|
//#endregion
|
|
5011
5056
|
|
|
5012
5057
|
//#region OWNER_FILTER
|
|
@@ -5185,6 +5230,13 @@ if (urlState.search) {
|
|
|
5185
5230
|
document.getElementById('search-clear-btn').classList.add('visible');
|
|
5186
5231
|
}
|
|
5187
5232
|
|
|
5233
|
+
fetch('/api/config')
|
|
5234
|
+
.then((r) => r.json())
|
|
5235
|
+
.then((c) => {
|
|
5236
|
+
appConfig = c;
|
|
5237
|
+
})
|
|
5238
|
+
.catch(() => {});
|
|
5239
|
+
|
|
5188
5240
|
fetchSessions().then(async () => {
|
|
5189
5241
|
if (urlState.projectView) {
|
|
5190
5242
|
try {
|
|
@@ -5225,3 +5277,28 @@ window.addEventListener('popstate', () => {
|
|
|
5225
5277
|
if (s.messages !== messagePanelOpen) toggleMessagePanel();
|
|
5226
5278
|
});
|
|
5227
5279
|
//#endregion
|
|
5280
|
+
|
|
5281
|
+
// #region HUB_INTEGRATION
|
|
5282
|
+
(async function initHub() {
|
|
5283
|
+
const cfg = await fetch('/hub-config')
|
|
5284
|
+
.then((r) => r.json())
|
|
5285
|
+
.catch(() => ({}));
|
|
5286
|
+
if (!cfg.enabled) return;
|
|
5287
|
+
window.__HUB__ = cfg;
|
|
5288
|
+
document.addEventListener('keydown', (e) => {
|
|
5289
|
+
if (e.ctrlKey && e.altKey && (e.key === 'ArrowLeft' || e.key === 'ArrowRight')) {
|
|
5290
|
+
e.preventDefault();
|
|
5291
|
+
window.parent?.postMessage({ type: 'hub:keydown', key: e.key }, '*');
|
|
5292
|
+
}
|
|
5293
|
+
if (e.altKey && !e.ctrlKey && !e.shiftKey && !e.metaKey && /^[1-9]$/.test(e.key)) {
|
|
5294
|
+
e.preventDefault();
|
|
5295
|
+
window.parent?.postMessage({ type: 'hub:keydown', key: e.key }, '*');
|
|
5296
|
+
}
|
|
5297
|
+
});
|
|
5298
|
+
})();
|
|
5299
|
+
|
|
5300
|
+
window.hubNavigate = function hubNavigate(app, url) {
|
|
5301
|
+
if (!window.__HUB__?.enabled) return;
|
|
5302
|
+
window.parent?.postMessage({ type: 'hub:navigate', app, url }, '*');
|
|
5303
|
+
};
|
|
5304
|
+
// #endregion HUB_INTEGRATION
|
package/public/index.html
CHANGED
|
@@ -496,6 +496,7 @@
|
|
|
496
496
|
</div>
|
|
497
497
|
<div id="team-modal-body" class="modal-body"></div>
|
|
498
498
|
<div class="modal-footer">
|
|
499
|
+
<button id="session-info-dismiss-btn" class="btn btn-secondary" onclick="toggleDismissSession(_infoModalSessionId)">Dismiss</button>
|
|
499
500
|
<button class="btn btn-primary" onclick="closeTeamModal()">Close</button>
|
|
500
501
|
</div>
|
|
501
502
|
</div>
|
package/public/style.css
CHANGED
|
@@ -3330,6 +3330,22 @@ pre.mermaid svg {
|
|
|
3330
3330
|
color: var(--accent);
|
|
3331
3331
|
}
|
|
3332
3332
|
|
|
3333
|
+
.marketplace-btn {
|
|
3334
|
+
color: #888;
|
|
3335
|
+
cursor: pointer;
|
|
3336
|
+
display: inline-flex;
|
|
3337
|
+
align-items: center;
|
|
3338
|
+
transition:
|
|
3339
|
+
color 0.15s,
|
|
3340
|
+
filter 0.15s;
|
|
3341
|
+
border-radius: 3px;
|
|
3342
|
+
}
|
|
3343
|
+
|
|
3344
|
+
.marketplace-btn:hover {
|
|
3345
|
+
color: var(--accent);
|
|
3346
|
+
filter: drop-shadow(0 0 3px var(--accent));
|
|
3347
|
+
}
|
|
3348
|
+
|
|
3333
3349
|
.project-group-header .group-count {
|
|
3334
3350
|
font-weight: 400;
|
|
3335
3351
|
color: var(--text-muted);
|
package/server.js
CHANGED
|
@@ -48,6 +48,17 @@ function getClaudeDir() {
|
|
|
48
48
|
return process.env.CLAUDE_DIR || path.join(os.homedir(), '.claude');
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
function getMarketplaceUrl() {
|
|
52
|
+
const idx = process.argv.findIndex(arg => arg.startsWith('--marketplace-url'));
|
|
53
|
+
if (idx !== -1) {
|
|
54
|
+
const arg = process.argv[idx];
|
|
55
|
+
if (arg.includes('=')) return arg.split('=').slice(1).join('=');
|
|
56
|
+
if (process.argv[idx + 1]) return process.argv[idx + 1];
|
|
57
|
+
}
|
|
58
|
+
return process.env.MARKETPLACE_URL || null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const MARKETPLACE_URL = getMarketplaceUrl();
|
|
51
62
|
const CLAUDE_DIR = getClaudeDir();
|
|
52
63
|
const TASKS_DIR = path.join(CLAUDE_DIR, 'tasks');
|
|
53
64
|
const PROJECTS_DIR = path.join(CLAUDE_DIR, 'projects');
|
|
@@ -181,6 +192,10 @@ app.param('taskId', (req, res, next, val) => {
|
|
|
181
192
|
// Parse JSON bodies
|
|
182
193
|
app.use(express.json());
|
|
183
194
|
|
|
195
|
+
app.get('/hub-config', (_req, res) => {
|
|
196
|
+
res.json({ enabled: !!process.env.CLAUDE_HUB, url: process.env.HUB_URL || null });
|
|
197
|
+
});
|
|
198
|
+
|
|
184
199
|
// Serve static files
|
|
185
200
|
app.get('/sw.js', (req, res) => {
|
|
186
201
|
res.setHeader('Cache-Control', 'no-cache');
|
|
@@ -1251,6 +1266,10 @@ app.get('/api/version', (req, res) => {
|
|
|
1251
1266
|
res.json({ version: pkg.version });
|
|
1252
1267
|
});
|
|
1253
1268
|
|
|
1269
|
+
app.get('/api/config', (req, res) => {
|
|
1270
|
+
res.json({ marketplaceUrl: MARKETPLACE_URL });
|
|
1271
|
+
});
|
|
1272
|
+
|
|
1254
1273
|
// API: Get all tasks across all sessions
|
|
1255
1274
|
app.get('/api/tasks/all', async (req, res) => {
|
|
1256
1275
|
try {
|