claude-code-hub 0.36.0 → 0.37.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/package.json +4 -4
- package/public/app.js +23 -0
- package/server.js +6 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-hub",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.37.0",
|
|
4
4
|
"description": "Unified hub for Claude Code tools — Marketplace + Kanban in one PWA",
|
|
5
5
|
"main": "server.js",
|
|
6
6
|
"bin": {
|
|
@@ -37,9 +37,9 @@
|
|
|
37
37
|
},
|
|
38
38
|
"homepage": "https://github.com/NikiforovAll/claude-code-hub#readme",
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"claude-code-cost": "^0.
|
|
41
|
-
"claude-code-kanban": "^4.
|
|
42
|
-
"claude-code-marketplace": "^0.
|
|
40
|
+
"claude-code-cost": "^0.23.0",
|
|
41
|
+
"claude-code-kanban": "^4.26.0",
|
|
42
|
+
"claude-code-marketplace": "^0.18.0",
|
|
43
43
|
"claude-code-memory-explorer": "^0.14.0",
|
|
44
44
|
"express": "^4.21.0",
|
|
45
45
|
"open": "^10.1.0"
|
package/public/app.js
CHANGED
|
@@ -10,6 +10,8 @@ const themeState = loadThemeState();
|
|
|
10
10
|
// hub-theme: starting unset means there is nothing to race against each sub-app's own
|
|
11
11
|
// project self-restore on boot. Once set it never returns to null.
|
|
12
12
|
let projectState = null;
|
|
13
|
+
// The config dir the hub falls back to (~/.claude). Stays out of the window title.
|
|
14
|
+
let defaultConfigDir = null;
|
|
13
15
|
// mode: 'project' (Ctrl+Alt+P) or 'configDir' (Ctrl+Alt+W). One widget, two row sources.
|
|
14
16
|
const palette = {
|
|
15
17
|
open: false,
|
|
@@ -120,6 +122,8 @@ async function init() {
|
|
|
120
122
|
const res = await fetch('/api/config');
|
|
121
123
|
const config = await res.json();
|
|
122
124
|
setApps(config.apps);
|
|
125
|
+
defaultConfigDir = config.defaultConfigDir ?? null;
|
|
126
|
+
applyTitle(config.activeConfigDir);
|
|
123
127
|
themeAccents = config.themeAccents ?? {};
|
|
124
128
|
applyHubTheme();
|
|
125
129
|
buildIframes();
|
|
@@ -130,6 +134,13 @@ async function init() {
|
|
|
130
134
|
registerSW();
|
|
131
135
|
}
|
|
132
136
|
|
|
137
|
+
// The hub has no visible chrome, so the window/tab title is the only place the active config dir
|
|
138
|
+
// shows. Only a non-default dir is named, and by basename alone — the PWA window prepends the
|
|
139
|
+
// manifest name, so a product name here would read as "Claude Code Hub - .claude-eom · Hub".
|
|
140
|
+
function applyTitle(dir) {
|
|
141
|
+
document.title = dir && dir !== defaultConfigDir ? basename(dir) : 'Claude Code Hub';
|
|
142
|
+
}
|
|
143
|
+
|
|
133
144
|
function setApps(next) {
|
|
134
145
|
apps = next;
|
|
135
146
|
allowedOrigins = new Set(Object.values(apps).map((a) => new URL(a.url).origin));
|
|
@@ -210,6 +221,17 @@ function listenMessages() {
|
|
|
210
221
|
if (data.url) iframes[data.app].src = apps[data.app].url + data.url;
|
|
211
222
|
} else if (data.type === 'hub:keydown') {
|
|
212
223
|
handleForwardedKey(data);
|
|
224
|
+
} else if (data.type === 'hub:openExternal') {
|
|
225
|
+
// In the installed PWA window a framed sub-app's own target=_blank opens
|
|
226
|
+
// nothing, so the shims hand external links up to the top frame instead.
|
|
227
|
+
let url;
|
|
228
|
+
try {
|
|
229
|
+
url = new URL(String(data.url));
|
|
230
|
+
} catch (_) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') return;
|
|
234
|
+
window.open(url.href, '_blank', 'noopener');
|
|
213
235
|
} else if (data.type === 'hub:theme') {
|
|
214
236
|
// Legacy senders pass only {theme}; colorTheme is optional and sticky.
|
|
215
237
|
if (data.theme !== 'light' && data.theme !== 'dark') return;
|
|
@@ -622,6 +644,7 @@ async function commitConfigDir(row) {
|
|
|
622
644
|
setApps((await sendJson('POST', '/api/config-dirs/activate', { path: target })).apps);
|
|
623
645
|
// The palette renders from this cache before its refetch lands, and initialSel keys off it.
|
|
624
646
|
palette.configDirs.active = target;
|
|
647
|
+
applyTitle(target);
|
|
625
648
|
reloadIframes();
|
|
626
649
|
} catch (err) {
|
|
627
650
|
console.warn('config dir switch failed:', err.message);
|
package/server.js
CHANGED
|
@@ -38,6 +38,10 @@ function activePool() {
|
|
|
38
38
|
// be known before the first spawn, and the hub already has one localStorage key to reason about.
|
|
39
39
|
const HUB_CONFIG_FILE = path.join(os.homedir(), '.claude-hub', 'config.json');
|
|
40
40
|
|
|
41
|
+
// The dir the hub would use with no saved config. Set by loadHubConfig; the client uses it to keep
|
|
42
|
+
// the default dir out of the window title.
|
|
43
|
+
let defaultConfigDir = null;
|
|
44
|
+
|
|
41
45
|
function expandHome(p) {
|
|
42
46
|
return p.startsWith('~') ? p.replace('~', os.homedir()) : p;
|
|
43
47
|
}
|
|
@@ -59,6 +63,7 @@ function loadHubConfig() {
|
|
|
59
63
|
const fallback = canonical(
|
|
60
64
|
process.env.CLAUDE_CONFIG_DIR || process.env.CLAUDE_DIR || path.join(os.homedir(), '.claude'),
|
|
61
65
|
);
|
|
66
|
+
defaultConfigDir = fallback;
|
|
62
67
|
let raw = '';
|
|
63
68
|
let saved = {};
|
|
64
69
|
try {
|
|
@@ -346,7 +351,7 @@ function appsConfig() {
|
|
|
346
351
|
}
|
|
347
352
|
|
|
348
353
|
app.get('/api/config', (_req, res) => {
|
|
349
|
-
res.json({ themeAccents, apps: appsConfig() });
|
|
354
|
+
res.json({ themeAccents, apps: appsConfig(), activeConfigDir: hubConfig.activeConfigDir, defaultConfigDir });
|
|
350
355
|
});
|
|
351
356
|
|
|
352
357
|
app.get('/api/config-dirs', (_req, res) => {
|