@kecan0406/ttheme 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -62,16 +62,17 @@ came from — `ttheme` prints it, and it is in the theme's TOML.
62
62
  npx @kecan0406/ttheme@latest init
63
63
  ```
64
64
 
65
- It detects your terminal, asks which ones to wire, places the zsh layer under
66
- `~/.config/ttheme`, and edits your terminal config and `~/.zshrc` between
67
- `# ttheme begin` / `# ttheme end` markers everything outside the markers is
68
- left alone. Running it again updates in place; `--yes` skips every prompt and
69
- takes the defaults.
70
-
71
- **`init` installs no palettes.** It sets the machinery up and then offers the
72
- catalog a series at a time — `tab` marks a series, enter installs every palette
73
- in it; `ttheme browse` opens the full catalog any time, to add or drop single
74
- palettes.
65
+ It asks which terminals to wire and which series to install — `space` marks
66
+ one, enter moves on, and `select all` takes the lot then shows what it is
67
+ about to change and writes it all at once, so cancelling before that touches
68
+ nothing. It places the zsh layer under `~/.config/ttheme` and edits your
69
+ terminal config and `~/.zshrc` between `# ttheme begin` / `# ttheme end`
70
+ markers — everything outside the markers is left alone. It ends with a receipt,
71
+ paints the first palette onto the tab you ran it in and lists what to do next
72
+ (`exec zsh` for the `ttheme` command here, a terminal restart for new tabs).
73
+ Running it again updates in place; `--yes` skips every prompt and installs no
74
+ palettes — `ttheme browse` opens the full catalog any time, to add or drop
75
+ single palettes.
75
76
 
76
77
  **WezTerm and iTerm2** — one archive per terminal in the
