buddy-workbench 0.1.28 → 0.1.30
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 +10 -1
- package/server/routes/data-backup.js +23 -1
- package/server/routes/launchers.js +13 -0
- package/server/services/browser.js +39 -0
- package/ui/dist/assets/index-D-d3VqIY.js +534 -0
- package/ui/dist/assets/index-D15KPzAZ.css +1 -0
- package/ui/dist/index.html +2 -2
- package/ui/dist/assets/index-BdUCs_ZS.css +0 -1
- package/ui/dist/assets/index-CvmdTKfk.js +0 -530
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "buddy-workbench",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.30",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -24,6 +24,15 @@
|
|
|
24
24
|
"version": "node -e \"const v=process.env.npm_package_version; const fs=require('fs'); ['ui/package.json', 'ui/package-lock.json'].forEach(p=>{if(fs.existsSync(p)){const j=JSON.parse(fs.readFileSync(p)); j.version=v; if(j.packages&&j.packages['']){j.packages[''].version=v;} fs.writeFileSync(p, JSON.stringify(j,null,2)+'\\n');}});\" && git add ui/package.json ui/package-lock.json"
|
|
25
25
|
},
|
|
26
26
|
"devbuddyChangelog": [
|
|
27
|
+
{
|
|
28
|
+
"version": "0.1.28",
|
|
29
|
+
"name": "Presentation planning and presenter tools",
|
|
30
|
+
"notes": [
|
|
31
|
+
"Added a Presentations library and Presentation Studio for planning, timing, and delivering presentations.",
|
|
32
|
+
"Added a configurable audience view with Markdown takeaways, themes, time boxes, branding, and full-screen support.",
|
|
33
|
+
"Added step-linked quick captures and Markdown export with clipboard copy and .md downloads."
|
|
34
|
+
]
|
|
35
|
+
},
|
|
27
36
|
{
|
|
28
37
|
"version": "0.1.27",
|
|
29
38
|
"name": "Data backups and settings refinements",
|
|
@@ -41,9 +41,31 @@ async function collectFiles(directory, relativeDirectory = '') {
|
|
|
41
41
|
|
|
42
42
|
async function dataDirectoryStats() {
|
|
43
43
|
const files = await collectFiles(paths.dataDir);
|
|
44
|
+
const groups = new Map();
|
|
45
|
+
|
|
46
|
+
for (const file of files) {
|
|
47
|
+
const [topLevelName] = file.relativePath.split('/');
|
|
48
|
+
const isRootFile = !file.relativePath.includes('/');
|
|
49
|
+
const key = isRootFile ? '.' : topLevelName;
|
|
50
|
+
const current = groups.get(key) || {
|
|
51
|
+
name: isRootFile ? 'Root files' : topLevelName,
|
|
52
|
+
path: key,
|
|
53
|
+
sizeBytes: 0,
|
|
54
|
+
fileCount: 0
|
|
55
|
+
};
|
|
56
|
+
current.sizeBytes += file.size;
|
|
57
|
+
current.fileCount += 1;
|
|
58
|
+
groups.set(key, current);
|
|
59
|
+
}
|
|
60
|
+
|
|
44
61
|
return {
|
|
45
62
|
sizeBytes: files.reduce((total, file) => total + file.size, 0),
|
|
46
|
-
fileCount: files.length
|
|
63
|
+
fileCount: files.length,
|
|
64
|
+
breakdown: [...groups.values()].sort((a, b) => b.sizeBytes - a.sizeBytes || a.name.localeCompare(b.name)),
|
|
65
|
+
largestFiles: [...files]
|
|
66
|
+
.sort((a, b) => b.size - a.size || a.relativePath.localeCompare(b.relativePath))
|
|
67
|
+
.slice(0, 8)
|
|
68
|
+
.map(({ relativePath, size }) => ({ path: relativePath, sizeBytes: size }))
|
|
47
69
|
};
|
|
48
70
|
}
|
|
49
71
|
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { existsSync, statSync } from 'node:fs';
|
|
1
2
|
import { Router } from 'express';
|
|
2
3
|
import { listLaunchers, saveLaunchers } from '../repositories/launchers.js';
|
|
3
4
|
import { clearScriptLogs, runScript, runningErrorCounts, runningScripts, scriptErrorLogs, scriptLogs, stopScript } from '../services/process-manager.js';
|
|
4
5
|
import { readPackageScripts } from '../services/package-scripts.js';
|
|
5
6
|
import { currentGitBranch, getGitRemoteUrl, toWebRepoUrl } from '../services/git.js';
|
|
7
|
+
import { openInSystemTerminal } from '../services/browser.js';
|
|
6
8
|
|
|
7
9
|
const router = Router();
|
|
8
10
|
function validLauncher(body) {
|
|
@@ -35,6 +37,17 @@ router.post('/', (req, res) => { const config = validLauncher(req.body); if (!co
|
|
|
35
37
|
router.put('/:id', (req, res) => { const config = validLauncher(req.body); if (!config) return invalid(res); const launchers = listLaunchers(); const index = launchers.findIndex((item) => item.id === req.params.id); if (index === -1) return res.status(404).json({ error: 'Configuration not found.' }); const launcher = { id: req.params.id, ...config }; launchers[index] = launcher; saveLaunchers(launchers); res.json(launcher); });
|
|
36
38
|
router.delete('/:id', (req, res) => { const launchers = listLaunchers(); const next = launchers.filter((item) => item.id !== req.params.id); if (next.length === launchers.length) return res.status(404).json({ error: 'Configuration not found.' }); saveLaunchers(next); res.status(204).end(); });
|
|
37
39
|
router.get('/running', (_req, res) => res.json({ running: runningScripts(), errorCounts: runningErrorCounts() }));
|
|
40
|
+
router.post('/:id/open-terminal', async (req, res) => {
|
|
41
|
+
const launcher = listLaunchers().find((item) => item.id === req.params.id);
|
|
42
|
+
if (!launcher) return res.status(404).json({ error: 'Configuration not found.' });
|
|
43
|
+
try {
|
|
44
|
+
if (!existsSync(launcher.folder) || !statSync(launcher.folder).isDirectory()) return res.status(400).json({ error: 'The configured project folder does not exist.' });
|
|
45
|
+
await openInSystemTerminal(launcher.folder);
|
|
46
|
+
res.json({ ok: true });
|
|
47
|
+
} catch (error) {
|
|
48
|
+
res.status(500).json({ error: error.message || 'Unable to open the terminal.' });
|
|
49
|
+
}
|
|
50
|
+
});
|
|
38
51
|
router.post('/:id/stop', (req, res) => { try { stopScript(req.params.id, req.body?.scriptId); res.json({ ok: true }); } catch (error) { res.status(404).json({ error: error.message }); } });
|
|
39
52
|
router.post('/:id/start', (req, res) => {
|
|
40
53
|
const launcher = listLaunchers().find((item) => item.id === req.params.id);
|
|
@@ -108,3 +108,42 @@ export function openInSystemFolder(targetPath) {
|
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
function execFileAsync(command, args) {
|
|
112
|
+
return new Promise((resolve, reject) => {
|
|
113
|
+
execFile(command, args, (error) => {
|
|
114
|
+
if (error) reject(error);
|
|
115
|
+
else resolve();
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export async function openInSystemTerminal(targetPath) {
|
|
121
|
+
if (!targetPath || typeof targetPath !== 'string') throw new Error('A project folder is required.');
|
|
122
|
+
|
|
123
|
+
if (process.platform === 'darwin') {
|
|
124
|
+
await execFileAsync('open', ['-a', 'Terminal', targetPath]);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (process.platform === 'win32') {
|
|
129
|
+
await execFileAsync('powershell.exe', ['-NoProfile', '-Command', 'Start-Process', 'cmd.exe', '-WorkingDirectory', targetPath]);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const terminalCandidates = [
|
|
134
|
+
['x-terminal-emulator', ['--working-directory', targetPath]],
|
|
135
|
+
['gnome-terminal', ['--working-directory', targetPath]],
|
|
136
|
+
['konsole', ['--workdir', targetPath]],
|
|
137
|
+
['xfce4-terminal', ['--working-directory', targetPath]]
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
for (const [command, args] of terminalCandidates) {
|
|
141
|
+
try {
|
|
142
|
+
await execFileAsync(command, args);
|
|
143
|
+
return;
|
|
144
|
+
} catch {
|
|
145
|
+
// Try the next common terminal emulator.
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
throw new Error('No supported terminal application was found.');
|
|
149
|
+
}
|