@kecan0406/ttheme 0.1.3 → 0.1.4
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/bin/ttheme.js +601 -172
- package/dist/manifest.json +1 -1
- package/dist/shell/palettes.zsh +1 -1
- package/package.json +1 -1
- package/shell/adapters/_osc.zsh +4 -0
- package/shell/adapters/ghostty.zsh +26 -0
- package/shell/ttheme.zsh +7 -2
package/bin/ttheme.js
CHANGED
|
@@ -4098,6 +4098,9 @@ var require_src = __commonJS((exports, module) => {
|
|
|
4098
4098
|
module.exports = { cursor, scroll, erase, beep };
|
|
4099
4099
|
});
|
|
4100
4100
|
|
|
4101
|
+
// src/bin.ts
|
|
4102
|
+
import { isMainThread as isMainThread2 } from "node:worker_threads";
|
|
4103
|
+
|
|
4101
4104
|
// node_modules/commander/lib/error.js
|
|
4102
4105
|
class CommanderError extends Error {
|
|
4103
4106
|
constructor(exitCode, code, message) {
|
|
@@ -6144,7 +6147,7 @@ var program = new Command;
|
|
|
6144
6147
|
// package.json
|
|
6145
6148
|
var package_default = {
|
|
6146
6149
|
name: "@kecan0406/ttheme",
|
|
6147
|
-
version: "0.1.
|
|
6150
|
+
version: "0.1.4",
|
|
6148
6151
|
type: "module",
|
|
6149
6152
|
bin: {
|
|
6150
6153
|
ttheme: "bin/ttheme.js"
|
|
@@ -7042,12 +7045,12 @@ ${themes.length} themes (${rotation(themes).length} in the new-tab rotation) ->
|
|
|
7042
7045
|
|
|
7043
7046
|
// src/find.ts
|
|
7044
7047
|
import { spawn as spawn2 } from "node:child_process";
|
|
7045
|
-
import { existsSync as existsSync3, mkdirSync as
|
|
7048
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync5, mkdtempSync, readFileSync as readFileSync6, rmSync as rmSync5, statSync as statSync3, writeFileSync as writeFileSync5 } from "node:fs";
|
|
7046
7049
|
import { tmpdir } from "node:os";
|
|
7047
|
-
import { dirname as
|
|
7050
|
+
import { dirname as dirname4, join as join7 } from "node:path";
|
|
7048
7051
|
|
|
7049
7052
|
// src/backdrop.ts
|
|
7050
|
-
import { mkdirSync, readdirSync as readdirSync2, rmSync as rmSync2, statSync, writeFileSync } from "node:fs";
|
|
7053
|
+
import { mkdirSync, readdirSync as readdirSync2, readFileSync as readFileSync2, renameSync, rmSync as rmSync2, statSync, utimesSync, writeFileSync } from "node:fs";
|
|
7051
7054
|
import { join as join3 } from "node:path";
|
|
7052
7055
|
|
|
7053
7056
|
// src/png.ts
|
|
@@ -7231,7 +7234,11 @@ var FILL = { width: 2560, height: 1550 };
|
|
|
7231
7234
|
var FIGURE = 2560;
|
|
7232
7235
|
var FAINT = 0.1;
|
|
7233
7236
|
var HEADROOM = 0.15;
|
|
7237
|
+
var KEEP = 0.55;
|
|
7238
|
+
var PLACE = 0.65;
|
|
7239
|
+
var STANDS = 3;
|
|
7234
7240
|
var PEAK = luminance(mix("#19161e", "#9b86c8", 0.2));
|
|
7241
|
+
var SHELF = "shelf";
|
|
7235
7242
|
function mix(a, b, t) {
|
|
7236
7243
|
const from = rgb(a);
|
|
7237
7244
|
const to = rgb(b);
|
|
@@ -7321,11 +7328,14 @@ function figureBox(image) {
|
|
|
7321
7328
|
function headAnchor(box) {
|
|
7322
7329
|
return Math.min(0.5, (HEADROOM + 0.5) * (box.w * FILL.height / FILL.width / box.h));
|
|
7323
7330
|
}
|
|
7331
|
+
function band(box, h, anchor) {
|
|
7332
|
+
const top = Math.min(Math.max(0, anchor * box.h - h / 2), box.h - h);
|
|
7333
|
+
return { x: box.x, y: box.y + top, w: box.w, h };
|
|
7334
|
+
}
|
|
7324
7335
|
function fillBox(box, width, height, anchor) {
|
|
7325
7336
|
const h = box.w * height / width;
|
|
7326
7337
|
if (h <= box.h) {
|
|
7327
|
-
|
|
7328
|
-
return { x: box.x, y: box.y + top, w: box.w, h };
|
|
7338
|
+
return band(box, h, anchor);
|
|
7329
7339
|
}
|
|
7330
7340
|
const w = box.h * width / height;
|
|
7331
7341
|
return { x: box.x + (box.w - w) / 2, y: box.y, w, h: box.h };
|
|
@@ -7342,20 +7352,66 @@ function figure(image, box) {
|
|
|
7342
7352
|
const k = Math.min(1, FIGURE / Math.max(box.w, box.h));
|
|
7343
7353
|
return resample(image, box, Math.round(box.w * k), Math.round(box.h * k));
|
|
7344
7354
|
}
|
|
7345
|
-
function
|
|
7346
|
-
|
|
7355
|
+
function fillFrame(box, width, height, anchor, clear) {
|
|
7356
|
+
const keep = KEEP * box.h;
|
|
7357
|
+
const span = box.w * height / width;
|
|
7358
|
+
if (clear < STANDS || span > keep) {
|
|
7359
|
+
return { crop: fillBox(box, width, height, anchor), at: { x: 0, y: 0, w: width, h: height }, inset: false };
|
|
7360
|
+
}
|
|
7361
|
+
const short = 1 - span / keep;
|
|
7362
|
+
const h = height * (1 - HEADROOM * short);
|
|
7363
|
+
const w = box.w * h / keep;
|
|
7364
|
+
return {
|
|
7365
|
+
crop: band(box, keep, anchor),
|
|
7366
|
+
at: { x: (width - w) * PLACE, y: height - h, w, h },
|
|
7367
|
+
inset: true
|
|
7368
|
+
};
|
|
7369
|
+
}
|
|
7370
|
+
function paint(image, frame, width, height) {
|
|
7371
|
+
const w = Math.max(1, Math.round(frame.at.w));
|
|
7372
|
+
const h = Math.max(1, Math.round(frame.at.h));
|
|
7373
|
+
const cut = resample(image, frame.crop, w, h);
|
|
7374
|
+
const out = new Uint8Array(width * height * 4);
|
|
7375
|
+
const ox = Math.round(frame.at.x);
|
|
7376
|
+
const oy = Math.round(frame.at.y);
|
|
7377
|
+
for (let y = 0;y < h; y++) {
|
|
7378
|
+
const ty = oy + y;
|
|
7379
|
+
const from = Math.max(0, -ox);
|
|
7380
|
+
const to = Math.min(w, width - ox);
|
|
7381
|
+
if (ty < 0 || ty >= height || to <= from) {
|
|
7382
|
+
continue;
|
|
7383
|
+
}
|
|
7384
|
+
out.set(cut.data.subarray((y * w + from) * 4, (y * w + to) * 4), (ty * width + ox + from) * 4);
|
|
7385
|
+
}
|
|
7386
|
+
return { width, height, data: out };
|
|
7387
|
+
}
|
|
7388
|
+
function fill(image, box, anchor, clear) {
|
|
7389
|
+
const frame = fillFrame(box, FILL.width, FILL.height, anchor, clear);
|
|
7390
|
+
return frame.inset ? paint(image, frame, FILL.width, FILL.height) : resample(image, frame.crop, FILL.width, FILL.height);
|
|
7347
7391
|
}
|
|
7348
|
-
function tryOn(image, colors, tone, width, height) {
|
|
7392
|
+
function tryOn(image, colors, tone, width, height, clear) {
|
|
7349
7393
|
const box = figureBox(image);
|
|
7350
|
-
const
|
|
7351
|
-
|
|
7394
|
+
const frame = fillFrame(box, FILL.width, FILL.height, headAnchor(box), clear);
|
|
7395
|
+
let shown;
|
|
7396
|
+
if (frame.inset) {
|
|
7397
|
+
const view = coverBox({ x: 0, y: 0, w: FILL.width, h: FILL.height }, width, height);
|
|
7398
|
+
const k = width / view.w;
|
|
7399
|
+
shown = paint(image, {
|
|
7400
|
+
...frame,
|
|
7401
|
+
at: { x: (frame.at.x - view.x) * k, y: (frame.at.y - view.y) * k, w: frame.at.w * k, h: frame.at.h * k }
|
|
7402
|
+
}, width, height);
|
|
7403
|
+
} else {
|
|
7404
|
+
shown = resample(image, coverBox(frame.crop, width, height), width, height);
|
|
7405
|
+
}
|
|
7406
|
+
return composite(tint(shown, colors.background, tone.color), colors.background, tone.opacity);
|
|
7352
7407
|
}
|
|
7353
7408
|
function backgroundsDir(configHome) {
|
|
7354
7409
|
return join3(configHome, "ttheme", "backgrounds");
|
|
7355
7410
|
}
|
|
7356
|
-
function backdropConf(name, fillPath, opacity, from) {
|
|
7411
|
+
function backdropConf(name, fillPath, opacity, from, key) {
|
|
7357
7412
|
return [
|
|
7358
7413
|
...from ? [`# from ${from}`] : [],
|
|
7414
|
+
...key ? [`# image ${key}`] : [],
|
|
7359
7415
|
`background-image = ${fillPath}`,
|
|
7360
7416
|
"background-image-fit = cover",
|
|
7361
7417
|
"background-image-position = center",
|
|
@@ -7373,26 +7429,139 @@ function clearBackdrop(dir, name) {
|
|
|
7373
7429
|
}
|
|
7374
7430
|
}
|
|
7375
7431
|
}
|
|
7432
|
+
function imageKey(origin) {
|
|
7433
|
+
return `${origin.site}_${origin.id}`;
|
|
7434
|
+
}
|
|
7435
|
+
function shelfDir(dir, name) {
|
|
7436
|
+
return join3(dir, SHELF, name);
|
|
7437
|
+
}
|
|
7438
|
+
function belongs(file, name) {
|
|
7439
|
+
const rest = file.slice(name.length);
|
|
7440
|
+
return file.startsWith(name) && (rest === ".png" || rest === ".conf" || /^@fill-\d+\.png$/.test(rest));
|
|
7441
|
+
}
|
|
7442
|
+
function activeKey(dir, name) {
|
|
7443
|
+
try {
|
|
7444
|
+
return /^# image (\S+)$/m.exec(readFileSync2(join3(dir, `${name}.conf`), "utf8"))?.[1];
|
|
7445
|
+
} catch {
|
|
7446
|
+
return;
|
|
7447
|
+
}
|
|
7448
|
+
}
|
|
7449
|
+
function shelved(dir, name) {
|
|
7450
|
+
try {
|
|
7451
|
+
return readdirSync2(shelfDir(dir, name));
|
|
7452
|
+
} catch {
|
|
7453
|
+
return [];
|
|
7454
|
+
}
|
|
7455
|
+
}
|
|
7456
|
+
function imageKeys(dir, name) {
|
|
7457
|
+
const active = activeKey(dir, name);
|
|
7458
|
+
return [...active ? [active] : [], ...shelved(dir, name)].sort();
|
|
7459
|
+
}
|
|
7460
|
+
function shelve(dir, name) {
|
|
7461
|
+
const key = activeKey(dir, name);
|
|
7462
|
+
if (!key) {
|
|
7463
|
+
return;
|
|
7464
|
+
}
|
|
7465
|
+
const to = join3(shelfDir(dir, name), key);
|
|
7466
|
+
rmSync2(to, { recursive: true, force: true });
|
|
7467
|
+
mkdirSync(to, { recursive: true });
|
|
7468
|
+
for (const file of readdirSync2(dir)) {
|
|
7469
|
+
if (belongs(file, name)) {
|
|
7470
|
+
renameSync(join3(dir, file), join3(to, file.slice(name.length)));
|
|
7471
|
+
}
|
|
7472
|
+
}
|
|
7473
|
+
clearBackdrop(dir, name);
|
|
7474
|
+
}
|
|
7475
|
+
function unshelve(dir, name, key) {
|
|
7476
|
+
const from = join3(shelfDir(dir, name), key);
|
|
7477
|
+
for (const file of readdirSync2(from)) {
|
|
7478
|
+
renameSync(join3(from, file), join3(dir, `${name}${file}`));
|
|
7479
|
+
}
|
|
7480
|
+
rmSync2(from, { recursive: true });
|
|
7481
|
+
}
|
|
7482
|
+
function originalOf(dir, name, key) {
|
|
7483
|
+
try {
|
|
7484
|
+
return readdirSync2(join3(dir, "originals")).find((file) => file.startsWith(`${name}-${key}.`));
|
|
7485
|
+
} catch {
|
|
7486
|
+
return;
|
|
7487
|
+
}
|
|
7488
|
+
}
|
|
7489
|
+
function newest(dir, name, key) {
|
|
7490
|
+
const file = originalOf(dir, name, key);
|
|
7491
|
+
if (file) {
|
|
7492
|
+
const now = new Date;
|
|
7493
|
+
utimesSync(join3(dir, "originals", file), now, now);
|
|
7494
|
+
}
|
|
7495
|
+
}
|
|
7496
|
+
function switchImage(configHome, name, step) {
|
|
7497
|
+
const dir = backgroundsDir(configHome);
|
|
7498
|
+
const keys = imageKeys(dir, name);
|
|
7499
|
+
const now = activeKey(dir, name);
|
|
7500
|
+
if (!now || keys.length < 2) {
|
|
7501
|
+
throw new Error(`${name} has no other image`);
|
|
7502
|
+
}
|
|
7503
|
+
const at2 = (keys.indexOf(now) + step + keys.length) % keys.length;
|
|
7504
|
+
const key = keys[at2];
|
|
7505
|
+
shelve(dir, name);
|
|
7506
|
+
unshelve(dir, name, key);
|
|
7507
|
+
newest(dir, name, key);
|
|
7508
|
+
return { key, at: at2 + 1, of: keys.length };
|
|
7509
|
+
}
|
|
7510
|
+
function dropImage(configHome, name) {
|
|
7511
|
+
const dir = backgroundsDir(configHome);
|
|
7512
|
+
const files = readdirSync2(dir).filter((file) => belongs(file, name));
|
|
7513
|
+
if (files.length === 0) {
|
|
7514
|
+
throw new Error(`${name} has no image`);
|
|
7515
|
+
}
|
|
7516
|
+
const keys = imageKeys(dir, name);
|
|
7517
|
+
const now = activeKey(dir, name);
|
|
7518
|
+
const original = now && originalOf(dir, name, now);
|
|
7519
|
+
if (original) {
|
|
7520
|
+
rmSync2(join3(dir, "originals", original));
|
|
7521
|
+
}
|
|
7522
|
+
for (const file of files) {
|
|
7523
|
+
rmSync2(join3(dir, file));
|
|
7524
|
+
}
|
|
7525
|
+
clearBackdrop(dir, name);
|
|
7526
|
+
const rest = keys.filter((key) => key !== now);
|
|
7527
|
+
const next = rest[(now ? keys.indexOf(now) : 0) % rest.length];
|
|
7528
|
+
if (next) {
|
|
7529
|
+
unshelve(dir, name, next);
|
|
7530
|
+
newest(dir, name, next);
|
|
7531
|
+
}
|
|
7532
|
+
return { key: now, left: rest.length };
|
|
7533
|
+
}
|
|
7534
|
+
function retire(dir, name, key) {
|
|
7535
|
+
if (activeKey(dir, name) !== key) {
|
|
7536
|
+
shelve(dir, name);
|
|
7537
|
+
}
|
|
7538
|
+
if (key) {
|
|
7539
|
+
rmSync2(join3(shelfDir(dir, name), key), { recursive: true, force: true });
|
|
7540
|
+
}
|
|
7541
|
+
clearBackdrop(dir, name);
|
|
7542
|
+
}
|
|
7376
7543
|
function installBackdrop(configHome, colors, tone, image, original) {
|
|
7377
7544
|
const dir = backgroundsDir(configHome);
|
|
7378
7545
|
const name = colors.name;
|
|
7546
|
+
const key = imageKey(original);
|
|
7379
7547
|
mkdirSync(join3(dir, "originals"), { recursive: true });
|
|
7380
|
-
|
|
7548
|
+
retire(dir, name, key);
|
|
7381
7549
|
const box = figureBox(image);
|
|
7382
7550
|
const anchor = headAnchor(box);
|
|
7551
|
+
const clear = transparency(image);
|
|
7383
7552
|
const whole = join3(dir, `${name}.png`);
|
|
7384
7553
|
const fillPath = join3(dir, `${name}@fill-${Math.round(anchor * 100)}.png`);
|
|
7385
7554
|
const conf = join3(dir, `${name}.conf`);
|
|
7386
7555
|
const source = join3(dir, "originals", `${name}-${original.site}_${original.id}.${original.ext}`);
|
|
7387
7556
|
writeFileSync(whole, encodePng(tint(figure(image, box), colors.background, tone.color)));
|
|
7388
|
-
writeFileSync(fillPath, encodePng(tint(fill(image, box, anchor), colors.background, tone.color)));
|
|
7389
|
-
writeFileSync(conf, backdropConf(name, fillPath, tone.opacity, original.from));
|
|
7557
|
+
writeFileSync(fillPath, encodePng(tint(fill(image, box, anchor, clear), colors.background, tone.color)));
|
|
7558
|
+
writeFileSync(conf, backdropConf(name, fillPath, tone.opacity, original.from, key));
|
|
7390
7559
|
writeFileSync(source, original.bytes);
|
|
7391
7560
|
return [whole, fillPath, conf, source];
|
|
7392
7561
|
}
|
|
7393
7562
|
function origins(configHome) {
|
|
7394
7563
|
const dir = join3(backgroundsDir(configHome), "originals");
|
|
7395
|
-
const
|
|
7564
|
+
const newest2 = new Map;
|
|
7396
7565
|
let files;
|
|
7397
7566
|
try {
|
|
7398
7567
|
files = readdirSync2(dir);
|
|
@@ -7406,21 +7575,23 @@ function origins(configHome) {
|
|
|
7406
7575
|
continue;
|
|
7407
7576
|
}
|
|
7408
7577
|
const at2 = statSync(join3(dir, file)).mtimeMs;
|
|
7409
|
-
const known =
|
|
7578
|
+
const known = newest2.get(name);
|
|
7410
7579
|
if (!known || at2 > known.at) {
|
|
7411
|
-
|
|
7580
|
+
newest2.set(name, { site, id: Number(id), at: at2 });
|
|
7412
7581
|
}
|
|
7413
7582
|
}
|
|
7414
|
-
return new Map([...
|
|
7583
|
+
return new Map([...newest2].map(([name, { site, id }]) => [name, { site, id }]));
|
|
7415
7584
|
}
|
|
7416
7585
|
|
|
7417
7586
|
// src/booru.ts
|
|
7587
|
+
import { readdirSync as readdirSync3, rmSync as rmSync3, statSync as statSync2 } from "node:fs";
|
|
7418
7588
|
import { setDefaultAutoSelectFamilyAttemptTimeout } from "node:net";
|
|
7419
7589
|
import { homedir } from "node:os";
|
|
7420
7590
|
import { join as join4 } from "node:path";
|
|
7421
7591
|
import { setTimeout as sleep } from "node:timers/promises";
|
|
7422
7592
|
var PAGE = 100;
|
|
7423
7593
|
var MAX_PIXELS = 25000000;
|
|
7594
|
+
var CACHE_BYTES = 512 * 1024 * 1024;
|
|
7424
7595
|
var RATINGS = ["safe", "questionable", "explicit"];
|
|
7425
7596
|
var BLOCKS = ["nudity", "underwear"];
|
|
7426
7597
|
var EXPOSED = {
|
|
@@ -7435,10 +7606,18 @@ var CONNECT = 3000;
|
|
|
7435
7606
|
var MAX_WAIT = 60000;
|
|
7436
7607
|
var TRIES = 3;
|
|
7437
7608
|
var AGENT = `ttheme/${package_default.version} (+${package_default.homepage})`;
|
|
7609
|
+
var HELD = ["orig", "tile", "thumb", "cut"];
|
|
7438
7610
|
var paused = new Map;
|
|
7439
7611
|
var HOSTS = hostMap(process.env.TTHEME_FIND_HOSTS);
|
|
7440
7612
|
var CUTOUTS = cutoutMap(process.env.TTHEME_FIND_CUTOUTS);
|
|
7441
7613
|
setDefaultAutoSelectFamilyAttemptTimeout(CONNECT);
|
|
7614
|
+
function listing(dir) {
|
|
7615
|
+
try {
|
|
7616
|
+
return readdirSync3(dir);
|
|
7617
|
+
} catch {
|
|
7618
|
+
return [];
|
|
7619
|
+
}
|
|
7620
|
+
}
|
|
7442
7621
|
function absolute(url) {
|
|
7443
7622
|
return url.startsWith("//") ? `https:${url}` : url;
|
|
7444
7623
|
}
|
|
@@ -7762,8 +7941,38 @@ function mates(owners) {
|
|
|
7762
7941
|
}
|
|
7763
7942
|
return byOwner;
|
|
7764
7943
|
}
|
|
7944
|
+
function cacheRoot() {
|
|
7945
|
+
return join4(process.env.XDG_CACHE_HOME ?? join4(homedir(), ".cache"), "ttheme");
|
|
7946
|
+
}
|
|
7765
7947
|
function cacheDir(site) {
|
|
7766
|
-
return join4(
|
|
7948
|
+
return join4(cacheRoot(), site.key);
|
|
7949
|
+
}
|
|
7950
|
+
function sweepCache(limit = CACHE_BYTES, root2 = cacheRoot()) {
|
|
7951
|
+
const files = [];
|
|
7952
|
+
let total = 0;
|
|
7953
|
+
for (const site of listing(root2)) {
|
|
7954
|
+
for (const kind of HELD) {
|
|
7955
|
+
const dir = join4(root2, site, kind);
|
|
7956
|
+
for (const name of listing(dir)) {
|
|
7957
|
+
const path2 = join4(dir, name);
|
|
7958
|
+
try {
|
|
7959
|
+
const { size, mtimeMs } = statSync2(path2);
|
|
7960
|
+
files.push({ path: path2, size, at: mtimeMs });
|
|
7961
|
+
total += size;
|
|
7962
|
+
} catch {}
|
|
7963
|
+
}
|
|
7964
|
+
}
|
|
7965
|
+
}
|
|
7966
|
+
files.sort((a, b) => a.at - b.at);
|
|
7967
|
+
for (const file of files) {
|
|
7968
|
+
if (total <= limit) {
|
|
7969
|
+
return;
|
|
7970
|
+
}
|
|
7971
|
+
try {
|
|
7972
|
+
rmSync3(file.path, { force: true });
|
|
7973
|
+
total -= file.size;
|
|
7974
|
+
} catch {}
|
|
7975
|
+
}
|
|
7767
7976
|
}
|
|
7768
7977
|
function retryAfter(value, now) {
|
|
7769
7978
|
if (value !== null && /^\s*\d+\s*$/.test(value)) {
|
|
@@ -7846,7 +8055,7 @@ async function fetchBytes(site, url, signal, progress) {
|
|
|
7846
8055
|
}
|
|
7847
8056
|
|
|
7848
8057
|
// src/catalog.ts
|
|
7849
|
-
import { existsSync, mkdirSync as mkdirSync2, readFileSync as
|
|
8058
|
+
import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
7850
8059
|
import { dirname, join as join5 } from "node:path";
|
|
7851
8060
|
var REGISTRY_URL = "https://kecan0406.github.io/ttheme/manifest.json";
|
|
7852
8061
|
function catalogPath(configHome) {
|
|
@@ -7863,7 +8072,7 @@ function readCatalog(configHome) {
|
|
|
7863
8072
|
if (!existsSync(path2)) {
|
|
7864
8073
|
throw new Error(`no catalog at ${path2} — run \`ttheme init\` first`);
|
|
7865
8074
|
}
|
|
7866
|
-
return parseCatalog(
|
|
8075
|
+
return parseCatalog(readFileSync3(path2, "utf8"));
|
|
7867
8076
|
}
|
|
7868
8077
|
function parseCatalog(source) {
|
|
7869
8078
|
let doc;
|
|
@@ -7925,7 +8134,6 @@ function search(palettes, query) {
|
|
|
7925
8134
|
|
|
7926
8135
|
// src/cutout.ts
|
|
7927
8136
|
import { spawn } from "node:child_process";
|
|
7928
|
-
import { readFileSync as readFileSync3 } from "node:fs";
|
|
7929
8137
|
var TIMEOUT2 = 30000;
|
|
7930
8138
|
var LEAST = 3;
|
|
7931
8139
|
var MOST = 97;
|
|
@@ -7967,16 +8175,12 @@ function removeBackground(input, output, signal) {
|
|
|
7967
8175
|
});
|
|
7968
8176
|
child.on("error", reject);
|
|
7969
8177
|
child.on("close", (code) => {
|
|
7970
|
-
if (code
|
|
7971
|
-
|
|
7972
|
-
`).at(-1) || `osascript exited ${code}`));
|
|
8178
|
+
if (code === 0) {
|
|
8179
|
+
resolve();
|
|
7973
8180
|
return;
|
|
7974
8181
|
}
|
|
7975
|
-
|
|
7976
|
-
|
|
7977
|
-
} catch (error) {
|
|
7978
|
-
reject(error);
|
|
7979
|
-
}
|
|
8182
|
+
reject(new Error(failure.trim().split(`
|
|
8183
|
+
`).at(-1) || `osascript exited ${code}`));
|
|
7980
8184
|
});
|
|
7981
8185
|
});
|
|
7982
8186
|
}
|
|
@@ -8530,7 +8734,7 @@ function renderFind(view, cols, rows) {
|
|
|
8530
8734
|
}
|
|
8531
8735
|
|
|
8532
8736
|
// src/palettes.ts
|
|
8533
|
-
import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync4, rmSync as
|
|
8737
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync4, rmSync as rmSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
8534
8738
|
import { homedir as homedir2 } from "node:os";
|
|
8535
8739
|
import { dirname as dirname2, join as join6 } from "node:path";
|
|
8536
8740
|
var EMITTERS = { ghostty, kitty, alacritty };
|
|
@@ -8658,7 +8862,7 @@ function forget(configHome2, catalog, terminals, names) {
|
|
|
8658
8862
|
for (const { file } of themeFiles(terminal, toTheme(entry, catalog))) {
|
|
8659
8863
|
const path2 = join6(configHome2, terminal, "themes", file);
|
|
8660
8864
|
if (existsSync2(path2)) {
|
|
8661
|
-
|
|
8865
|
+
rmSync4(path2, { force: true });
|
|
8662
8866
|
removed.push(path2);
|
|
8663
8867
|
}
|
|
8664
8868
|
}
|
|
@@ -8667,6 +8871,117 @@ function forget(configHome2, catalog, terminals, names) {
|
|
|
8667
8871
|
return removed;
|
|
8668
8872
|
}
|
|
8669
8873
|
|
|
8874
|
+
// src/render.ts
|
|
8875
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "node:fs";
|
|
8876
|
+
import { dirname as dirname3 } from "node:path";
|
|
8877
|
+
import { isMainThread, parentPort, Worker } from "node:worker_threads";
|
|
8878
|
+
var LANES = { thumb: "tile", show: "view", backdrop: "view" };
|
|
8879
|
+
function work(task) {
|
|
8880
|
+
const image = decodeImage(new Uint8Array(readFileSync5(task.from)), MAX_PIXELS);
|
|
8881
|
+
if (task.job === "backdrop") {
|
|
8882
|
+
installBackdrop(task.home, task.colors, task.tone, image, {
|
|
8883
|
+
...task.origin,
|
|
8884
|
+
bytes: new Uint8Array(readFileSync5(task.source))
|
|
8885
|
+
});
|
|
8886
|
+
return 0;
|
|
8887
|
+
}
|
|
8888
|
+
if (task.job === "thumb") {
|
|
8889
|
+
mkdirSync4(dirname3(task.to), { recursive: true });
|
|
8890
|
+
writeFileSync4(task.to, encodePng(contain(image, task.width, task.height)));
|
|
8891
|
+
return 0;
|
|
8892
|
+
}
|
|
8893
|
+
const clear = transparency(image);
|
|
8894
|
+
mkdirSync4(dirname3(task.to), { recursive: true });
|
|
8895
|
+
writeFileSync4(task.to, encodePng(tryOn(image, task.colors, task.tone, task.width, task.height, clear)));
|
|
8896
|
+
return clear;
|
|
8897
|
+
}
|
|
8898
|
+
function serveRenders(port) {
|
|
8899
|
+
port.on("message", (task) => {
|
|
8900
|
+
try {
|
|
8901
|
+
port.postMessage({ id: task.id, value: work(task) });
|
|
8902
|
+
} catch (error) {
|
|
8903
|
+
port.postMessage({ id: task.id, error: error instanceof Error ? error.message : String(error) });
|
|
8904
|
+
}
|
|
8905
|
+
});
|
|
8906
|
+
}
|
|
8907
|
+
|
|
8908
|
+
class Line2 {
|
|
8909
|
+
worker;
|
|
8910
|
+
next = 1;
|
|
8911
|
+
waiting = new Map;
|
|
8912
|
+
send(task) {
|
|
8913
|
+
const id = this.next++;
|
|
8914
|
+
return new Promise((ok, no) => {
|
|
8915
|
+
const worker = this.open();
|
|
8916
|
+
this.waiting.set(id, { ok, no });
|
|
8917
|
+
worker.ref();
|
|
8918
|
+
worker.postMessage({ ...task, id });
|
|
8919
|
+
});
|
|
8920
|
+
}
|
|
8921
|
+
close() {
|
|
8922
|
+
this.worker?.terminate();
|
|
8923
|
+
this.worker = undefined;
|
|
8924
|
+
this.drop(new Error("renderer closed"));
|
|
8925
|
+
}
|
|
8926
|
+
open() {
|
|
8927
|
+
if (this.worker) {
|
|
8928
|
+
return this.worker;
|
|
8929
|
+
}
|
|
8930
|
+
const worker = new Worker(import.meta.filename);
|
|
8931
|
+
worker.unref();
|
|
8932
|
+
worker.on("message", ({ id, value, error }) => {
|
|
8933
|
+
const held = this.waiting.get(id);
|
|
8934
|
+
this.waiting.delete(id);
|
|
8935
|
+
if (this.waiting.size === 0) {
|
|
8936
|
+
worker.unref();
|
|
8937
|
+
}
|
|
8938
|
+
if (error !== undefined) {
|
|
8939
|
+
held?.no(new Error(error));
|
|
8940
|
+
} else {
|
|
8941
|
+
held?.ok(value);
|
|
8942
|
+
}
|
|
8943
|
+
});
|
|
8944
|
+
const lost = (error) => {
|
|
8945
|
+
if (this.worker === worker) {
|
|
8946
|
+
this.worker = undefined;
|
|
8947
|
+
}
|
|
8948
|
+
this.drop(error instanceof Error ? error : new Error("renderer stopped"));
|
|
8949
|
+
};
|
|
8950
|
+
worker.on("error", lost);
|
|
8951
|
+
worker.on("exit", () => lost(undefined));
|
|
8952
|
+
this.worker = worker;
|
|
8953
|
+
return worker;
|
|
8954
|
+
}
|
|
8955
|
+
drop(error) {
|
|
8956
|
+
for (const held of this.waiting.values()) {
|
|
8957
|
+
held.no(error);
|
|
8958
|
+
}
|
|
8959
|
+
this.waiting.clear();
|
|
8960
|
+
}
|
|
8961
|
+
}
|
|
8962
|
+
|
|
8963
|
+
class Renderer {
|
|
8964
|
+
lines = new Map;
|
|
8965
|
+
run(task) {
|
|
8966
|
+
const lane = LANES[task.job];
|
|
8967
|
+
let line = this.lines.get(lane);
|
|
8968
|
+
if (!line) {
|
|
8969
|
+
line = new Line2;
|
|
8970
|
+
this.lines.set(lane, line);
|
|
8971
|
+
}
|
|
8972
|
+
return line.send(task);
|
|
8973
|
+
}
|
|
8974
|
+
close() {
|
|
8975
|
+
for (const line of this.lines.values()) {
|
|
8976
|
+
line.close();
|
|
8977
|
+
}
|
|
8978
|
+
this.lines.clear();
|
|
8979
|
+
}
|
|
8980
|
+
}
|
|
8981
|
+
if (!isMainThread && parentPort) {
|
|
8982
|
+
serveRenders(parentPort);
|
|
8983
|
+
}
|
|
8984
|
+
|
|
8670
8985
|
// src/unblock.ts
|
|
8671
8986
|
import { connect, createServer } from "node:net";
|
|
8672
8987
|
var SPLIT = 20;
|
|
@@ -8801,12 +9116,13 @@ function initial(setting, raw) {
|
|
|
8801
9116
|
}
|
|
8802
9117
|
return setting.choices.find((choice) => choice === raw) ?? setting.choices[0];
|
|
8803
9118
|
}
|
|
8804
|
-
var
|
|
9119
|
+
var PROBE = 12;
|
|
9120
|
+
var THUMB = 12;
|
|
8805
9121
|
var PRELOAD = 2;
|
|
8806
9122
|
var SETTLE = 150;
|
|
8807
9123
|
var ESCAPE = 30;
|
|
8808
9124
|
var TRY_WIDTH = 1280;
|
|
8809
|
-
var LOOKAHEAD =
|
|
9125
|
+
var LOOKAHEAD = 2;
|
|
8810
9126
|
function blank(key, searching) {
|
|
8811
9127
|
return {
|
|
8812
9128
|
key,
|
|
@@ -8845,19 +9161,16 @@ function incomplete(input) {
|
|
|
8845
9161
|
const at2 = input.lastIndexOf("\x1B");
|
|
8846
9162
|
return at2 !== -1 && /^\[?[0-9;]*$/.test(input.slice(at2 + 1));
|
|
8847
9163
|
}
|
|
8848
|
-
function tick() {
|
|
8849
|
-
return new Promise((resolve2) => setImmediate(resolve2));
|
|
8850
|
-
}
|
|
8851
9164
|
function readCache(site, name) {
|
|
8852
9165
|
try {
|
|
8853
|
-
return JSON.parse(
|
|
9166
|
+
return JSON.parse(readFileSync6(join7(cacheDir(site), name), "utf8"));
|
|
8854
9167
|
} catch {
|
|
8855
9168
|
return {};
|
|
8856
9169
|
}
|
|
8857
9170
|
}
|
|
8858
9171
|
function writeCache(site, name, data) {
|
|
8859
|
-
|
|
8860
|
-
|
|
9172
|
+
mkdirSync5(cacheDir(site), { recursive: true });
|
|
9173
|
+
writeFileSync5(join7(cacheDir(site), name), `${JSON.stringify(data)}
|
|
8861
9174
|
`);
|
|
8862
9175
|
}
|
|
8863
9176
|
|
|
@@ -8884,6 +9197,8 @@ class Finder {
|
|
|
8884
9197
|
unsaved = new Set;
|
|
8885
9198
|
thumbQueue = [];
|
|
8886
9199
|
thumbing = 0;
|
|
9200
|
+
clarity = new Map;
|
|
9201
|
+
renders = new Renderer;
|
|
8887
9202
|
sent = new Map;
|
|
8888
9203
|
placed = new Map;
|
|
8889
9204
|
current;
|
|
@@ -8955,7 +9270,7 @@ class Finder {
|
|
|
8955
9270
|
return `${site.key}:${id}`;
|
|
8956
9271
|
}
|
|
8957
9272
|
origPath(site, id, ext) {
|
|
8958
|
-
return join7(
|
|
9273
|
+
return join7(cacheDir(site), "orig", `${id}.${ext}`);
|
|
8959
9274
|
}
|
|
8960
9275
|
tileOf(site, post2, variants) {
|
|
8961
9276
|
const version2 = rendition(post2) ?? post2;
|
|
@@ -9024,7 +9339,10 @@ class Finder {
|
|
|
9024
9339
|
this.write("\x1B_Ga=d,d=A,q=2\x1B\\\x1B[H\x1B[J");
|
|
9025
9340
|
stdin.setRawMode(false);
|
|
9026
9341
|
stdin.pause();
|
|
9027
|
-
|
|
9342
|
+
this.saveProbes();
|
|
9343
|
+
this.renders.close();
|
|
9344
|
+
rmSync5(this.scratch, { recursive: true, force: true });
|
|
9345
|
+
sweepCache();
|
|
9028
9346
|
return code;
|
|
9029
9347
|
}
|
|
9030
9348
|
write(text) {
|
|
@@ -9085,7 +9403,7 @@ class Finder {
|
|
|
9085
9403
|
this.rows = process.stdout.rows || this.rows;
|
|
9086
9404
|
this.scroll();
|
|
9087
9405
|
if (this.view.mode === "try" && this.current) {
|
|
9088
|
-
this.
|
|
9406
|
+
this.reveal();
|
|
9089
9407
|
}
|
|
9090
9408
|
this.pump();
|
|
9091
9409
|
this.draw();
|
|
@@ -9260,9 +9578,9 @@ class Finder {
|
|
|
9260
9578
|
save(changed) {
|
|
9261
9579
|
const path2 = join7(this.home, "ttheme", "config.zsh");
|
|
9262
9580
|
try {
|
|
9263
|
-
const before = existsSync3(path2) ?
|
|
9581
|
+
const before = existsSync3(path2) ? readFileSync6(path2, "utf8") : "";
|
|
9264
9582
|
const after = changed.reduce((text, setting) => withSetting(text, setting.name, this.setting(setting.name)), before);
|
|
9265
|
-
|
|
9583
|
+
writeFileSync5(path2, after);
|
|
9266
9584
|
} catch (error) {
|
|
9267
9585
|
this.view.error = error instanceof Error ? error.message : String(error);
|
|
9268
9586
|
}
|
|
@@ -9489,12 +9807,12 @@ class Finder {
|
|
|
9489
9807
|
this.show();
|
|
9490
9808
|
this.draw();
|
|
9491
9809
|
const tags = this.query(site);
|
|
9810
|
+
this.tally(gen, site, board, tags);
|
|
9492
9811
|
try {
|
|
9493
|
-
const
|
|
9812
|
+
const owners = await this.mateOwners(site);
|
|
9494
9813
|
if (gen !== this.gen) {
|
|
9495
9814
|
return;
|
|
9496
9815
|
}
|
|
9497
|
-
board.total = total;
|
|
9498
9816
|
const room = tagsOf(tags) + 1 <= site.tagBudget;
|
|
9499
9817
|
board.sources = [
|
|
9500
9818
|
...room ? [...owners.keys()].map((owner) => ({ tags: `${tags} user:${owner}`, page: 0, done: false })) : [],
|
|
@@ -9507,6 +9825,16 @@ class Finder {
|
|
|
9507
9825
|
this.fail(gen, error);
|
|
9508
9826
|
}
|
|
9509
9827
|
}
|
|
9828
|
+
async tally(gen, site, board, tags) {
|
|
9829
|
+
try {
|
|
9830
|
+
const total = await fetchCount(site, tags, this.signal);
|
|
9831
|
+
if (gen === this.gen) {
|
|
9832
|
+
board.total = total;
|
|
9833
|
+
this.show();
|
|
9834
|
+
this.draw();
|
|
9835
|
+
}
|
|
9836
|
+
} catch {}
|
|
9837
|
+
}
|
|
9510
9838
|
fail(gen, error) {
|
|
9511
9839
|
if (gen !== this.gen || this.signal.aborted) {
|
|
9512
9840
|
return;
|
|
@@ -9521,6 +9849,24 @@ class Finder {
|
|
|
9521
9849
|
const { perRow, rowsVis } = gridShape(this.cols, this.rows);
|
|
9522
9850
|
return this.view.tiles.length < (this.board.top + rowsVis + LOOKAHEAD) * perRow;
|
|
9523
9851
|
}
|
|
9852
|
+
async nextPage(site, board, gen) {
|
|
9853
|
+
const source = board.sources.find((s) => !s.done);
|
|
9854
|
+
if (!source) {
|
|
9855
|
+
return;
|
|
9856
|
+
}
|
|
9857
|
+
const posts = await fetchPosts(site, source.tags, source.page, this.signal);
|
|
9858
|
+
if (gen !== this.gen) {
|
|
9859
|
+
return;
|
|
9860
|
+
}
|
|
9861
|
+
source.page++;
|
|
9862
|
+
source.done = posts.length < PAGE;
|
|
9863
|
+
for (const post2 of posts) {
|
|
9864
|
+
if (!board.seen.has(post2.id)) {
|
|
9865
|
+
board.seen.add(post2.id);
|
|
9866
|
+
board.queue.push(post2);
|
|
9867
|
+
}
|
|
9868
|
+
}
|
|
9869
|
+
}
|
|
9524
9870
|
async pump() {
|
|
9525
9871
|
const gen = this.gen;
|
|
9526
9872
|
const board = this.board;
|
|
@@ -9529,40 +9875,40 @@ class Finder {
|
|
|
9529
9875
|
}
|
|
9530
9876
|
this.pumping = gen;
|
|
9531
9877
|
const site = this.site;
|
|
9878
|
+
const flight = [];
|
|
9879
|
+
let page;
|
|
9532
9880
|
try {
|
|
9533
9881
|
board.searching = true;
|
|
9534
|
-
while (gen === this.gen
|
|
9535
|
-
|
|
9536
|
-
const
|
|
9537
|
-
|
|
9538
|
-
|
|
9539
|
-
|
|
9540
|
-
const
|
|
9541
|
-
|
|
9542
|
-
|
|
9543
|
-
}
|
|
9544
|
-
source.page++;
|
|
9545
|
-
source.done = posts.length < PAGE;
|
|
9546
|
-
for (const post2 of posts) {
|
|
9547
|
-
if (!board.seen.has(post2.id)) {
|
|
9548
|
-
board.seen.add(post2.id);
|
|
9549
|
-
board.queue.push(post2);
|
|
9882
|
+
while (gen === this.gen) {
|
|
9883
|
+
while (flight.length < PROBE && board.queue.length > 0 && this.wants()) {
|
|
9884
|
+
const post2 = board.queue.shift();
|
|
9885
|
+
flight.push({ post: post2, passed: this.passes(site, post2) });
|
|
9886
|
+
}
|
|
9887
|
+
if (!page && board.queue.length < PROBE && board.sources.some((s) => !s.done) && this.wants()) {
|
|
9888
|
+
const job = this.nextPage(site, board, gen).then(() => {
|
|
9889
|
+
if (page === job) {
|
|
9890
|
+
page = undefined;
|
|
9550
9891
|
}
|
|
9892
|
+
});
|
|
9893
|
+
job.catch(() => {});
|
|
9894
|
+
page = job;
|
|
9895
|
+
}
|
|
9896
|
+
const head = flight.shift();
|
|
9897
|
+
if (!head) {
|
|
9898
|
+
if (!page) {
|
|
9899
|
+
break;
|
|
9551
9900
|
}
|
|
9901
|
+
await page;
|
|
9552
9902
|
continue;
|
|
9553
9903
|
}
|
|
9554
|
-
const
|
|
9555
|
-
const passed = await Promise.all(batch.map((post2) => this.passes(site, post2)));
|
|
9556
|
-
this.saveProbes(site);
|
|
9904
|
+
const passed = await head.passed;
|
|
9557
9905
|
if (gen !== this.gen) {
|
|
9558
9906
|
return;
|
|
9559
9907
|
}
|
|
9560
|
-
|
|
9561
|
-
|
|
9562
|
-
|
|
9563
|
-
|
|
9564
|
-
}
|
|
9565
|
-
});
|
|
9908
|
+
board.checked++;
|
|
9909
|
+
if (passed) {
|
|
9910
|
+
this.admit(site, head.post);
|
|
9911
|
+
}
|
|
9566
9912
|
this.show();
|
|
9567
9913
|
this.draw();
|
|
9568
9914
|
}
|
|
@@ -9574,6 +9920,7 @@ class Finder {
|
|
|
9574
9920
|
} catch (error) {
|
|
9575
9921
|
this.fail(gen, error);
|
|
9576
9922
|
} finally {
|
|
9923
|
+
this.saveProbes();
|
|
9577
9924
|
if (this.pumping === gen) {
|
|
9578
9925
|
this.pumping = 0;
|
|
9579
9926
|
}
|
|
@@ -9615,10 +9962,14 @@ class Finder {
|
|
|
9615
9962
|
return false;
|
|
9616
9963
|
}
|
|
9617
9964
|
}
|
|
9618
|
-
saveProbes(
|
|
9619
|
-
|
|
9620
|
-
|
|
9965
|
+
saveProbes() {
|
|
9966
|
+
for (const key of this.unsaved) {
|
|
9967
|
+
const site = SITES.find((s) => s.key === key);
|
|
9968
|
+
if (site) {
|
|
9969
|
+
writeCache(site, "probes.json", this.probes(site));
|
|
9970
|
+
}
|
|
9621
9971
|
}
|
|
9972
|
+
this.unsaved.clear();
|
|
9622
9973
|
}
|
|
9623
9974
|
admit(site, post2) {
|
|
9624
9975
|
this.posts.set(this.mark(site, post2.id), post2);
|
|
@@ -9644,22 +9995,27 @@ class Finder {
|
|
|
9644
9995
|
}
|
|
9645
9996
|
const found = origins(this.home);
|
|
9646
9997
|
const known = readCache(site, "owners.json");
|
|
9647
|
-
const
|
|
9648
|
-
for (const sibling of this.catalog.palettes) {
|
|
9998
|
+
const siblings = this.catalog.palettes.flatMap((sibling) => {
|
|
9649
9999
|
const origin = found.get(sibling.name);
|
|
9650
|
-
|
|
9651
|
-
|
|
10000
|
+
return sibling.group === this.entry.group && sibling.name !== this.entry.name && origin?.site === site.key ? [{ name: sibling.name, id: origin.id }] : [];
|
|
10001
|
+
});
|
|
10002
|
+
const asked = await Promise.all(siblings.map(async (sibling) => {
|
|
10003
|
+
const owner = known[sibling.id];
|
|
10004
|
+
if (owner !== undefined) {
|
|
10005
|
+
return { ...sibling, owner };
|
|
9652
10006
|
}
|
|
9653
|
-
|
|
9654
|
-
|
|
9655
|
-
|
|
9656
|
-
|
|
9657
|
-
|
|
9658
|
-
|
|
9659
|
-
|
|
9660
|
-
|
|
10007
|
+
try {
|
|
10008
|
+
return { ...sibling, owner: (await fetchPost(site, sibling.id, this.signal))?.owner ?? "" };
|
|
10009
|
+
} catch {
|
|
10010
|
+
return;
|
|
10011
|
+
}
|
|
10012
|
+
}));
|
|
10013
|
+
const byPalette = new Map;
|
|
10014
|
+
for (const sibling of asked) {
|
|
10015
|
+
if (sibling) {
|
|
10016
|
+
known[sibling.id] = sibling.owner;
|
|
10017
|
+
byPalette.set(sibling.name, sibling.owner);
|
|
9661
10018
|
}
|
|
9662
|
-
byPalette.set(sibling.name, owner);
|
|
9663
10019
|
}
|
|
9664
10020
|
writeCache(site, "owners.json", known);
|
|
9665
10021
|
const owners = mates(byPalette);
|
|
@@ -9668,7 +10024,7 @@ class Finder {
|
|
|
9668
10024
|
}
|
|
9669
10025
|
async cached(site, path2, url, progress2) {
|
|
9670
10026
|
if (existsSync3(path2)) {
|
|
9671
|
-
return
|
|
10027
|
+
return statSync3(path2).size;
|
|
9672
10028
|
}
|
|
9673
10029
|
const running = this.inflight.get(path2);
|
|
9674
10030
|
if (running) {
|
|
@@ -9676,9 +10032,9 @@ class Finder {
|
|
|
9676
10032
|
}
|
|
9677
10033
|
const job = (async () => {
|
|
9678
10034
|
const bytes = await fetchBytes(site, url, this.signal, progress2);
|
|
9679
|
-
|
|
9680
|
-
|
|
9681
|
-
return bytes;
|
|
10035
|
+
mkdirSync5(dirname4(path2), { recursive: true });
|
|
10036
|
+
writeFileSync5(path2, bytes);
|
|
10037
|
+
return bytes.length;
|
|
9682
10038
|
})();
|
|
9683
10039
|
this.inflight.set(path2, job);
|
|
9684
10040
|
try {
|
|
@@ -9688,7 +10044,7 @@ class Finder {
|
|
|
9688
10044
|
}
|
|
9689
10045
|
}
|
|
9690
10046
|
thumbs() {
|
|
9691
|
-
while (this.thumbing <
|
|
10047
|
+
while (this.thumbing < THUMB && this.thumbQueue.length > 0) {
|
|
9692
10048
|
const { site, post: post2 } = this.thumbQueue.shift();
|
|
9693
10049
|
this.thumbing++;
|
|
9694
10050
|
this.thumb(site, post2).catch(() => {}).finally(() => {
|
|
@@ -9700,12 +10056,11 @@ class Finder {
|
|
|
9700
10056
|
async thumb(site, post2) {
|
|
9701
10057
|
const w = TILE.cols * this.cell.w;
|
|
9702
10058
|
const h = TILE.rows * this.cell.h;
|
|
9703
|
-
const path2 = join7(
|
|
10059
|
+
const path2 = join7(cacheDir(site), "tile", `${post2.id}-${w}x${h}.png`);
|
|
9704
10060
|
if (!existsSync3(path2)) {
|
|
9705
10061
|
const thumb = join7(cacheDir(site), "thumb", `${post2.id}.${extension(post2.preview)}`);
|
|
9706
|
-
|
|
9707
|
-
|
|
9708
|
-
writeFileSync4(path2, encodePng(contain(decodeImage(bytes, MAX_PIXELS), w, h)));
|
|
10062
|
+
await this.cached(site, thumb, post2.preview);
|
|
10063
|
+
await this.renders.run({ job: "thumb", from: thumb, to: path2, width: w, height: h });
|
|
9709
10064
|
}
|
|
9710
10065
|
this.thumbPath.set(this.mark(site, post2.id), path2);
|
|
9711
10066
|
this.show();
|
|
@@ -9721,8 +10076,7 @@ class Finder {
|
|
|
9721
10076
|
return;
|
|
9722
10077
|
}
|
|
9723
10078
|
if (this.current?.id === tile.id) {
|
|
9724
|
-
this.
|
|
9725
|
-
this.draw();
|
|
10079
|
+
this.reveal();
|
|
9726
10080
|
return;
|
|
9727
10081
|
}
|
|
9728
10082
|
this.settle = setTimeout(() => void this.load(tile), SETTLE);
|
|
@@ -9744,9 +10098,9 @@ class Finder {
|
|
|
9744
10098
|
view.fetching = { id: tile.id, got: 0, size: 0 };
|
|
9745
10099
|
this.draw();
|
|
9746
10100
|
}
|
|
9747
|
-
const
|
|
10101
|
+
const size = await this.cached(site, path2, version2.file, (got, total) => {
|
|
9748
10102
|
if (view.fetching?.id === tile.id) {
|
|
9749
|
-
view.fetching = { id: tile.id, got, size };
|
|
10103
|
+
view.fetching = { id: tile.id, got, size: total };
|
|
9750
10104
|
this.draw();
|
|
9751
10105
|
}
|
|
9752
10106
|
});
|
|
@@ -9754,42 +10108,48 @@ class Finder {
|
|
|
9754
10108
|
return;
|
|
9755
10109
|
}
|
|
9756
10110
|
view.fetching = undefined;
|
|
10111
|
+
if (site !== this.site) {
|
|
10112
|
+
return;
|
|
10113
|
+
}
|
|
10114
|
+
const current = { site, id: tile.id, path: path2, ext: version2.ext, size, using: "plain", failed: false };
|
|
9757
10115
|
view.preparing = tile.id;
|
|
9758
10116
|
this.flush();
|
|
9759
|
-
await
|
|
9760
|
-
|
|
9761
|
-
if (site !== this.site) {
|
|
10117
|
+
const plain2 = await this.render(current, "plain");
|
|
10118
|
+
if (control.signal.aborted || site !== this.site) {
|
|
9762
10119
|
return;
|
|
9763
10120
|
}
|
|
9764
|
-
|
|
9765
|
-
site,
|
|
9766
|
-
id: tile.id,
|
|
9767
|
-
image,
|
|
9768
|
-
plain: image,
|
|
9769
|
-
failed: false,
|
|
9770
|
-
bytes,
|
|
9771
|
-
ext: version2.ext,
|
|
9772
|
-
clear: transparency(image)
|
|
9773
|
-
};
|
|
9774
|
-
if (current.clear === 0 && canRemoveBackground() && this.setting("TTHEME_FIND_REMOVE_BG") !== "off") {
|
|
10121
|
+
if (plain2.clear === 0 && canRemoveBackground() && this.setting("TTHEME_FIND_REMOVE_BG") !== "off") {
|
|
9775
10122
|
view.preparing = undefined;
|
|
9776
10123
|
view.cutting = tile.id;
|
|
9777
10124
|
this.flush();
|
|
9778
|
-
|
|
10125
|
+
current.cut = await this.cutOut(site, tile.id, path2, control.signal);
|
|
9779
10126
|
if (control.signal.aborted || site !== this.site) {
|
|
9780
10127
|
return;
|
|
9781
10128
|
}
|
|
9782
|
-
|
|
9783
|
-
|
|
9784
|
-
|
|
9785
|
-
|
|
10129
|
+
view.cutting = undefined;
|
|
10130
|
+
if (current.cut) {
|
|
10131
|
+
view.preparing = tile.id;
|
|
10132
|
+
this.flush();
|
|
10133
|
+
const made = await this.render(current, "cut").catch(() => {
|
|
10134
|
+
return;
|
|
10135
|
+
});
|
|
10136
|
+
if (control.signal.aborted || site !== this.site) {
|
|
10137
|
+
return;
|
|
10138
|
+
}
|
|
10139
|
+
if (made && keepable(made.clear)) {
|
|
10140
|
+
current.using = "cut";
|
|
10141
|
+
} else {
|
|
10142
|
+
rmSync5(current.cut, { force: true });
|
|
10143
|
+
current.cut = undefined;
|
|
10144
|
+
current.failed = true;
|
|
10145
|
+
}
|
|
9786
10146
|
} else {
|
|
9787
10147
|
current.failed = true;
|
|
9788
10148
|
}
|
|
9789
10149
|
}
|
|
9790
10150
|
this.current = current;
|
|
9791
10151
|
if (view.tiles[view.focus]?.id === tile.id) {
|
|
9792
|
-
this.present();
|
|
10152
|
+
await this.present();
|
|
9793
10153
|
}
|
|
9794
10154
|
this.preload();
|
|
9795
10155
|
} catch (error) {
|
|
@@ -9810,13 +10170,16 @@ class Finder {
|
|
|
9810
10170
|
}
|
|
9811
10171
|
}
|
|
9812
10172
|
async cutOut(site, id, path2, signal) {
|
|
9813
|
-
const out = join7(
|
|
10173
|
+
const out = join7(cacheDir(site), "cut", `${id}.png`);
|
|
10174
|
+
if (existsSync3(out)) {
|
|
10175
|
+
return out;
|
|
10176
|
+
}
|
|
9814
10177
|
try {
|
|
9815
|
-
|
|
9816
|
-
|
|
9817
|
-
return
|
|
10178
|
+
mkdirSync5(dirname4(out), { recursive: true });
|
|
10179
|
+
await removeBackground(path2, out, signal);
|
|
10180
|
+
return out;
|
|
9818
10181
|
} catch {
|
|
9819
|
-
|
|
10182
|
+
rmSync5(out, { force: true });
|
|
9820
10183
|
return;
|
|
9821
10184
|
}
|
|
9822
10185
|
}
|
|
@@ -9825,10 +10188,8 @@ class Finder {
|
|
|
9825
10188
|
if (!current?.cut || this.view.shown?.id !== current.id) {
|
|
9826
10189
|
return;
|
|
9827
10190
|
}
|
|
9828
|
-
current.
|
|
9829
|
-
|
|
9830
|
-
this.present();
|
|
9831
|
-
this.draw();
|
|
10191
|
+
current.using = current.using === "cut" ? "plain" : "cut";
|
|
10192
|
+
this.reveal();
|
|
9832
10193
|
}
|
|
9833
10194
|
preload() {
|
|
9834
10195
|
const view = this.view;
|
|
@@ -9847,30 +10208,65 @@ class Finder {
|
|
|
9847
10208
|
continue;
|
|
9848
10209
|
}
|
|
9849
10210
|
const path2 = this.origPath(site, post2.id, version2.ext);
|
|
9850
|
-
if (existsSync3(path2)) {
|
|
9851
|
-
continue;
|
|
9852
|
-
}
|
|
9853
10211
|
this.prefetching++;
|
|
9854
|
-
this.cached(site, path2, version2.file).catch(() => {}).finally(() => {
|
|
10212
|
+
this.cached(site, path2, version2.file).then(() => this.render({ site, id: post2.id, path: path2, ext: version2.ext, size: 0, using: "plain", failed: false }, "plain")).catch(() => {}).finally(() => {
|
|
9855
10213
|
this.prefetching--;
|
|
9856
10214
|
});
|
|
9857
10215
|
}
|
|
9858
10216
|
}
|
|
9859
|
-
|
|
9860
|
-
const current = this.current;
|
|
9861
|
-
if (!current) {
|
|
9862
|
-
return;
|
|
9863
|
-
}
|
|
10217
|
+
async render(current, using) {
|
|
9864
10218
|
const W = this.cols * this.cell.w;
|
|
9865
10219
|
const H = this.rows * this.cell.h;
|
|
9866
10220
|
const width2 = Math.min(W, TRY_WIDTH);
|
|
9867
10221
|
const height = Math.max(1, Math.round(H * width2 / W));
|
|
9868
|
-
const
|
|
9869
|
-
const
|
|
9870
|
-
|
|
9871
|
-
|
|
10222
|
+
const path2 = join7(this.scratch, `${current.site.key}-${current.id}${using === "cut" ? "c" : ""}-${width2}x${height}.png`);
|
|
10223
|
+
const key = `${current.site.key}:${current.id}:${using}`;
|
|
10224
|
+
const known = this.clarity.get(key);
|
|
10225
|
+
if (known !== undefined && existsSync3(path2)) {
|
|
10226
|
+
return { clear: known, path: path2 };
|
|
10227
|
+
}
|
|
10228
|
+
const clear = await this.renders.run({
|
|
10229
|
+
job: "show",
|
|
10230
|
+
from: using === "cut" ? current.cut : current.path,
|
|
10231
|
+
to: path2,
|
|
10232
|
+
width: width2,
|
|
10233
|
+
height,
|
|
10234
|
+
colors: this.entry,
|
|
10235
|
+
tone: this.tone
|
|
10236
|
+
});
|
|
10237
|
+
this.clarity.set(key, clear);
|
|
10238
|
+
return { clear, path: path2 };
|
|
10239
|
+
}
|
|
10240
|
+
async present() {
|
|
10241
|
+
const current = this.current;
|
|
10242
|
+
if (!current) {
|
|
10243
|
+
return;
|
|
10244
|
+
}
|
|
10245
|
+
const using = current.using;
|
|
10246
|
+
const { clear, path: path2 } = await this.render(current, using);
|
|
10247
|
+
if (this.current !== current || current.using !== using) {
|
|
10248
|
+
return;
|
|
9872
10249
|
}
|
|
9873
|
-
this.view.shown = {
|
|
10250
|
+
this.view.shown = {
|
|
10251
|
+
id: current.id,
|
|
10252
|
+
clear,
|
|
10253
|
+
bytes: current.size,
|
|
10254
|
+
path: path2,
|
|
10255
|
+
cut: current.cut ? using === "cut" ? "on" : "off" : current.failed ? "failed" : "none"
|
|
10256
|
+
};
|
|
10257
|
+
}
|
|
10258
|
+
reveal() {
|
|
10259
|
+
const view = this.view;
|
|
10260
|
+
view.preparing = this.current?.id;
|
|
10261
|
+
this.flush();
|
|
10262
|
+
this.present().catch((error) => {
|
|
10263
|
+
view.error = error instanceof Error ? error.message : String(error);
|
|
10264
|
+
}).finally(() => {
|
|
10265
|
+
if (view.preparing === this.current?.id) {
|
|
10266
|
+
view.preparing = undefined;
|
|
10267
|
+
}
|
|
10268
|
+
this.draw();
|
|
10269
|
+
});
|
|
9874
10270
|
}
|
|
9875
10271
|
async install() {
|
|
9876
10272
|
const view = this.view;
|
|
@@ -9882,15 +10278,24 @@ class Finder {
|
|
|
9882
10278
|
view.installing = tile.id;
|
|
9883
10279
|
view.error = undefined;
|
|
9884
10280
|
this.flush();
|
|
9885
|
-
await tick();
|
|
9886
10281
|
try {
|
|
9887
|
-
|
|
9888
|
-
|
|
9889
|
-
|
|
9890
|
-
|
|
9891
|
-
|
|
9892
|
-
|
|
10282
|
+
await this.renders.run({
|
|
10283
|
+
job: "backdrop",
|
|
10284
|
+
from: current.using === "cut" ? current.cut : current.path,
|
|
10285
|
+
source: current.path,
|
|
10286
|
+
home: this.home,
|
|
10287
|
+
colors: this.entry,
|
|
10288
|
+
tone: this.tone,
|
|
10289
|
+
origin: {
|
|
10290
|
+
site: current.site.key,
|
|
10291
|
+
id: current.id,
|
|
10292
|
+
ext: current.ext,
|
|
10293
|
+
from: `${current.site.name} ${current.id} ${current.site.pageUrl(current.id)}`
|
|
10294
|
+
}
|
|
9893
10295
|
});
|
|
10296
|
+
const known = readCache(current.site, "owners.json");
|
|
10297
|
+
known[current.id] = this.posts.get(this.mark(current.site, current.id))?.owner ?? "";
|
|
10298
|
+
writeCache(current.site, "owners.json", known);
|
|
9894
10299
|
process.stderr.write(`background · ${this.entry.name} ← ${current.site.name} ${current.id}
|
|
9895
10300
|
`);
|
|
9896
10301
|
this.finish(0);
|
|
@@ -9981,19 +10386,38 @@ async function runFind(name) {
|
|
|
9981
10386
|
return new Finder(home, catalog, entry, entry.booru ?? "").run();
|
|
9982
10387
|
}
|
|
9983
10388
|
|
|
10389
|
+
// src/images.ts
|
|
10390
|
+
function runImage(name, action) {
|
|
10391
|
+
const home = configHome();
|
|
10392
|
+
find2(readCatalog(home).palettes, name);
|
|
10393
|
+
if (action === "next" || action === "prev") {
|
|
10394
|
+
const { key, at: at2, of } = switchImage(home, name, action === "next" ? 1 : -1);
|
|
10395
|
+
process.stderr.write(`background · ${name} ${at2}/${of} ${key}
|
|
10396
|
+
`);
|
|
10397
|
+
return 0;
|
|
10398
|
+
}
|
|
10399
|
+
if (action === "drop") {
|
|
10400
|
+
const { key, left } = dropImage(home, name);
|
|
10401
|
+
process.stderr.write(`background · ${name} removed ${key ?? "the picture"} · ${left} left
|
|
10402
|
+
`);
|
|
10403
|
+
return 0;
|
|
10404
|
+
}
|
|
10405
|
+
throw new Error(`unknown image action ${action} — next, prev or drop`);
|
|
10406
|
+
}
|
|
10407
|
+
|
|
9984
10408
|
// src/init.ts
|
|
9985
10409
|
import {
|
|
9986
10410
|
chmodSync,
|
|
9987
10411
|
copyFileSync,
|
|
9988
10412
|
existsSync as existsSync5,
|
|
9989
|
-
mkdirSync as
|
|
9990
|
-
readdirSync as
|
|
9991
|
-
readFileSync as
|
|
9992
|
-
rmSync as
|
|
9993
|
-
writeFileSync as
|
|
10413
|
+
mkdirSync as mkdirSync6,
|
|
10414
|
+
readdirSync as readdirSync4,
|
|
10415
|
+
readFileSync as readFileSync7,
|
|
10416
|
+
rmSync as rmSync6,
|
|
10417
|
+
writeFileSync as writeFileSync6
|
|
9994
10418
|
} from "node:fs";
|
|
9995
10419
|
import { homedir as homedir3 } from "node:os";
|
|
9996
|
-
import { dirname as
|
|
10420
|
+
import { dirname as dirname5, join as join8 } from "node:path";
|
|
9997
10421
|
|
|
9998
10422
|
// node_modules/@clack/core/dist/index.mjs
|
|
9999
10423
|
import { styleText } from "node:util";
|
|
@@ -11932,12 +12356,12 @@ async function runBrowse() {
|
|
|
11932
12356
|
|
|
11933
12357
|
// src/init.ts
|
|
11934
12358
|
function copyDir(copies, from, to) {
|
|
11935
|
-
for (const f2 of
|
|
12359
|
+
for (const f2 of readdirSync4(from)) {
|
|
11936
12360
|
copies.push({ from: join8(from, f2), to: join8(to, f2) });
|
|
11937
12361
|
}
|
|
11938
12362
|
}
|
|
11939
12363
|
function loadManifest(root2) {
|
|
11940
|
-
return JSON.parse(
|
|
12364
|
+
return JSON.parse(readFileSync7(join8(root2, "dist", "manifest.json"), "utf8"));
|
|
11941
12365
|
}
|
|
11942
12366
|
function planInit(opts, paths) {
|
|
11943
12367
|
const dist = join8(paths.root, "dist");
|
|
@@ -11953,7 +12377,7 @@ function planInit(opts, paths) {
|
|
|
11953
12377
|
const configPath = join8(home, "config.zsh");
|
|
11954
12378
|
const settings2 = {
|
|
11955
12379
|
file: configPath,
|
|
11956
|
-
content: configFile(existsSync5(configPath) ?
|
|
12380
|
+
content: configFile(existsSync5(configPath) ? readFileSync7(configPath, "utf8") : "")
|
|
11957
12381
|
};
|
|
11958
12382
|
const notes = [];
|
|
11959
12383
|
if (opts.terminals.includes("ghostty")) {
|
|
@@ -11961,7 +12385,7 @@ function planInit(opts, paths) {
|
|
|
11961
12385
|
}
|
|
11962
12386
|
if (opts.terminals.includes("alacritty")) {
|
|
11963
12387
|
const config = join8(paths.configHome, "alacritty", "alacritty.toml");
|
|
11964
|
-
if (existsSync5(config) && !
|
|
12388
|
+
if (existsSync5(config) && !readFileSync7(config, "utf8").includes("# ttheme begin")) {
|
|
11965
12389
|
notes.push("alacritty.toml already exists — ttheme left it alone; add its themes/ import yourself");
|
|
11966
12390
|
}
|
|
11967
12391
|
}
|
|
@@ -11971,23 +12395,23 @@ function planInit(opts, paths) {
|
|
|
11971
12395
|
}
|
|
11972
12396
|
function applyInit(plan) {
|
|
11973
12397
|
for (const c2 of plan.copies) {
|
|
11974
|
-
|
|
11975
|
-
|
|
12398
|
+
mkdirSync6(dirname5(c2.to), { recursive: true });
|
|
12399
|
+
rmSync6(c2.to, { force: true });
|
|
11976
12400
|
copyFileSync(c2.from, c2.to);
|
|
11977
12401
|
if (c2.executable) {
|
|
11978
12402
|
chmodSync(c2.to, 493);
|
|
11979
12403
|
}
|
|
11980
12404
|
}
|
|
11981
|
-
|
|
11982
|
-
|
|
11983
|
-
const configHome2 =
|
|
12405
|
+
mkdirSync6(dirname5(plan.settings.file), { recursive: true });
|
|
12406
|
+
writeFileSync6(plan.settings.file, plan.settings.content);
|
|
12407
|
+
const configHome2 = dirname5(dirname5(plan.settings.file));
|
|
11984
12408
|
writeCatalog(configHome2, plan.catalog);
|
|
11985
12409
|
writeInstalled(configHome2, plan.installed);
|
|
11986
12410
|
sync(configHome2, plan.catalog, plan.installed);
|
|
11987
12411
|
for (const e of plan.edits) {
|
|
11988
|
-
|
|
11989
|
-
const current = existsSync5(e.file) ?
|
|
11990
|
-
|
|
12412
|
+
mkdirSync6(dirname5(e.file), { recursive: true });
|
|
12413
|
+
const current = existsSync5(e.file) ? readFileSync7(e.file, "utf8") : "";
|
|
12414
|
+
writeFileSync6(e.file, upsertBlock(current, e.block));
|
|
11991
12415
|
}
|
|
11992
12416
|
}
|
|
11993
12417
|
function accepted(value) {
|
|
@@ -12009,7 +12433,7 @@ function verify(plan) {
|
|
|
12009
12433
|
const missing = [
|
|
12010
12434
|
...plan.copies.filter((c2) => !existsSync5(c2.to)).map((c2) => c2.to),
|
|
12011
12435
|
...existsSync5(plan.settings.file) ? [] : [plan.settings.file],
|
|
12012
|
-
...plan.edits.filter((e) => !
|
|
12436
|
+
...plan.edits.filter((e) => !readFileSync7(e.file, "utf8").includes("# ttheme begin")).map((e) => e.file)
|
|
12013
12437
|
];
|
|
12014
12438
|
if (missing.length > 0) {
|
|
12015
12439
|
throw new Error(`init left gaps:
|
|
@@ -12134,6 +12558,9 @@ function createProgram() {
|
|
|
12134
12558
|
program2.command("find", { hidden: true }).argument("<palette>").description("pick a safebooru background for a palette — preview opens this on tab").action(async (name) => {
|
|
12135
12559
|
process.exitCode = await runFind(name);
|
|
12136
12560
|
});
|
|
12561
|
+
program2.command("image", { hidden: true }).argument("<palette>").argument("<action>", "next, prev or drop").description("switch a palette between its saved backgrounds, or remove the one shown — preview calls this").action((name, action) => {
|
|
12562
|
+
process.exitCode = runImage(name, action);
|
|
12563
|
+
});
|
|
12137
12564
|
program2.command("update").description("refresh the catalog from the registry").action(() => runUpdate());
|
|
12138
12565
|
return program2;
|
|
12139
12566
|
}
|
|
@@ -12152,4 +12579,6 @@ ${error instanceof Error ? error.message : String(error)}`);
|
|
|
12152
12579
|
}
|
|
12153
12580
|
|
|
12154
12581
|
// src/bin.ts
|
|
12155
|
-
|
|
12582
|
+
if (isMainThread2) {
|
|
12583
|
+
process.exitCode = await runCli(process.argv);
|
|
12584
|
+
}
|
package/dist/manifest.json
CHANGED
package/dist/shell/palettes.zsh
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Generated by `mise run build` — do not edit.
|
|
2
2
|
# Source of truth: themes/*.toml in the ttheme repo.
|
|
3
3
|
|
|
4
|
-
typeset -g TTHEME_VERSION=0.1.
|
|
4
|
+
typeset -g TTHEME_VERSION=0.1.4
|
|
5
5
|
|
|
6
6
|
# seed for `ttheme config` when no config.zsh exists yet
|
|
7
7
|
typeset -g TTHEME_CONFIG_TEMPLATE='# ttheme settings — exported variables win over this file
|
package/package.json
CHANGED
package/shell/adapters/_osc.zsh
CHANGED
|
@@ -420,6 +420,32 @@ __tt_pv_bg_find() {
|
|
|
420
420
|
return 0
|
|
421
421
|
}
|
|
422
422
|
|
|
423
|
+
__tt_pv_bg_images() {
|
|
424
|
+
local -a shelf=(${TTHEME_CONFIG:h}/backgrounds/shelf/$1/*(N/))
|
|
425
|
+
REPLY=$(( ${#shelf} + 1 ))
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
__tt_pv_bg_image() {
|
|
429
|
+
local name=$tune act=$1 err REPLY
|
|
430
|
+
local -i rc
|
|
431
|
+
if [[ $act != drop ]]; then
|
|
432
|
+
__tt_pv_bg_images $name
|
|
433
|
+
(( REPLY > 1 )) || { msg="$name has one image" msgt=200; return 0 }
|
|
434
|
+
fi
|
|
435
|
+
__tt_pv_untune
|
|
436
|
+
__tt_pv_bg_close
|
|
437
|
+
err=$(__tt_cli image $name $act 2>&1)
|
|
438
|
+
rc=$?
|
|
439
|
+
err=${${err//$'\n'/ }## #}
|
|
440
|
+
resized=1 bgname="" bgshown="" bgsent=() bgdim=()
|
|
441
|
+
unset "bgsrc[$name]"
|
|
442
|
+
__tt_bg_load $name
|
|
443
|
+
bgload[$name]="" bgedit[$name]=1
|
|
444
|
+
msg=${err:-"background · $name"} msgt=$(( rc ? 300 : 200 ))
|
|
445
|
+
[[ -r ${TTHEME_CONFIG:h}/backgrounds/$name.conf ]] || return 0
|
|
446
|
+
tune=$name tf=1 tsnap="${bgsize[$name]} ${bgpos[$name]} ${bgop[$name]} ${bgoff[$name]}"
|
|
447
|
+
}
|
|
448
|
+
|
|
423
449
|
__tt_pv_bg_panel() {
|
|
424
450
|
local name=$1 z=$'\e[0m' b=$'\e[1m' d=$'\e[2m' c=$ac val sty REPLY
|
|
425
451
|
local -a labs=(size position opacity) at=(0 3 6)
|
package/shell/ttheme.zsh
CHANGED
|
@@ -618,6 +618,8 @@ __tt_pv_foot() {
|
|
|
618
618
|
kk+=(space '=' enter)
|
|
619
619
|
if (( bgoff[$tune] )); then kl+=(show default keep); else kl+=(hide default keep); fi
|
|
620
620
|
__tt_pv_bg_findable $tune && { kk+=(f); kl+=(find) }
|
|
621
|
+
__tt_pv_bg_images $tune
|
|
622
|
+
(( REPLY > 1 )) && { kk+=(', .' D); kl+=("image ×$REPLY" remove) }
|
|
621
623
|
right=$b"esc"$z$d" undo"$z
|
|
622
624
|
elif (( conf )); then
|
|
623
625
|
badge=CONFIG kk=(↑↓ ←→ enter) kl=(setting value save)
|
|
@@ -700,8 +702,8 @@ __tt_pv_help() {
|
|
|
700
702
|
"enter · esc restores $back"
|
|
701
703
|
)
|
|
702
704
|
if (( bgcw )); then
|
|
703
|
-
hk+=("tune bg" "" "" "")
|
|
704
|
-
hv+=("tab · finds one on the boorus if none" "↑↓ field ←→ step ⇧←→ ×10 1-9 place" "space hides · = default · f
|
|
705
|
+
hk+=("tune bg" "" "" "" "")
|
|
706
|
+
hv+=("tab · finds one on the boorus if none" "↑↓ field ←→ step ⇧←→ ×10 1-9 place" "space hides · = default · f adds one" "enter keeps · esc undoes" ", . other saved images · D removes this one")
|
|
705
707
|
fi
|
|
706
708
|
hk+=(config "")
|
|
707
709
|
hv+=("alt-c · ↑↓ setting ←→ value" "enter saves · esc undoes")
|
|
@@ -782,6 +784,9 @@ __tt_pv_tune() {
|
|
|
782
784
|
;;
|
|
783
785
|
' ') __tt_pv_bg_adjust on ;;
|
|
784
786
|
'=') __tt_pv_bg_adjust def ;;
|
|
787
|
+
',') __tt_pv_bg_image prev ;;
|
|
788
|
+
'.') __tt_pv_bg_image next ;;
|
|
789
|
+
D) __tt_pv_bg_image drop ;;
|
|
785
790
|
f)
|
|
786
791
|
__tt_pv_bg_findable $tune || return 0
|
|
787
792
|
name=$tune
|