77
78
  [latest release](https://github.com/kecan0406/ttheme/releases/latest):
@@ -267,8 +268,8 @@ New tabs take the next palette in group order, with the counter shared across
267
268
  tabs — so opening four tabs walks you through four different characters rather
268
269
  than rolling the same one twice.
269
270
 
270
- Settings live in `~/.config/ttheme/config.zsh` — `init` seeds it from your
271
- answers, `ttheme config` opens it in `$EDITOR` and alt-c in `preview` edits it in place. Each line is a plain zsh
271
+ Settings live in `~/.config/ttheme/config.zsh` — `init` seeds it with the
272
+ defaults and never overwrites a line you changed, `ttheme config` opens it in `$EDITOR` and alt-c in `preview` edits it in place. Each line is a plain zsh
272
273
  `: ${VAR:=value}` assignment, so a variable exported before the layer loads
273
274
  still wins:
274
275
 
@@ -281,20 +282,23 @@ still wins:
281
282
 
282
283
  ## The catalog
283
284
 
284
- A fresh install carries no palettes at all. `ttheme browse` opens the catalog as
285
- a live picker groups fold and unfold, typing filters, the tab repaints as the
286
- cursor lands on a palette, `tab` marks one (or a whole series from its header),
287
- and enter installs exactly what is marked and removes what is not:
285
+ The catalog is not installed wholesale: `init` installs the series you pick, and
286
+ `init --yes` none at all. `ttheme browse` opens the catalog as a live picker
287
+ groups fold and unfold, typing filters (a query has no spaces, `space` is the
288
+ pick key), the tab repaints as the cursor lands on a palette, `space` marks one
289
+ (a series from its header, everything shown from `select all`), and enter
290
+ installs exactly what is marked and removes what is not:
288
291
 
289
292
  ```
290
293
  ◆ catalog (4/107 · 2 picked)
291
294
  │ ⌕ bo_
295
+ │ ○ select all (4)
292
296
  │ ▾ Bocchi the Rock! (2/4) ぼっち・ざ・ろっく!
293
297
  │ ▶ ● bocchi ● ▁▁▁▁▁▁ Sakura + Kessoku
294
298
  │ ● kita ● ▁▁▁▁▁▁ Wild Cherry + Kessoku
295
299
  │ ○ nijika ● ▁▁▁▁▁▁ Medallion
296
300
  │ ○ ryo ● ▁▁▁▁▁▁ TokyoNight Storm
297
- └ ↑↓ move · ←→ fold · tab pick · type to filter · enter install · esc cancel
301
+ └ ↑↓ move · ←→ fold · space pick · type to filter · enter install · esc cancel
298
302
  ```
299
303
 
300
304
  The counts stay honest: `4/107` is what the filter matched out of the catalog,
package/bin/ttheme.js CHANGED
@@ -6144,7 +6144,7 @@ var program = new Command;
6144
6144
  // package.json
6145
6145
  var package_default = {
6146
6146
  name: "@kecan0406/ttheme",
6147
- version: "0.1.1",
6147
+ version: "0.1.2",
6148
6148
  type: "module",
6149
6149
  bin: {
6150
6150
  ttheme: "bin/ttheme.js"
@@ -6812,23 +6812,17 @@ function settingLine(name, value) {
6812
6812
  function settingPattern(name) {
6813
6813
  return new RegExp(String.raw`^#? ?: \$\{${name}[^\n]*$`, "m");
6814
6814
  }
6815
- function appendSetting(content, name, value) {
6815
+ function ensureSetting(content, name) {
6816
+ if (settingPattern(name).test(content)) {
6817
+ return content;
6818
+ }
6819
+ const setting = CONFIG_SETTINGS[name];
6816
6820
  return `${content.replace(/\n*$/, `
6817
6821
  `)}
6818
- ${CONFIG_SETTINGS[name].doc}
6819
- ${settingLine(name, value)}
6822
+ ${setting.doc}
6823
+ ${settingLine(name, setting.default)}
6820
6824
  `;
6821
6825
  }
6822
- function applySetting(content, name, value) {
6823
- const pattern = settingPattern(name);
6824
- if (pattern.test(content)) {
6825
- return content.replace(pattern, () => settingLine(name, value));
6826
- }
6827
- return appendSetting(content, name, value);
6828
- }
6829
- function ensureSetting(content, name) {
6830
- return settingPattern(name).test(content) ? content : appendSetting(content, name, CONFIG_SETTINGS[name].default);
6831
- }
6832
6826
  function configTemplate() {
6833
6827
  const sections = Object.entries(CONFIG_SETTINGS).map(([name, s]) => `${s.doc}
6834
6828
  ${settingLine(name, s.default)}`);
@@ -6837,11 +6831,11 @@ ${settingLine(name, s.default)}`);
6837
6831
  `)}
6838
6832
  `;
6839
6833
  }
6840
- function configFile(content, opts) {
6841
- const seeded = content === "" ? configTemplate() : content;
6842
- const tabbed = applySetting(seeded, "TTHEME_TAB_PALETTE", opts.tabPalette);
6843
- const announced = applySetting(tabbed, "TTHEME_ANNOUNCE", opts.announce ? "1" : "0");
6844
- return ensureSetting(ensureSetting(announced, "TTHEME_FX"), "TTHEME_SORT");
6834
+ function configFile(content) {
6835
+ if (content === "") {
6836
+ return configTemplate();
6837
+ }
6838
+ return Object.keys(CONFIG_SETTINGS).reduce(ensureSetting, content);
6845
6839
  }
6846
6840
  function kittyBlock(palette) {
6847
6841
  return palette ? `include themes/${palette}.conf` : "";
@@ -9970,34 +9964,6 @@ class a extends V {
9970
9964
  });
9971
9965
  }
9972
9966
  }
9973
- var n$1 = class n extends V {
9974
- options;
9975
- cursor = 0;
9976
- get _selectedValue() {
9977
- return this.options[this.cursor];
9978
- }
9979
- changeValue() {
9980
- const e = this._selectedValue;
9981
- this.value = e === undefined ? undefined : e.value;
9982
- }
9983
- constructor(e) {
9984
- super(e, false), this.options = e.options;
9985
- const o2 = this.options.findIndex(({ value: s }) => s === e.initialValue), t2 = o2 === -1 ? 0 : o2;
9986
- this.cursor = this.options[t2]?.disabled ? findCursor(t2, 1, this.options) : t2, this.changeValue(), this.on("cursor", (s) => {
9987
- switch (s) {
9988
- case "left":
9989
- case "up":
9990
- this.cursor = findCursor(this.cursor, -1, this.options);
9991
- break;
9992
- case "down":
9993
- case "right":
9994
- this.cursor = findCursor(this.cursor, 1, this.options);
9995
- break;
9996
- }
9997
- this.changeValue();
9998
- });
9999
- }
10000
- };
10001
9967
 
10002
9968
  // node_modules/@clack/prompts/dist/index.mjs
10003
9969
  import { styleText as styleText2, stripVTControlCharacters as stripVTControlCharacters3 } from "node:util";
@@ -10100,7 +10066,7 @@ var limitOptions = ({
10100
10066
  d && g2++, c2 && g2++;
10101
10067
  const T3 = f2 + (d ? 1 : 0), y = W - (c2 ? 1 : 0);
10102
10068
  for (let t2 = T3;t2 < y; t2++) {
10103
- const n3 = e[t2], o2 = n3 ? w(n3, t2 === l) : "", h2 = wrapAnsi(o2, i, {
10069
+ const n2 = e[t2], o2 = n2 ? w(n2, t2 === l) : "", h2 = wrapAnsi(o2, i, {
10104
10070
  hard: true,
10105
10071
  trim: false
10106
10072
  }).split(`
@@ -10108,17 +10074,17 @@ var limitOptions = ({
10108
10074
  s.push(h2), g2 += h2.length;
10109
10075
  }
10110
10076
  if (g2 > v) {
10111
- let t2 = 0, n3 = 0, o2 = g2;
10077
+ let t2 = 0, n2 = 0, o2 = g2;
10112
10078
  const h2 = l - T3;
10113
10079
  let u3 = v;
10114
10080
  const L = () => I(s, o2, 0, h2, u3), E = () => I(s, o2, h2 + 1, s.length, u3, true);
10115
- d ? ({ lineCount: o2, removals: t2 } = L(), o2 > u3 && (c2 || (u3 -= 1), { lineCount: o2, removals: n3 } = E())) : (c2 || (u3 -= 1), { lineCount: o2, removals: n3 } = E(), o2 > u3 && (u3 -= 1, { lineCount: o2, removals: t2 } = L())), t2 > 0 && (d = true, s.splice(0, t2)), n3 > 0 && (c2 = true, s.splice(s.length - n3, n3));
10081
+ d ? ({ lineCount: o2, removals: t2 } = L(), o2 > u3 && (c2 || (u3 -= 1), { lineCount: o2, removals: n2 } = E())) : (c2 || (u3 -= 1), { lineCount: o2, removals: n2 } = E(), o2 > u3 && (u3 -= 1, { lineCount: o2, removals: t2 } = L())), t2 > 0 && (d = true, s.splice(0, t2)), n2 > 0 && (c2 = true, s.splice(s.length - n2, n2));
10116
10082
  }
10117
10083
  const x = [];
10118
10084
  d && x.push(M2);
10119
10085
  for (const t2 of s)
10120
- for (const n3 of t2)
10121
- x.push(n3);
10086
+ for (const n2 of t2)
10087
+ x.push(n2);
10122
10088
  return c2 && x.push(M2), x;
10123
10089
  };
10124
10090
  var confirm = (i) => {
@@ -10185,58 +10151,58 @@ ${styleText2("reset", styleText2("dim", `Press ${styleText2(["gray", "bgWhite",
10185
10151
  render() {
10186
10152
  const t2 = i.withGuide ?? settings.withGuide, a2 = wrapTextWithPrefix(i.output, i.message, t2 ? `${symbolBar(this.state)} ` : "", `${symbol(this.state)} `), r2 = `${t2 ? `${styleText2("gray", S_BAR)}
10187
10153
  ` : ""}${a2}
10188
- `, o2 = this.value ?? [], p2 = (n3, l) => {
10189
- if (n3.disabled)
10190
- return u3(n3, "disabled");
10191
- const s = o2.includes(n3.value);
10192
- return l && s ? u3(n3, "active-selected") : s ? u3(n3, "selected") : u3(n3, l ? "active" : "inactive");
10154
+ `, o2 = this.value ?? [], p2 = (n2, l) => {
10155
+ if (n2.disabled)
10156
+ return u3(n2, "disabled");
10157
+ const s = o2.includes(n2.value);
10158
+ return l && s ? u3(n2, "active-selected") : s ? u3(n2, "selected") : u3(n2, l ? "active" : "inactive");
10193
10159
  };
10194
10160
  switch (this.state) {
10195
10161
  case "submit": {
10196
- const n3 = this.options.filter(({ value: s }) => o2.includes(s)).map((s) => u3(s, "submitted")).join(styleText2("dim", ", ")) || styleText2("dim", "none"), l = wrapTextWithPrefix(i.output, n3, t2 ? `${styleText2("gray", S_BAR)} ` : "");
10162
+ const n2 = this.options.filter(({ value: s }) => o2.includes(s)).map((s) => u3(s, "submitted")).join(styleText2("dim", ", ")) || styleText2("dim", "none"), l = wrapTextWithPrefix(i.output, n2, t2 ? `${styleText2("gray", S_BAR)} ` : "");
10197
10163
  return `${r2}${l}`;
10198
10164
  }
10199
10165
  case "cancel": {
10200
- const n3 = this.options.filter(({ value: s }) => o2.includes(s)).map((s) => u3(s, "cancelled")).join(styleText2("dim", ", "));
10201
- if (n3.trim() === "")
10166
+ const n2 = this.options.filter(({ value: s }) => o2.includes(s)).map((s) => u3(s, "cancelled")).join(styleText2("dim", ", "));
10167
+ if (n2.trim() === "")
10202
10168
  return `${r2}${styleText2("gray", S_BAR)}`;
10203
- const l = wrapTextWithPrefix(i.output, n3, t2 ? `${styleText2("gray", S_BAR)} ` : "");
10169
+ const l = wrapTextWithPrefix(i.output, n2, t2 ? `${styleText2("gray", S_BAR)} ` : "");
10204
10170
  return `${r2}${l}${t2 ? `
10205
10171
  ${styleText2("gray", S_BAR)}` : ""}`;
10206
10172
  }
10207
10173
  case "error": {
10208
- const n3 = t2 ? `${styleText2("yellow", S_BAR)} ` : "", l = this.error.split(`
10174
+ const n2 = t2 ? `${styleText2("yellow", S_BAR)} ` : "", l = this.error.split(`
10209
10175
  `).map(($, C2) => C2 === 0 ? `${t2 ? `${styleText2("yellow", S_BAR_END)} ` : ""}${styleText2("yellow", $)}` : ` ${$}`).join(`
10210
10176
  `), s = r2.split(`
10211
10177
  `).length, h2 = l.split(`
10212
10178
  `).length + 1;
10213
- return `${r2}${n3}${limitOptions({
10179
+ return `${r2}${n2}${limitOptions({
10214
10180
  output: i.output,
10215
10181
  options: this.options,
10216
10182
  cursor: this.cursor,
10217
10183
  maxItems: i.maxItems,
10218
- columnPadding: n3.length,
10184
+ columnPadding: n2.length,
10219
10185
  rowPadding: s + h2,
10220
10186
  style: p2
10221
10187
  }).join(`
10222
- ${n3}`)}
10188
+ ${n2}`)}
10223
10189
  ${l}
10224
10190
  `;
10225
10191
  }
10226
10192
  default: {
10227
- const n3 = t2 ? `${styleText2("cyan", S_BAR)} ` : "", l = r2.split(`
10193
+ const n2 = t2 ? `${styleText2("cyan", S_BAR)} ` : "", l = r2.split(`
10228
10194
  `).length, s = v ? formatInstructionFooter(MULTISELECT_INSTRUCTIONS, t2) : t2 ? [styleText2("cyan", S_BAR_END)] : [], h2 = s.join(`
10229
10195
  `), $ = s.length + 1;
10230
- return `${r2}${n3}${limitOptions({
10196
+ return `${r2}${n2}${limitOptions({
10231
10197
  output: i.output,
10232
10198
  options: this.options,
10233
10199
  cursor: this.cursor,
10234
10200
  maxItems: i.maxItems,
10235
- columnPadding: n3.length,
10201
+ columnPadding: n2.length,
10236
10202
  rowPadding: l + $,
10237
10203
  style: p2
10238
10204
  }).join(`
10239
- ${n3}`)}
10205
+ ${n2}`)}
10240
10206
  ${h2}
10241
10207
  `;
10242
10208
  }
@@ -10268,18 +10234,18 @@ var C2 = (o2, e, s) => {
10268
10234
  hard: true,
10269
10235
  trim: false
10270
10236
  }, i = wrapAnsi(o2, e, a2).split(`
10271
- `), c2 = i.reduce((n3, t2) => Math.max(dist_default2(t2), n3), 0), u3 = i.map(s).reduce((n3, t2) => Math.max(dist_default2(t2), n3), 0), g2 = e - (u3 - c2);
10237
+ `), c2 = i.reduce((n2, t2) => Math.max(dist_default2(t2), n2), 0), u3 = i.map(s).reduce((n2, t2) => Math.max(dist_default2(t2), n2), 0), g2 = e - (u3 - c2);
10272
10238
  return wrapAnsi(o2, g2, a2);
10273
10239
  };
10274
10240
  var note = (o2 = "", e = "", s) => {
10275
10241
  const a2 = s?.output ?? process$1.stdout, i = s?.withGuide ?? settings.withGuide, c2 = s?.format ?? W$1, g2 = ["", ...C2(o2, getColumns(a2) - 6, c2).split(`
10276
- `).map(c2), ""], n3 = dist_default2(e), t2 = Math.max(g2.reduce((m3, F) => {
10242
+ `).map(c2), ""], n2 = dist_default2(e), t2 = Math.max(g2.reduce((m3, F) => {
10277
10243
  const O = dist_default2(F);
10278
10244
  return O > m3 ? O : m3;
10279
- }, 0), n3) + 2, h2 = g2.map((m3) => `${styleText2("gray", S_BAR)} ${m3}${" ".repeat(t2 - dist_default2(m3))}${styleText2("gray", S_BAR)}`).join(`
10245
+ }, 0), n2) + 2, h2 = g2.map((m3) => `${styleText2("gray", S_BAR)} ${m3}${" ".repeat(t2 - dist_default2(m3))}${styleText2("gray", S_BAR)}`).join(`
10280
10246
  `), T3 = i ? `${styleText2("gray", S_BAR)}
10281
10247
  ` : "", l$1 = i ? S_CONNECT_LEFT : S_CORNER_BOTTOM_LEFT;
10282
- a2.write(`${T3}${styleText2("green", S_STEP_SUBMIT)} ${styleText2("reset", e)} ${styleText2("gray", S_BAR_H.repeat(Math.max(t2 - n3 - 1, 1)) + S_CORNER_TOP_RIGHT)}
10248
+ a2.write(`${T3}${styleText2("green", S_STEP_SUBMIT)} ${styleText2("reset", e)} ${styleText2("gray", S_BAR_H.repeat(Math.max(t2 - n2 - 1, 1)) + S_CORNER_TOP_RIGHT)}
10283
10249
  ${h2}
10284
10250
  ${styleText2("gray", l$1 + S_BAR_H.repeat(t2 + 2) + S_CORNER_BOTTOM_RIGHT)}
10285
10251
  `);
@@ -10293,69 +10259,6 @@ var SELECT_INSTRUCTIONS = [
10293
10259
  `${styleText2("dim", "↑/↓")} to navigate`,
10294
10260
  `${styleText2("dim", "Enter:")} confirm`
10295
10261
  ];
10296
- var c2 = (t2, o2) => t2.includes(`
10297
- `) ? t2.split(`
10298
- `).map((d) => o2(d)).join(`
10299
- `) : o2(t2);
10300
- var select = (t2) => {
10301
- const o2 = (n3, m3) => {
10302
- if (n3 === undefined)
10303
- return "";
10304
- const s = n3.label ?? String(n3.value);
10305
- switch (m3) {
10306
- case "disabled":
10307
- return `${styleText2("gray", S_RADIO_INACTIVE)} ${c2(s, (i) => styleText2("gray", i))}${n3.hint ? ` ${styleText2("dim", `(${n3.hint ?? "disabled"})`)}` : ""}`;
10308
- case "selected":
10309
- return `${c2(s, (i) => styleText2("dim", i))}`;
10310
- case "active":
10311
- return `${styleText2("green", S_RADIO_ACTIVE)} ${s}${n3.hint ? ` ${styleText2("dim", `(${n3.hint})`)}` : ""}`;
10312
- case "cancelled":
10313
- return `${c2(s, (i) => styleText2(["strikethrough", "dim"], i))}`;
10314
- default:
10315
- return `${styleText2("dim", S_RADIO_INACTIVE)} ${c2(s, (i) => styleText2("dim", i))}`;
10316
- }
10317
- }, d = t2.showInstructions ?? true;
10318
- return new n$1({
10319
- options: t2.options,
10320
- signal: t2.signal,
10321
- input: t2.input,
10322
- output: t2.output,
10323
- initialValue: t2.initialValue,
10324
- render() {
10325
- const n3 = t2.withGuide ?? settings.withGuide, m3 = `${symbol(this.state)} `, s = `${symbolBar(this.state)} `, i = wrapTextWithPrefix(t2.output, t2.message, s, m3), u4 = `${n3 ? `${styleText2("gray", S_BAR)}
10326
- ` : ""}${i}
10327
- `;
10328
- switch (this.state) {
10329
- case "submit": {
10330
- const r2 = n3 ? `${styleText2("gray", S_BAR)} ` : "", a2 = wrapTextWithPrefix(t2.output, o2(this.options[this.cursor], "selected"), r2);
10331
- return `${u4}${a2}`;
10332
- }
10333
- case "cancel": {
10334
- const r2 = n3 ? `${styleText2("gray", S_BAR)} ` : "", a2 = wrapTextWithPrefix(t2.output, o2(this.options[this.cursor], "cancelled"), r2);
10335
- return `${u4}${a2}${n3 ? `
10336
- ${styleText2("gray", S_BAR)}` : ""}`;
10337
- }
10338
- default: {
10339
- const r2 = n3 ? `${styleText2("cyan", S_BAR)} ` : "", a2 = u4.split(`
10340
- `).length, p2 = d ? formatInstructionFooter(SELECT_INSTRUCTIONS, n3) : n3 ? [styleText2("cyan", S_BAR_END)] : [], b2 = p2.join(`
10341
- `), f2 = p2.length + 1;
10342
- return `${u4}${r2}${limitOptions({
10343
- output: t2.output,
10344
- cursor: this.cursor,
10345
- options: this.options,
10346
- maxItems: t2.maxItems,
10347
- columnPadding: r2.length,
10348
- rowPadding: a2 + f2,
10349
- style: (g2, x) => o2(g2, g2.disabled ? "disabled" : x ? "active" : "inactive")
10350
- }).join(`
10351
- ${r2}`)}
10352
- ${b2}
10353
- `;
10354
- }
10355
- }
10356
- }
10357
- }).prompt();
10358
- };
10359
10262
  var i = `${styleText2("gray", S_BAR)} `;
10360
10263
 
10361
10264
  // src/market.ts
@@ -10366,7 +10269,7 @@ import { openSync, writeSync } from "node:fs";
10366
10269
  import { ReadStream } from "node:tty";
10367
10270
  var QUERY_CODES = ["10", "11", "12", "17", ...Array.from({ length: 16 }, (_2, i2) => `4;${i2}`)];
10368
10271
  function paletteOsc(entry) {
10369
- const osc4 = entry.ansi.map((c3, i2) => `;${i2};${c3}`).join("");
10272
+ const osc4 = entry.ansi.map((c2, i2) => `;${i2};${c2}`).join("");
10370
10273
  return [
10371
10274
  `\x1B]11;${entry.background}\x1B\\`,
10372
10275
  `\x1B]10;${entry.foreground}\x1B\\`,
@@ -10422,7 +10325,7 @@ async function queryTerminalColors() {
10422
10325
  // src/ansi.ts
10423
10326
  function rgb2(hex2) {
10424
10327
  const h2 = hex2.replace("#", "");
10425
- return [h2.slice(0, 2), h2.slice(2, 4), h2.slice(4, 6)].map((c3) => Number.parseInt(c3, 16)).join(";");
10328
+ return [h2.slice(0, 2), h2.slice(2, 4), h2.slice(4, 6)].map((c2) => Number.parseInt(c2, 16)).join(";");
10426
10329
  }
10427
10330
  function ansiChip(text, bg2) {
10428
10331
  return `\x1B[48;2;${rgb2(bg2)}m ${text} \x1B[0m`;
@@ -10434,7 +10337,7 @@ function ansiFg(color2) {
10434
10337
  return `\x1B[38;2;${rgb2(color2)}m`;
10435
10338
  }
10436
10339
  function ansiSwatch(colors, bg2) {
10437
- return `\x1B[48;2;${rgb2(bg2)}m${colors.map((c3) => `\x1B[38;2;${rgb2(c3)}m▄`).join("")} \x1B[0m`;
10340
+ return `\x1B[48;2;${rgb2(bg2)}m${colors.map((c2) => `\x1B[38;2;${rgb2(c2)}m▄`).join("")} \x1B[0m`;
10438
10341
  }
10439
10342
 
10440
10343
  // src/palette-prompt.ts
@@ -10485,6 +10388,7 @@ var DIM = "\x1B[2m";
10485
10388
  var BOLD = "\x1B[1m";
10486
10389
  var BOLD_INVERSE = "\x1B[1;7m";
10487
10390
  var CYAN = "\x1B[36m";
10391
+ var YELLOW2 = "\x1B[33m";
10488
10392
  function promptFx(value) {
10489
10393
  return value === "decode" || value === "glitch" ? value : "typewriter";
10490
10394
  }
@@ -10511,7 +10415,12 @@ class PalettePrompt extends V {
10511
10415
  fxSeed = 0;
10512
10416
  fxTimer;
10513
10417
  constructor(opts) {
10514
- super({ render: () => this.draw(), input: opts.input, output: opts.output }, true);
10418
+ super({
10419
+ render: () => this.draw(),
10420
+ input: opts.input,
10421
+ output: opts.output,
10422
+ validate: opts.required ? () => this.picked.size === 0 ? `pick at least one ${this.scope}` : undefined : undefined
10423
+ }, true);
10515
10424
  this.entries = opts.entries;
10516
10425
  this.scope = opts.scope ?? "palette";
10517
10426
  this.picked = new Set(opts.installed ?? []);
@@ -10552,26 +10461,32 @@ class PalettePrompt extends V {
10552
10461
  }
10553
10462
  });
10554
10463
  this.on("key", (_char, key) => {
10555
- if (key?.name === "tab") {
10464
+ if (key?.name === "space") {
10556
10465
  this.pick(this.rows[this.cursor]);
10557
10466
  }
10558
10467
  });
10559
10468
  }
10469
+ _isActionKey(char) {
10470
+ return char === "\t" || char === " ";
10471
+ }
10560
10472
  members(group) {
10561
10473
  const filter = this.scope === "palette" ? this.userInput.trim() : "";
10562
10474
  return this.named.filter((e) => e.group === group && (filter === "" || matchesPalette(e, filter)));
10563
10475
  }
10476
+ everyone(rows) {
10477
+ return rows.flatMap((r2) => r2.kind === "group" ? this.members(r2.name) : []);
10478
+ }
10564
10479
  pick(row) {
10565
- const names = row?.kind === "palette" ? [row.entry.name] : row?.kind === "group" ? this.members(row.name).map((e) => e.name) : [];
10480
+ const names = row?.kind === "palette" ? [row.entry.name] : row?.kind === "group" ? this.members(row.name).map((e) => e.name) : row?.kind === "all" ? this.everyone(this.rows).map((e) => e.name) : [];
10566
10481
  if (names.length === 0) {
10567
10482
  return;
10568
10483
  }
10569
- const add = names.some((n3) => !this.picked.has(n3));
10570
- for (const n3 of names) {
10484
+ const add = names.some((n2) => !this.picked.has(n2));
10485
+ for (const n2 of names) {
10571
10486
  if (add) {
10572
- this.picked.add(n3);
10487
+ this.picked.add(n2);
10573
10488
  } else {
10574
- this.picked.delete(n3);
10489
+ this.picked.delete(n2);
10575
10490
  }
10576
10491
  }
10577
10492
  }
@@ -10625,18 +10540,23 @@ class PalettePrompt extends V {
10625
10540
  }
10626
10541
  rebuild(snap) {
10627
10542
  const focused = this.rows[this.cursor];
10628
- this.rows = this.scope === "series" ? seriesRows(this.entries, this.userInput) : pickerRows(this.entries, this.expanded, this.userInput);
10543
+ const body = this.scope === "series" ? seriesRows(this.entries, this.userInput) : pickerRows(this.entries, this.expanded, this.userInput);
10544
+ const start = body.length > 0 ? firstPalette(body) + 1 : 0;
10545
+ this.rows = body.length > 0 ? [{ kind: "all", count: this.allCount(body) }, ...body] : [];
10629
10546
  if (snap === "first") {
10630
- this.cursor = firstPalette(this.rows);
10547
+ this.cursor = start;
10631
10548
  } else if (snap === "keep") {
10632
10549
  const name = focused?.kind === "palette" ? focused.entry.name : "";
10633
10550
  const kept = name ? this.rows.findIndex((r2) => r2.kind === "palette" && r2.entry.name === name) : -1;
10634
- this.cursor = kept === -1 ? firstPalette(this.rows) : kept;
10551
+ this.cursor = kept === -1 ? start : kept;
10635
10552
  } else if (this.cursor >= this.rows.length) {
10636
10553
  this.cursor = Math.max(0, this.rows.length - 1);
10637
10554
  }
10638
10555
  this.sync();
10639
10556
  }
10557
+ allCount(body) {
10558
+ return this.scope === "series" ? body.length : this.everyone(body).length;
10559
+ }
10640
10560
  move(delta) {
10641
10561
  const next = this.cursor + delta;
10642
10562
  if (next >= 0 && next < this.rows.length) {
@@ -10674,6 +10594,11 @@ class PalettePrompt extends V {
10674
10594
  }
10675
10595
  renderRow(row, focused) {
10676
10596
  const marker = focused ? "▶ " : " ";
10597
+ if (row.kind === "all") {
10598
+ const box2 = this.everyone(this.rows).every((e2) => this.picked.has(e2.name)) ? "● " : "○ ";
10599
+ const tail = `(${row.count})`;
10600
+ return `${marker}${box2}select all ${this.color ? `${DIM}${tail}${RESET}` : tail}`;
10601
+ }
10677
10602
  if (row.kind === "group" && this.scope === "series") {
10678
10603
  const box2 = this.pickedIn(row.name) === row.count ? "● " : "○ ";
10679
10604
  const name = row.name.padEnd(this.seriesPad);
@@ -10714,7 +10639,7 @@ class PalettePrompt extends V {
10714
10639
  }
10715
10640
  const filter = this.userInput.trim();
10716
10641
  const total = this.scope === "series" ? this.series.length : this.named.length;
10717
- const matched = this.scope === "series" ? this.rows.length : filter ? this.named.filter((e) => matchesPalette(e, filter)).length : total;
10642
+ const matched = this.scope === "series" ? this.rows.filter((r2) => r2.kind === "group").length : filter ? this.named.filter((e) => matchesPalette(e, filter)).length : total;
10718
10643
  const head = `${bar("◆")} ${title} ${dim(`(${matched}/${total} · ${this.pickedCount()} picked)`)}`;
10719
10644
  const search2 = `${bar("│")} ${this.userInput ? `⌕ ${this.userInput}_` : dim(`⌕ search…${this.example ? ` e.g. ${this.animatedExample()}` : ""}`)}`;
10720
10645
  if (this.cursor < this.top) {
@@ -10732,7 +10657,8 @@ class PalettePrompt extends V {
10732
10657
  const below = this.rows.length - this.top - window2.length;
10733
10658
  const more = below > 0 ? `${bar("│")} ${dim(`↓ ${below} more`)}` : bar("│");
10734
10659
  const fold = this.scope === "series" ? "" : " · ←→ fold";
10735
- const hint = `${bar("")} ${dim(`↑↓ move${fold} · tab pick · type to filter · enter install · esc cancel`)}`;
10660
+ const go = this.scope === "series" ? "continue" : "install";
10661
+ const hint = this.state === "error" ? `${bar("└")} ${this.color ? `${YELLOW2}${this.error}${RESET}` : this.error}` : `${bar("└")} ${dim(`↑↓ move${fold} · space pick · type to filter · enter ${go} · esc cancel`)}`;
10736
10662
  return [head, search2, ...body, more, hint].join(`
10737
10663
  `);
10738
10664
  }
@@ -10747,8 +10673,8 @@ function runAdd(names) {
10747
10673
  const home = configHome();
10748
10674
  const catalog = readCatalog(home);
10749
10675
  const state = readInstalled(home);
10750
- const already = names.filter((n3) => state.palettes.includes(n3));
10751
- const fresh = names.filter((n3) => !state.palettes.includes(n3));
10676
+ const already = names.filter((n2) => state.palettes.includes(n2));
10677
+ const fresh = names.filter((n2) => !state.palettes.includes(n2));
10752
10678
  if (fresh.length === 0) {
10753
10679
  console.log(`already installed: ${already.join(", ")}`);
10754
10680
  return;
@@ -10765,12 +10691,12 @@ function runRemove(names) {
10765
10691
  const home = configHome();
10766
10692
  const catalog = readCatalog(home);
10767
10693
  const state = readInstalled(home);
10768
- const gone = names.filter((n3) => state.palettes.includes(n3));
10694
+ const gone = names.filter((n2) => state.palettes.includes(n2));
10769
10695
  if (gone.length === 0) {
10770
10696
  console.log(`not installed: ${names.join(", ")}`);
10771
10697
  return;
10772
10698
  }
10773
- const next = { ...state, palettes: state.palettes.filter((n3) => !gone.includes(n3)) };
10699
+ const next = { ...state, palettes: state.palettes.filter((n2) => !gone.includes(n2)) };
10774
10700
  sync(home, catalog, next);
10775
10701
  forget(home, catalog, state.terminals, gone);
10776
10702
  writeInstalled(home, next);
@@ -10813,17 +10739,15 @@ async function runUpdate() {
10813
10739
  const added = catalog.palettes.length - before;
10814
10740
  console.log(`catalog ${catalog.version} — ${catalog.palettes.length} palettes${added > 0 ? ` (+${added})` : ""}`);
10815
10741
  }
10816
- async function runBrowse(scope = "palette") {
10817
- const home = configHome();
10818
- const catalog = readCatalog(home);
10819
- const state = readInstalled(home);
10742
+ async function pickPalettes(catalog, installed, scope, required = false) {
10820
10743
  const entries = process.env.TTHEME_SORT === "series" ? catalog.palettes : alphabetical(catalog.palettes);
10821
10744
  const live = process.stdout.isTTY === true && !process.env.NO_COLOR;
10822
10745
  const saved = live ? await queryTerminalColors() : new Map;
10823
10746
  const prompt = new PalettePrompt({
10824
10747
  entries,
10825
10748
  scope,
10826
- installed: state.palettes,
10749
+ installed,
10750
+ required,
10827
10751
  color: !process.env.NO_COLOR,
10828
10752
  fx: promptFx(process.env.TTHEME_FX),
10829
10753
  onFocus: live ? (entry) => process.stdout.write(paletteOsc(entry)) : undefined
@@ -10831,12 +10755,21 @@ async function runBrowse(scope = "palette") {
10831
10755
  const done = await prompt.prompt();
10832
10756
  process.stdout.write(restoreOsc(saved));
10833
10757
  if (isCancel(done)) {
10758
+ return;
10759
+ }
10760
+ return catalog.palettes.filter((e) => prompt.picked.has(e.name)).map((e) => e.name);
10761
+ }
10762
+ async function runBrowse() {
10763
+ const home = configHome();
10764
+ const catalog = readCatalog(home);
10765
+ const state = readInstalled(home);
10766
+ const wanted = await pickPalettes(catalog, state.palettes, "palette");
10767
+ if (!wanted) {
10834
10768
  console.log("nothing changed");
10835
10769
  return;
10836
10770
  }
10837
- const wanted = catalog.palettes.filter((e) => prompt.picked.has(e.name)).map((e) => e.name);
10838
- const dropped = state.palettes.filter((n3) => !prompt.picked.has(n3));
10839
- const added = wanted.filter((n3) => !state.palettes.includes(n3));
10771
+ const dropped = state.palettes.filter((n2) => !wanted.includes(n2));
10772
+ const added = wanted.filter((n2) => !state.palettes.includes(n2));
10840
10773
  if (added.length === 0 && dropped.length === 0) {
10841
10774
  console.log("nothing changed");
10842
10775
  return;
@@ -10860,6 +10793,9 @@ function copyDir(copies, from, to) {
10860
10793
  copies.push({ from: join8(from, f2), to: join8(to, f2) });
10861
10794
  }
10862
10795
  }
10796
+ function loadManifest(root2) {
10797
+ return JSON.parse(readFileSync5(join8(root2, "dist", "manifest.json"), "utf8"));
10798
+ }
10863
10799
  function planInit(opts, paths) {
10864
10800
  const dist = join8(paths.root, "dist");
10865
10801
  const home = join8(paths.configHome, "ttheme");
@@ -10874,7 +10810,7 @@ function planInit(opts, paths) {
10874
10810
  const configPath = join8(home, "config.zsh");
10875
10811
  const settings2 = {
10876
10812
  file: configPath,
10877
- content: configFile(existsSync5(configPath) ? readFileSync5(configPath, "utf8") : "", opts)
10813
+ content: configFile(existsSync5(configPath) ? readFileSync5(configPath, "utf8") : "")
10878
10814
  };
10879
10815
  const notes = [];
10880
10816
  if (opts.terminals.includes("ghostty")) {
@@ -10887,17 +10823,16 @@ function planInit(opts, paths) {
10887
10823
  }
10888
10824
  }
10889
10825
  notes.push("wezterm and iterm2: import the palettes you install from the release archive");
10890
- const catalog = JSON.parse(readFileSync5(join8(dist, "manifest.json"), "utf8"));
10891
- const installed = { terminals: opts.terminals, palettes: [] };
10892
- return { copies, edits, settings: settings2, catalog, installed, notes };
10826
+ const installed = { terminals: opts.terminals, palettes: opts.palettes };
10827
+ return { copies, edits, settings: settings2, catalog: loadManifest(paths.root), installed, notes };
10893
10828
  }
10894
10829
  function applyInit(plan) {
10895
- for (const c3 of plan.copies) {
10896
- mkdirSync5(dirname4(c3.to), { recursive: true });
10897
- rmSync5(c3.to, { force: true });
10898
- copyFileSync(c3.from, c3.to);
10899
- if (c3.executable) {
10900
- chmodSync(c3.to, 493);
10830
+ for (const c2 of plan.copies) {
10831
+ mkdirSync5(dirname4(c2.to), { recursive: true });
10832
+ rmSync5(c2.to, { force: true });
10833
+ copyFileSync(c2.from, c2.to);
10834
+ if (c2.executable) {
10835
+ chmodSync(c2.to, 493);
10901
10836
  }
10902
10837
  }
10903
10838
  mkdirSync5(dirname4(plan.settings.file), { recursive: true });
@@ -10919,28 +10854,17 @@ function accepted(value) {
10919
10854
  }
10920
10855
  return value;
10921
10856
  }
10922
- async function ask(detected, preselected) {
10923
- intro("ttheme init");
10924
- const terminals = accepted(await multiselect({
10857
+ async function askTerminals(detected, preselected) {
10858
+ return accepted(await multiselect({
10925
10859
  message: "wire which terminals?",
10926
10860
  options: INIT_TERMINALS.map((t2) => ({ value: t2, hint: t2 === detected ? "detected" : undefined })),
10927
10861
  initialValues: preselected,
10928
10862
  required: true
10929
10863
  }));
10930
- const tabPalette = accepted(await select({
10931
- message: "new tabs",
10932
- options: [
10933
- { value: "seq", label: "rotate through the palettes", hint: "default" },
10934
- { value: "off", label: "inherit the window colors" }
10935
- ],
10936
- initialValue: "seq"
10937
- }));
10938
- const announce = accepted(await confirm({ message: 'show the palette name under "Last login:"?', initialValue: true }));
10939
- return { terminals, tabPalette, announce };
10940
10864
  }
10941
- function report(plan, opts, interactive) {
10865
+ function verify(plan) {
10942
10866
  const missing = [
10943
- ...plan.copies.filter((c3) => !existsSync5(c3.to)).map((c3) => c3.to),
10867
+ ...plan.copies.filter((c2) => !existsSync5(c2.to)).map((c2) => c2.to),
10944
10868
  ...existsSync5(plan.settings.file) ? [] : [plan.settings.file],
10945
10869
  ...plan.edits.filter((e) => !readFileSync5(e.file, "utf8").includes("# ttheme begin")).map((e) => e.file)
10946
10870
  ];
@@ -10949,6 +10873,40 @@ function report(plan, opts, interactive) {
10949
10873
  ${missing.join(`
10950
10874
  `)}`);
10951
10875
  }
10876
+ }
10877
+ function seriesOf(catalog, names) {
10878
+ return [...new Set(catalog.palettes.filter((e) => names.includes(e.name)).map((e) => e.group))];
10879
+ }
10880
+ function paintStartup(catalog, installed) {
10881
+ const startup = catalog.palettes.find((e) => e.name === startupPalette(installed));
10882
+ const live = process.stdout.isTTY === true && !process.env.NO_COLOR && !process.env.TMUX;
10883
+ if (!startup || !live) {
10884
+ return false;
10885
+ }
10886
+ process.stdout.write(paletteOsc(startup));
10887
+ return true;
10888
+ }
10889
+ function receipt(plan, opts, painted) {
10890
+ const series = seriesOf(plan.catalog, opts.palettes);
10891
+ const startup = startupPalette(plan.installed);
10892
+ note([
10893
+ `${series.join(", ")} (${opts.palettes.length})`,
10894
+ `startup ${startup}${painted ? " — this tab wears it already" : ""}`,
10895
+ "new tabs rotate through the palettes — change with `ttheme config`"
10896
+ ].join(`
10897
+ `), `installed ${opts.palettes.length} palettes`);
10898
+ const next = ["exec zsh the ttheme command in this tab"];
10899
+ if (opts.terminals.includes("ghostty")) {
10900
+ next.push("restart ghostty new tabs pick up its config");
10901
+ }
10902
+ if (opts.terminals.includes("kitty")) {
10903
+ next.push("new kitty window picks up its config");
10904
+ }
10905
+ note([...next, ...plan.notes].join(`
10906
+ `), "next");
10907
+ outro("done");
10908
+ }
10909
+ function report(plan, opts) {
10952
10910
  const lines = [
10953
10911
  `placed ${plan.copies.length} files`,
10954
10912
  `settings in ${plan.settings.file} — edit later with \`ttheme config\``,
@@ -10961,15 +10919,9 @@ ${missing.join(`
10961
10919
  lines.push("open a new kitty window to pick up its config");
10962
10920
  }
10963
10921
  lines.push(...plan.notes);
10964
- lines.push("no palettes yet — `ttheme browse` picks them from the catalog");
10965
- if (interactive) {
10966
- note(lines.join(`
10967
- `), "done");
10968
- } else {
10969
- console.log(lines.join(`
10922
+ lines.push("no palettes yet — open a new shell (`exec zsh`), then `ttheme browse` picks them from the catalog");
10923
+ console.log(lines.join(`
10970
10924
  `));
10971
- console.log("run `ttheme browse` to pick your palettes");
10972
- }
10973
10925
  }
10974
10926
  async function runInit(flags = {}) {
10975
10927
  const root2 = join8(import.meta.dirname, "..");
@@ -10989,40 +10941,40 @@ async function runInit(flags = {}) {
10989
10941
  const detected = detectTerminal(process.env);
10990
10942
  const preselected = INIT_TERMINALS.filter((t2) => t2 === detected || existsSync5(join8(configHome2, t2)));
10991
10943
  const interactive = !flags.yes && process.stdin.isTTY === true && process.stdout.isTTY === true;
10992
- let opts;
10993
- if (interactive) {
10994
- opts = await ask(detected, preselected);
10995
- } else {
10944
+ if (!interactive) {
10996
10945
  if (preselected.length === 0) {
10997
10946
  throw new Error("no supported terminal detected — run this inside ghostty, kitty or alacritty");
10998
10947
  }
10999
- opts = { terminals: preselected, tabPalette: "seq", announce: true };
10948
+ const opts2 = { terminals: preselected, palettes: [] };
10949
+ const plan2 = planInit(opts2, paths);
10950
+ applyInit(plan2);
10951
+ verify(plan2);
10952
+ report(plan2, opts2);
10953
+ return;
11000
10954
  }
10955
+ intro("ttheme init");
10956
+ const terminals = await askTerminals(detected, preselected);
10957
+ const palettes = await pickPalettes(loadManifest(root2), [], "series", true);
10958
+ if (!palettes) {
10959
+ cancel("nothing changed");
10960
+ process.exit(1);
10961
+ }
10962
+ const opts = { terminals, palettes };
11001
10963
  const plan = planInit(opts, paths);
11002
- if (interactive) {
11003
- note([
11004
- `copy ${plan.copies.length} files under ${configHome2}`,
11005
- `write ${plan.settings.file}`,
11006
- ...plan.edits.map((e) => `edit ${e.file}`)
11007
- ].join(`
11008
- `), `wiring ${opts.terminals.join(", ")}`);
11009
- const go = accepted(await confirm({ message: "apply these changes?" }));
11010
- if (!go) {
11011
- cancel("nothing changed");
11012
- process.exit(1);
11013
- }
10964
+ note([
10965
+ `install ${palettes.length} palettes — ${seriesOf(plan.catalog, palettes).join(", ")}`,
10966
+ `copy ${plan.copies.length} files under ${configHome2}`,
10967
+ `write ${plan.settings.file}`,
10968
+ ...plan.edits.map((e) => `edit ${e.file}`)
10969
+ ].join(`
10970
+ `), `wiring ${terminals.join(", ")}`);
10971
+ if (!accepted(await confirm({ message: "apply these changes?" }))) {
10972
+ cancel("nothing changed");
10973
+ process.exit(1);
11014
10974
  }
11015
10975
  applyInit(plan);
11016
- report(plan, opts, interactive);
11017
- if (!interactive) {
11018
- return;
11019
- }
11020
- const browse = accepted(await confirm({ message: "pick the series to install now?", initialValue: true }));
11021
- if (browse) {
11022
- await runBrowse("series");
11023
- } else {
11024
- outro("run `ttheme browse` when you are ready");
11025
- }
10976
+ verify(plan);
10977
+ receipt(plan, opts, paintStartup(plan.catalog, plan.installed));
11026
10978
  }
11027
10979
 
11028
10980
  // src/cli.ts
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.1.1",
2
+ "version": "0.1.2",
3
3
  "gate": [
4
4
  {
5
5
  "rule": "foreground",
@@ -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.1
4
+ typeset -g TTHEME_VERSION=0.1.2
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kecan0406/ttheme",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "ttheme": "bin/ttheme.js"