theamify-cli 2.1.0 → 2.2.1
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/README.md +21 -11
- package/package.json +1 -1
- package/src/cli.js +10 -2
- package/src/commands/manage.js +69 -21
- package/src/core/engine.js +18 -0
- package/src/lib/self.js +121 -0
- package/src/wizard/browse.js +8 -0
- package/vendor/theamify +1 -1
package/README.md
CHANGED
|
@@ -16,15 +16,18 @@ npm install -g theamify-cli
|
|
|
16
16
|
The `-g` flag puts the `theamify` command on your system `PATH` (or provisions a user-local runtime under `~/.local/share/theamify`), so you can call it from **any folder and any terminal**:
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
theamify # ✨ interactive wizard (
|
|
19
|
+
theamify # ✨ interactive wizard (self-check → tools → download → browse → apply)
|
|
20
20
|
theamify list # list all themes with status
|
|
21
21
|
theamify info <name> # details + terminal preview
|
|
22
22
|
theamify get <name> # download & cache a theme
|
|
23
|
+
theamify get --all # download every theme up front
|
|
23
24
|
sudo theamify use <name> # apply to GRUB + rebuild
|
|
24
25
|
theamify update # update tools (chafa, grub-customizer) + re-download themes
|
|
25
26
|
theamify status # GRUB & dependency status
|
|
26
27
|
theamify doctor # diagnose install, GRUB, tools & dependencies
|
|
27
|
-
theamify
|
|
28
|
+
theamify upgrade # check for & install the latest npm version
|
|
29
|
+
theamify repair # fix a broken install (re-provision the engine)
|
|
30
|
+
theamify uninstall # remove theamify entirely (incl. npm package)
|
|
28
31
|
```
|
|
29
32
|
|
|
30
33
|
> 💡 Upgrading is the same command — `npm install -g theamify-cli@latest`, and your downloaded themes & registry edits are preserved.
|
|
@@ -43,19 +46,22 @@ theamify uninstall # remove only theamify (keeps chafa & grub-customize
|
|
|
43
46
|
| 🩺 **`theamify doctor`** | Diagnose install, GRUB, active theme, registry, tools & dependencies |
|
|
44
47
|
| 🗑 **Safe uninstall** | Removes **only theamify** — leaves chafa & grub-customizer for later use |
|
|
45
48
|
| ⌨️ **Ctrl+C / Ctrl+Z safe** | Signal safety net; interactive `sudo` is never frozen |
|
|
49
|
+
| 🔄 **Self-manage (v2.2+)** | `upgrade`, `repair`, auto-update check on every wizard run |
|
|
46
50
|
|
|
47
51
|
---
|
|
48
52
|
|
|
49
53
|
## 🧭 Wizard Sequence (every `theamify` run)
|
|
50
54
|
|
|
51
|
-
1. **
|
|
55
|
+
1. **Self-check** — checks npm for a newer `theamify-cli`; offers to update if found. Also self-repairs the engine/runtime.
|
|
56
|
+
2. **Detect companion tools** — `chafa` then `grub-customizer`. For each:
|
|
52
57
|
- **Already installed?** → *“Update it now? (or just continue)”* — Continue moves on.
|
|
53
58
|
- **Missing?** → *“Install `<name>`? Yes/No”* → installs via your package manager (`sudo`), then continues.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
59
|
+
3. **Download all themes** — asks *“Download all themes now?”* (default Yes) → fetches the whole registry so every theme is instantly previewable/applicable.
|
|
60
|
+
4. **Show the theme picker** — every registry theme with status (`[REMOTE]`/`[CACHED]`/`[ACTIVE]`).
|
|
61
|
+
5. **Pick a theme** → the **terminal thumbnail renders automatically** below the selection (no separate “preview” menu item).
|
|
62
|
+
6. **Act** — download/update, apply to GRUB, or open in browser. Choose `← Back to theme list` to keep browsing (the wizard never hard-exits until you pick **Quit**).
|
|
57
63
|
|
|
58
|
-
> Companion tools are
|
|
64
|
+
> Companion tools and themes are ready **before** the browser, so thumbnails & applies are instant when you need them.
|
|
59
65
|
|
|
60
66
|
---
|
|
61
67
|
|
|
@@ -79,11 +85,12 @@ theamify/
|
|
|
79
85
|
├── bin/theamify.js # npm bin entry → src/cli.js
|
|
80
86
|
├── src/
|
|
81
87
|
│ ├── cli.js # command router + signal safety net
|
|
82
|
-
│ ├── core/engine.js # runtime provisioning & engine forwarding
|
|
83
|
-
│ ├── commands/manage.js # doctor / status / uninstall
|
|
84
|
-
│ ├── wizard/browse.js # interactive wizard (browse → preview → apply)
|
|
88
|
+
│ ├── core/engine.js # runtime provisioning, repair & engine forwarding
|
|
89
|
+
│ ├── commands/manage.js # doctor / status / upgrade / repair / uninstall
|
|
90
|
+
│ ├── wizard/browse.js # interactive wizard (self-check → browse → preview → apply)
|
|
85
91
|
│ └── lib/
|
|
86
92
|
│ ├── conf.js # themes.conf registry parser
|
|
93
|
+
│ ├── self.js # version-check / self-update / self-uninstall / rc cleanup
|
|
87
94
|
│ └── tools.js # companion tools: chafa + grub-customizer
|
|
88
95
|
├── vendor/ # bundled bash engine (theamify + lib/ + config/)
|
|
89
96
|
├── test/ # unit tests (node:test)
|
|
@@ -122,7 +129,10 @@ The wizard manages two companion tools as first-class:
|
|
|
122
129
|
- **Detect** → if present, offer to update; if absent, install (with your consent).
|
|
123
130
|
- **Continue** regardless of Yes/No — the wizard proceeds to the theme picker.
|
|
124
131
|
- **`theamify update`** updates tools then re-downloads themes.
|
|
125
|
-
- **`theamify uninstall`** removes **
|
|
132
|
+
- **`theamify uninstall`** removes **everything theamify owns**: the runtime + all downloaded themes, resets GRUB to the default boot menu, removes the `theamify-cli` npm package, and strips leftover PATH markers from `~/.bashrc` / `~/.zshrc`. Companion tools **chafa** and **grub-customizer** stay on the system for later use — run the command after that and you get a normal `command not found`.
|
|
133
|
+
- **`theamify upgrade`** checks the npm registry and, if a newer version exists, offers to `npm install -g theamify-cli@latest`.
|
|
134
|
+
- **`theamify repair`** re-provisions the engine from the bundled package — the fix for a broken/missing install.
|
|
135
|
+
- Every `theamify` (wizard) run also **auto-checks for updates** and **self-repairs** before showing the theme picker.
|
|
126
136
|
|
|
127
137
|
---
|
|
128
138
|
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -2,7 +2,7 @@ import { defineCommand, runMain } from 'citty';
|
|
|
2
2
|
import pc from 'picocolors';
|
|
3
3
|
import { createRequire } from 'node:module';
|
|
4
4
|
import { runThemeBrowser } from './wizard/browse.js';
|
|
5
|
-
import { runDoctor, runStatus, runUninstallWizard } from './commands/manage.js';
|
|
5
|
+
import { runDoctor, runStatus, runUninstallWizard, runSelfUpgrade, runRepair } from './commands/manage.js';
|
|
6
6
|
import { updateManagedTools } from './lib/tools.js';
|
|
7
7
|
import { runEngine } from './core/engine.js';
|
|
8
8
|
|
|
@@ -42,7 +42,9 @@ ${pc.bold('Usage:')}
|
|
|
42
42
|
theamify open <name> Open the theme source page in a browser
|
|
43
43
|
theamify status Show GRUB & dependency status
|
|
44
44
|
theamify doctor Diagnose install, GRUB & dependencies
|
|
45
|
-
theamify
|
|
45
|
+
theamify upgrade Check for & install the latest npm version
|
|
46
|
+
theamify repair Fix a broken install (re-provision the engine)
|
|
47
|
+
theamify uninstall Remove theamify entirely (incl. npm package)
|
|
46
48
|
theamify clean Clear the repo clone cache
|
|
47
49
|
`);
|
|
48
50
|
}
|
|
@@ -96,6 +98,12 @@ const main = defineCommand({
|
|
|
96
98
|
return runStatus();
|
|
97
99
|
case 'doctor':
|
|
98
100
|
return runDoctor();
|
|
101
|
+
case 'upgrade':
|
|
102
|
+
case 'self-update':
|
|
103
|
+
case 'selfupdate':
|
|
104
|
+
return runSelfUpgrade();
|
|
105
|
+
case 'repair':
|
|
106
|
+
return runRepair();
|
|
99
107
|
case 'uninstall':
|
|
100
108
|
return runUninstallWizard();
|
|
101
109
|
case 'update':
|
package/src/commands/manage.js
CHANGED
|
@@ -7,10 +7,12 @@ import {
|
|
|
7
7
|
findInstalledRuntime,
|
|
8
8
|
USER_DIR,
|
|
9
9
|
removeShadowBin,
|
|
10
|
+
repairRuntime,
|
|
10
11
|
resolveEngine,
|
|
11
12
|
} from '../core/engine.js';
|
|
12
13
|
import { parseThemes, resolveConfPath } from '../lib/conf.js';
|
|
13
14
|
import { companionToolStatus } from '../lib/tools.js';
|
|
15
|
+
import { checkForUpdate, promptSelfUpdate, selfUninstall, cleanRcMarkers } from '../lib/self.js';
|
|
14
16
|
|
|
15
17
|
/** `theamify doctor` — installation, GRUB and dependency health. */
|
|
16
18
|
export async function runDoctor() {
|
|
@@ -80,14 +82,54 @@ export async function runStatus() {
|
|
|
80
82
|
if (res.exitCode !== 0) process.exit(res.exitCode);
|
|
81
83
|
}
|
|
82
84
|
|
|
85
|
+
/** `theamify upgrade` — self-update the npm CLI if a newer version is published. */
|
|
86
|
+
export async function runSelfUpgrade() {
|
|
87
|
+
const { outdated, latest, current } = await checkForUpdate();
|
|
88
|
+
if (!outdated) {
|
|
89
|
+
console.log(pc.green(`Already on the latest version (v${current}).`));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
console.log(`Available: ${pc.cyan('v' + latest)} (you have ${pc.dim('v' + current)})`);
|
|
93
|
+
await promptSelfUpdate();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** `theamify repair` — fix a broken install by re-provisioning the engine/runtime. */
|
|
97
|
+
export async function runRepair() {
|
|
98
|
+
console.log(pc.bold('\n🔧 theamify Repair\n'));
|
|
99
|
+
const engine = await repairRuntime();
|
|
100
|
+
console.log(` Engine : ${pc.cyan(engine)}`);
|
|
101
|
+
console.log(` Runtime : ${pc.cyan(path.join(USER_DIR))}`);
|
|
102
|
+
console.log(' Status : ' + pc.green('engine re-provisioned'));
|
|
103
|
+
console.log(' Downloads: ' + (fs.existsSync(path.join(USER_DIR, 'themes')) ? pc.green('themes preserved') : pc.dim('no cached themes yet')));
|
|
104
|
+
console.log();
|
|
105
|
+
}
|
|
106
|
+
|
|
83
107
|
/**
|
|
84
|
-
* Remove
|
|
85
|
-
*
|
|
86
|
-
*
|
|
108
|
+
* Remove a theamify runtime directory and everything in it (the engine PLUS all
|
|
109
|
+
* downloaded themes under themes/ and .repo_cache/). Uses sudo when the dir is
|
|
110
|
+
* root-owned.
|
|
111
|
+
* @param {string} dir absolute path to the runtime dir
|
|
112
|
+
* @returns {Promise<boolean>} true when the dir no longer exists
|
|
87
113
|
*/
|
|
88
|
-
async function
|
|
89
|
-
|
|
90
|
-
|
|
114
|
+
export async function removeRuntimeDir(dir) {
|
|
115
|
+
if (!fs.existsSync(dir)) return true;
|
|
116
|
+
try {
|
|
117
|
+
const owner = fs.statSync(dir).uid;
|
|
118
|
+
if (owner === 0 && process.getuid() !== 0) {
|
|
119
|
+
const res = await execa('sudo', ['rm', '-rf', dir], { stdio: 'inherit', reject: false });
|
|
120
|
+
return res.exitCode === 0 && !fs.existsSync(dir);
|
|
121
|
+
}
|
|
122
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
123
|
+
return !fs.existsSync(dir);
|
|
124
|
+
} catch {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Build the bash script that clears GRUB_THEME and rebuilds the boot menu. */
|
|
130
|
+
export function buildResetGrubScript(grubFile = '/etc/default/grub') {
|
|
131
|
+
return `
|
|
132
|
+
GRUB=${grubFile}
|
|
91
133
|
[ -f "$GRUB" ] || exit 0
|
|
92
134
|
sed -i '/^GRUB_THEME=/d' "$GRUB"
|
|
93
135
|
if command -v update-grub >/dev/null 2>&1; then
|
|
@@ -98,7 +140,17 @@ elif command -v grub2-mkconfig >/dev/null 2>&1; then
|
|
|
98
140
|
grub2-mkconfig -o /boot/grub2/grub.cfg
|
|
99
141
|
fi
|
|
100
142
|
`.trim();
|
|
101
|
-
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Remove GRUB_THEME from /etc/default/grub and rebuild the boot menu so the
|
|
147
|
+
* system returns to the default theme. Requires sudo when not root.
|
|
148
|
+
* @param {{grubFile?:string, asRoot?:boolean}} [opts] injectable for tests
|
|
149
|
+
* @returns {Promise<boolean>} true when GRUB was reset
|
|
150
|
+
*/
|
|
151
|
+
export async function resetGrubTheme({ grubFile = '/etc/default/grub', asRoot = process.getuid() === 0 } = {}) {
|
|
152
|
+
const script = buildResetGrubScript(grubFile);
|
|
153
|
+
if (asRoot) {
|
|
102
154
|
const res = await execa('bash', ['-c', script], { stdio: 'inherit', reject: false });
|
|
103
155
|
return res.exitCode === 0;
|
|
104
156
|
}
|
|
@@ -121,19 +173,9 @@ export async function runUninstallWizard() {
|
|
|
121
173
|
initialValue: true,
|
|
122
174
|
});
|
|
123
175
|
if (!p.isCancel(confirm) && confirm) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (owner === 0 && process.getuid() !== 0) {
|
|
128
|
-
await execa('sudo', ['rm', '-rf', dir], { stdio: 'inherit' });
|
|
129
|
-
} else {
|
|
130
|
-
fs.rmSync(dir, { recursive: true, force: true });
|
|
131
|
-
}
|
|
132
|
-
removed = true;
|
|
133
|
-
removeShadowBin();
|
|
134
|
-
} catch {
|
|
135
|
-
p.log.warn(`Could not remove ${found.dir}.`);
|
|
136
|
-
}
|
|
176
|
+
removed = await removeRuntimeDir(found.dir);
|
|
177
|
+
if (removed) removeShadowBin();
|
|
178
|
+
else p.log.warn(`Could not remove ${found.dir}.`);
|
|
137
179
|
}
|
|
138
180
|
}
|
|
139
181
|
|
|
@@ -145,6 +187,12 @@ export async function runUninstallWizard() {
|
|
|
145
187
|
if (p.isCancel(resetGrub)) { p.cancel('Aborted.'); process.exit(0); }
|
|
146
188
|
const grubReset = resetGrub ? await resetGrubTheme() : false;
|
|
147
189
|
|
|
190
|
+
// Remove the npm package so the `theamify` command actually disappears.
|
|
191
|
+
const npmRemoved = await selfUninstall();
|
|
192
|
+
|
|
193
|
+
// Remove leftover PATH markers from ~/.bashrc / ~/.zshrc so zero traces remain.
|
|
194
|
+
await cleanRcMarkers();
|
|
195
|
+
|
|
148
196
|
// Companion tools (chafa, grub-customizer) are intentionally LEFT in place —
|
|
149
197
|
// the user may want them for later; uninstall only removes theamify itself.
|
|
150
198
|
const themeNote = grubReset
|
|
@@ -152,6 +200,6 @@ export async function runUninstallWizard() {
|
|
|
152
200
|
: (resetGrub ? 'GRUB reset could not be completed — remove GRUB_THEME= from /etc/default/grub and rebuild.' : 'Your applied GRUB theme was left in place.');
|
|
153
201
|
|
|
154
202
|
p.outro(pc.green(
|
|
155
|
-
`Uninstalled.${removed ? ' Runtime + all downloaded themes removed.' : ''} ${themeNote} Companion tools (chafa, grub-customizer) were kept for your use.`,
|
|
203
|
+
`Uninstalled.${removed ? ' Runtime + all downloaded themes removed.' : ''} ${themeNote} ${npmRemoved ? 'theamify npm package removed — command no longer available.' : 'theamify npm package kept.'} Companion tools (chafa, grub-customizer) were kept for your use.`,
|
|
156
204
|
));
|
|
157
205
|
}
|
package/src/core/engine.js
CHANGED
|
@@ -94,6 +94,24 @@ export async function installUserRuntime() {
|
|
|
94
94
|
return USER_DIR;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Repair a broken install: re-provision the engine/runtime from the bundled
|
|
99
|
+
* package (fixes a missing/corrupt engine or runtime tree) and remove any stale
|
|
100
|
+
* shadow symlink. Downloads are preserved unless the whole runtime is missing.
|
|
101
|
+
* @returns {Promise<string>} path to the repaired engine
|
|
102
|
+
*/
|
|
103
|
+
export async function repairRuntime() {
|
|
104
|
+
const found = findInstalledRuntime();
|
|
105
|
+
if (found) {
|
|
106
|
+
syncEngineTo(found.dir);
|
|
107
|
+
removeShadowBin();
|
|
108
|
+
return path.join(found.dir, BIN_NAME);
|
|
109
|
+
}
|
|
110
|
+
const dir = await installUserRuntime();
|
|
111
|
+
removeShadowBin();
|
|
112
|
+
return path.join(dir, BIN_NAME);
|
|
113
|
+
}
|
|
114
|
+
|
|
97
115
|
/**
|
|
98
116
|
* Resolve the path to the engine to run for a subcommand.
|
|
99
117
|
* Prefers an installed runtime; otherwise provisions the bundled one in the
|
package/src/lib/self.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { execa } from 'execa';
|
|
2
|
+
import pc from 'picocolors';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
|
|
5
|
+
const require = createRequire(import.meta.url);
|
|
6
|
+
const pkg = require('../../package.json');
|
|
7
|
+
|
|
8
|
+
export const NPM_NAME = pkg.name;
|
|
9
|
+
export const BIN_NAME = 'theamify';
|
|
10
|
+
|
|
11
|
+
/** Simple numeric semver compare: returns <0, 0, >0 when a is older/equal/newer. */
|
|
12
|
+
export function compareVersions(a, b) {
|
|
13
|
+
const pa = String(a || '').split('.').map((n) => parseInt(n, 10) || 0);
|
|
14
|
+
const pb = String(b || '').split('.').map((n) => parseInt(n, 10) || 0);
|
|
15
|
+
for (let i = 0; i < 3; i++) {
|
|
16
|
+
const d = (pa[i] || 0) - (pb[i] || 0);
|
|
17
|
+
if (d !== 0) return d < 0 ? -1 : 1;
|
|
18
|
+
}
|
|
19
|
+
return 0;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Query npm for the latest published version of this package. */
|
|
23
|
+
export async function getLatestVersion() {
|
|
24
|
+
try {
|
|
25
|
+
const { stdout } = await execa('npm', ['view', NPM_NAME, 'version'], { reject: false });
|
|
26
|
+
const v = (stdout || '').trim();
|
|
27
|
+
return /^\d+\.\d+\.\d+/.test(v) ? v : null;
|
|
28
|
+
} catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Remove any leftover `# theamify CLI PATH` + follow-up `export PATH=` line from
|
|
35
|
+
* ~/.bashrc and ~/.zshrc (added by old installs). Called by uninstall so no
|
|
36
|
+
* traces of the tool remain in shell startup files.
|
|
37
|
+
*/
|
|
38
|
+
export async function cleanRcMarkers() {
|
|
39
|
+
const rcs = ['.bashrc', '.zshrc'].map((f) => path.join(require('node:os').homedir(), f));
|
|
40
|
+
const marker = '# theamify CLI PATH';
|
|
41
|
+
for (const rc of rcs) {
|
|
42
|
+
try {
|
|
43
|
+
const fs = require('node:fs');
|
|
44
|
+
if (!fs.existsSync(rc)) continue;
|
|
45
|
+
const lines = fs.readFileSync(rc, 'utf8').split('\n');
|
|
46
|
+
const out = [];
|
|
47
|
+
for (let i = 0; i < lines.length; i++) {
|
|
48
|
+
if (lines[i].includes(marker)) {
|
|
49
|
+
// Also drop the immediately-following `export PATH=` line.
|
|
50
|
+
if (lines[i + 1] && /^\s*export PATH=/.test(lines[i + 1])) i++;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
out.push(lines[i]);
|
|
54
|
+
}
|
|
55
|
+
fs.writeFileSync(rc, out.join('\n'));
|
|
56
|
+
} catch { /* rc not writable — skip */ }
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Check whether the running local version is behind the latest published one.
|
|
62
|
+
* @returns {Promise<{outdated:boolean, latest:string|null, current:string}>}
|
|
63
|
+
*/
|
|
64
|
+
export async function checkForUpdate() {
|
|
65
|
+
const latest = await getLatestVersion();
|
|
66
|
+
if (!latest) return { outdated: false, latest: null, current: pkg.version };
|
|
67
|
+
return { outdated: compareVersions(latest, pkg.version) > 0, latest, current: pkg.version };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* If a newer version is available, offer to self-update via `npm install -g`.
|
|
72
|
+
* @returns {Promise<boolean>} true when an update was performed
|
|
73
|
+
*/
|
|
74
|
+
export async function promptSelfUpdate() {
|
|
75
|
+
const { outdated, latest, current } = await checkForUpdate();
|
|
76
|
+
if (!outdated || !latest) return false;
|
|
77
|
+
|
|
78
|
+
const p = await import('@clack/prompts');
|
|
79
|
+
const want = await p.confirm({
|
|
80
|
+
message: `A new version (${pc.cyan('v' + latest)}) is available — you have ${pc.dim('v' + current)}. Update now?`,
|
|
81
|
+
initialValue: true,
|
|
82
|
+
});
|
|
83
|
+
if (p.isCancel(want) || !want) {
|
|
84
|
+
p.log.message(pc.dim(`Keeping v${current} — update later with: npm install -g ${NPM_NAME}@latest`));
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Release the terminal so npm's output is visible & interruptible.
|
|
89
|
+
console.log();
|
|
90
|
+
const res = await execa('npm', ['install', '-g', `${NPM_NAME}@latest`], { stdio: 'inherit', reject: false });
|
|
91
|
+
if (res.exitCode !== 0) {
|
|
92
|
+
p.log.warn('Update failed. You can retry with: npm install -g ' + NPM_NAME + '@latest');
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
p.log.success(`Updated to v${latest}. Restart ${BIN_NAME} to use the new version.`);
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Fully remove the npm package so the `theamify` command disappears from PATH.
|
|
101
|
+
* @returns {Promise<boolean>} true when uninstalled
|
|
102
|
+
*/
|
|
103
|
+
export async function selfUninstall() {
|
|
104
|
+
const p = await import('@clack/prompts');
|
|
105
|
+
const want = await p.confirm({
|
|
106
|
+
message: `Also remove the ${NPM_NAME} npm package, so the ${pc.cyan(BIN_NAME)} command is gone from your system?`,
|
|
107
|
+
initialValue: true,
|
|
108
|
+
});
|
|
109
|
+
if (p.isCancel(want) || !want) {
|
|
110
|
+
p.log.message(pc.dim(`Keeping the npm package — the ${BIN_NAME} command will remain.`));
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
console.log();
|
|
114
|
+
const res = await execa('npm', ['uninstall', '-g', NPM_NAME], { stdio: 'inherit', reject: false });
|
|
115
|
+
if (res.exitCode === 0) {
|
|
116
|
+
p.log.success(`${NPM_NAME} removed. The ${BIN_NAME} command is no longer available.`);
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
p.log.warn(`Could not uninstall ${NPM_NAME} automatically. Run: npm uninstall -g ${NPM_NAME}`);
|
|
120
|
+
return false;
|
|
121
|
+
}
|
package/src/wizard/browse.js
CHANGED
|
@@ -124,6 +124,14 @@ async function ensureAllThemes(engine, { prompt = true } = {}) {
|
|
|
124
124
|
* back to the theme list — it NEVER hard-exits until you choose Quit.
|
|
125
125
|
*/
|
|
126
126
|
export async function runThemeBrowser() {
|
|
127
|
+
// Step 0: self-check — offer an update if a newer version is published, and
|
|
128
|
+
// repair a broken/unprovisioned runtime before anything else.
|
|
129
|
+
const { checkForUpdate, promptSelfUpdate } = await import('../lib/self.js');
|
|
130
|
+
const { repairRuntime } = await import('../core/engine.js');
|
|
131
|
+
const { outdated } = await checkForUpdate();
|
|
132
|
+
if (outdated) await promptSelfUpdate();
|
|
133
|
+
await repairRuntime();
|
|
134
|
+
|
|
127
135
|
// Step 1: ensure companion tools (chafa, grub-customizer) — detect, install
|
|
128
136
|
// or update, then continue BEFORE showing the theme picker.
|
|
129
137
|
await ensureManagedTools();
|
package/vendor/theamify
CHANGED
|
@@ -10,7 +10,7 @@ set -euo pipefail
|
|
|
10
10
|
# -----------------------------------------------------------------------------
|
|
11
11
|
# VERSION & TOOL NAME
|
|
12
12
|
# -----------------------------------------------------------------------------
|
|
13
|
-
readonly VERSION="2.1
|
|
13
|
+
readonly VERSION="2.2.1"
|
|
14
14
|
readonly TOOL="theamify"
|
|
15
15
|
|
|
16
16
|
# -----------------------------------------------------------------------------
|