@kecan0406/ttheme 0.1.4 → 0.1.5

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
@@ -7,7 +7,8 @@ Steins;Gate, Lucky☆Star, Bocchi the Rock!, Monogatari, Sailor Moon, Serial
7
7
  Experiments Lain, VA-11 Hall-A, Persona 5, Undertale, Call of the Night,
8
8
  Higurashi, Doki Doki Literature Club, Umineko, K-On!, Cyberpunk: Edgerunners,
9
9
  Touhou Project, Chuunibyou and Jujutsu Kaisen, built for **Ghostty, kitty, Alacritty,
10
- WezTerm and iTerm2**, plus a zsh layer that rotates through them as you open tabs.
10
+ WezTerm and iTerm2**, plus a zsh layer that repaints tabs at runtime one palette everywhere, or
11
+ the next one on every new tab.
11
12
 
12
13
  Two things separate this from the usual color-scheme dump:
13
14
 
@@ -63,13 +64,16 @@ npx @kecan0406/ttheme@latest init
63
64
  ```
64
65
 
65
66
  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).
67
+ one, enter moves on, and `select all` takes the lot — and how the terminal
68
+ should wear them: `default` (preselected) puts one palette on every tab,
69
+ `rotate` gives every new tab the next palette, `keep` leaves your terminal colors
70
+ alone and only installs. Then it shows what it is about to change and writes it all at once,
71
+ so cancelling before that touches nothing. It places the zsh layer under
72
+ `~/.config/ttheme` and edits your terminal config and `~/.zshrc` between
73
+ `# ttheme begin` / `# ttheme end` markers everything outside the markers is
74
+ left alone. It ends with a receipt, paints the first palette onto the tab you
75
+ ran it in (not under `keep`) and lists what to do next (`exec zsh` for the
76
+ `ttheme` command here, a terminal restart for new tabs).
73
77
  Running it again updates in place; `--yes` skips every prompt and installs no
74
78
  palettes — `ttheme browse` opens the full catalog any time, to add or drop
75
79
  single palettes.
@@ -115,9 +119,10 @@ it for real.
115
119
 
116
120
  ```
117
121
  ttheme list every palette, grouped, with previews
118
- ttheme homura pin this tab (a unique prefix works: ttheme ho)
122
+ ttheme homura paint this tab (a unique prefix works: ttheme ho)
119
123
  ttheme preview browse live — focus repaints the tab, enter keeps it (this tab or default)
120
124
  ttheme next advance this tab to the next palette
125
+ ttheme default make a palette the one new tabs open with
121
126
  ttheme pin pick a palette for this directory — cd into it repaints, cd out restores
122
127
  ttheme unpin drop the palette pinned to this directory
123
128
  ttheme config edit settings in $EDITOR — they apply in new tabs
@@ -150,8 +155,8 @@ the cursor. The list and the sample widen with the window (the sample up to 64
150
155
  columns) and the gap between them takes the rest; the tuning panel takes the
151
156
  sample's place while open, and so does the key list once the sample is 48
152
157
  columns wide.
153
- Under Ghostty with `TTHEME_TAB_PALETTE=off`, enter asks **this tab** or
154
- **default**: default records the palette as your startup palette (so a later
158
+ Under Ghostty with `TTHEME_TAB_PALETTE=off` (the default), enter asks **this tab** or
159
+ **default**: default records the palette as your default palette (so a later
155
160
  `add` or `remove` keeps it), rewrites `theme =` in the `# ttheme begin` block of
156
161
  your Ghostty config and sends `SIGUSR2` to the Ghostty that owns the tab, so new tabs — and open tabs you have not
157
162
  painted by hand — take the palette without a restart. Painting is per surface
@@ -307,23 +312,24 @@ case your pick stays. The nearest pinned ancestor wins, so a project can pin
307
312
  one palette and a subfolder another. Open tabs pick up a changed pins file on
308
313
  their next `cd`; `unpin` drops the pin on the current directory.
309
314
 
310
- `preview`, `next`, `pin`, `unpin`, `config` and `help` match exactly; every other first argument is read
315
+ `preview`, `next`, `default`, `pin`, `unpin`, `config` and `help` match exactly; every other first argument is read
311
316
  as a palette name, where a unique prefix is enough. Mistyped names get a "did
312
317
  you mean" suggestion instead of a wall of output. Piped output drops color and
313
318
  turns tab-separated, and `NO_COLOR` is respected.
314
319
 
315
- New tabs take the next palette in group order, with the counter shared across
320
+ With `TTHEME_TAB_PALETTE=seq`, new tabs take the next palette in group order, with the counter shared across
316
321
  tabs — so opening four tabs walks you through four different characters rather
317
322
  than rolling the same one twice.
318
323
 
319
324
  Settings live in `~/.config/ttheme/config.zsh` — `init` seeds it with the
320
- 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
325
+ defaults and never overwrites a line you changed bar `TTHEME_TAB_PALETTE`, which its
326
+ default/rotate/keep question sets — `ttheme config` opens it in `$EDITOR` and alt-c in `preview` edits it in place. Each line is a plain zsh
321
327
  `: ${VAR:=value}` assignment, so a variable exported before the layer loads
322
328
  still wins:
323
329
 
324
330
  | Setting | Default | |
325
331
  |---|---|---|
326
- | `TTHEME_TAB_PALETTE` | `seq` | `off` keeps new tabs on the terminal's configured theme (`preview` → default changes it) |
332
+ | `TTHEME_TAB_PALETTE` | `off` | `off` keeps new tabs on the terminal's configured theme (`preview` → default changes it); `seq` gives every new tab the next palette. `init` writes `seq` for rotate and `off` for default and keep |
327
333
  | `TTHEME_ANNOUNCE` | `1` | `0` silences the one-line notice under "Last login:" |
328
334
  | `TTHEME_FX` | `typewriter` | search hint animation — `typewriter`, `decode` or `glitch` |
329
335
  | `TTHEME_SORT` | `abc` | `series` lists series and palettes in the order they were added instead of by name — in `ttheme`, `preview` and, once exported, the `init` picker |
package/bin/ttheme.js CHANGED
@@ -6147,7 +6147,7 @@ var program = new Command;
6147
6147
  // package.json
6148
6148
  var package_default = {
6149
6149
  name: "@kecan0406/ttheme",
6150
- version: "0.1.4",
6150
+ version: "0.1.5",
6151
6151
  type: "module",
6152
6152
  bin: {
6153
6153
  ttheme: "bin/ttheme.js"
@@ -6793,8 +6793,8 @@ function zshrcBlock(tthemeDir) {
6793
6793
  var CONFIG_HEADER = "# ttheme settings — exported variables win over this file";
6794
6794
  var CONFIG_SETTINGS = {
6795
6795
  TTHEME_TAB_PALETTE: {
6796
- doc: "# new tabs: seq rotates through the palettes, off keeps the configured terminal theme (default seq)",
6797
- default: "seq"
6796
+ doc: "# new tabs: off keeps the configured terminal theme, seq rotates through the palettes (default off)",
6797
+ default: "off"
6798
6798
  },
6799
6799
  TTHEME_ANNOUNCE: {
6800
6800
  doc: '# the palette notice under "Last login:": 1 shows it, 0 silences it (default 1)',
@@ -8312,9 +8312,10 @@ function dims(tile) {
8312
8312
  }
8313
8313
  function foot(line, cols, accent, spec) {
8314
8314
  const keys = [...spec.keys ?? []];
8315
+ let lead = spec.lead;
8315
8316
  let right = spec.right;
8316
8317
  const size = () => {
8317
- const segs2 = [...spec.lead ? [width(spec.lead[0])] : [], ...keys.map(([k, l]) => width(k) + 1 + width(l))];
8318
+ const segs2 = [...lead ? [width(lead[0])] : [], ...keys.map(([k, l]) => width(k) + 1 + width(l))];
8318
8319
  const left = (spec.badge ? width(spec.badge) + 4 : 0) + segs2.reduce((n, w) => n + w, 0) + 3 * Math.max(0, segs2.length - 1);
8319
8320
  return left + (right ? 3 + width(right[0]) + 1 + width(right[1]) : 0);
8320
8321
  };
@@ -8329,12 +8330,16 @@ function foot(line, cols, accent, spec) {
8329
8330
  break;
8330
8331
  }
8331
8332
  }
8333
+ if (lead && size() > cols) {
8334
+ const keep = Math.max(0, width(lead[0]) - (size() - cols) - 1);
8335
+ lead = [`${Array.from(lead[0]).slice(0, keep).join("")}…`, lead[1]];
8336
+ }
8332
8337
  let c = 0;
8333
8338
  if (spec.badge) {
8334
8339
  c = line.put(0, ` ${spec.badge} `, `\x1B[7;1m${accent}`) + 2;
8335
8340
  }
8336
8341
  const segs = [
8337
- ...spec.lead ? [[spec.lead]] : [],
8342
+ ...lead ? [[lead]] : [],
8338
8343
  ...keys.map(([k, l]) => [
8339
8344
  [k, B],
8340
8345
  [` ${l}`, D]
@@ -8756,6 +8761,7 @@ function readInstalled(configHome2) {
8756
8761
  return {
8757
8762
  terminals: doc.terminals.filter((t) => INIT_TERMINALS.includes(t)),
8758
8763
  ...doc.startup ? { startup: doc.startup } : {},
8764
+ ...doc.keepTheme ? { keepTheme: true } : {},
8759
8765
  palettes: doc.palettes
8760
8766
  };
8761
8767
  }
@@ -8836,7 +8842,7 @@ function sync(configHome2, catalog, state) {
8836
8842
  writeFileSync3(path2, content);
8837
8843
  written.push(path2);
8838
8844
  };
8839
- const startup = startupPalette(state);
8845
+ const startup = state.keepTheme ? undefined : startupPalette(state);
8840
8846
  for (const terminal of state.terminals) {
8841
8847
  for (const entry of entries) {
8842
8848
  for (const { file: file2, content } of themeFiles(terminal, toTheme(entry, catalog))) {
@@ -11531,6 +11537,34 @@ class a extends V {
11531
11537
  });
11532
11538
  }
11533
11539
  }
11540
+ var n$1 = class n extends V {
11541
+ options;
11542
+ cursor = 0;
11543
+ get _selectedValue() {
11544
+ return this.options[this.cursor];
11545
+ }
11546
+ changeValue() {
11547
+ const e = this._selectedValue;
11548
+ this.value = e === undefined ? undefined : e.value;
11549
+ }
11550
+ constructor(e) {
11551
+ super(e, false), this.options = e.options;
11552
+ const o2 = this.options.findIndex(({ value: s }) => s === e.initialValue), t2 = o2 === -1 ? 0 : o2;
11553
+ this.cursor = this.options[t2]?.disabled ? findCursor(t2, 1, this.options) : t2, this.changeValue(), this.on("cursor", (s) => {
11554
+ switch (s) {
11555
+ case "left":
11556
+ case "up":
11557
+ this.cursor = findCursor(this.cursor, -1, this.options);
11558
+ break;
11559
+ case "down":
11560
+ case "right":
11561
+ this.cursor = findCursor(this.cursor, 1, this.options);
11562
+ break;
11563
+ }
11564
+ this.changeValue();
11565
+ });
11566
+ }
11567
+ };
11534
11568
 
11535
11569
  // node_modules/@clack/prompts/dist/index.mjs
11536
11570
  import { styleText as styleText2, stripVTControlCharacters as stripVTControlCharacters3 } from "node:util";
@@ -11633,7 +11667,7 @@ var limitOptions = ({
11633
11667
  d && g2++, c2 && g2++;
11634
11668
  const T3 = f2 + (d ? 1 : 0), y = W - (c2 ? 1 : 0);
11635
11669
  for (let t2 = T3;t2 < y; t2++) {
11636
- const n2 = e[t2], o2 = n2 ? w(n2, t2 === l) : "", h2 = wrapAnsi(o2, i, {
11670
+ const n3 = e[t2], o2 = n3 ? w(n3, t2 === l) : "", h2 = wrapAnsi(o2, i, {
11637
11671
  hard: true,
11638
11672
  trim: false
11639
11673
  }).split(`
@@ -11641,17 +11675,17 @@ var limitOptions = ({
11641
11675
  s.push(h2), g2 += h2.length;
11642
11676
  }
11643
11677
  if (g2 > v) {
11644
- let t2 = 0, n2 = 0, o2 = g2;
11678
+ let t2 = 0, n3 = 0, o2 = g2;
11645
11679
  const h2 = l - T3;
11646
11680
  let u3 = v;
11647
11681
  const L = () => I(s, o2, 0, h2, u3), E = () => I(s, o2, h2 + 1, s.length, u3, true);
11648
- 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));
11682
+ 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));
11649
11683
  }
11650
11684
  const x = [];
11651
11685
  d && x.push(M2);
11652
11686
  for (const t2 of s)
11653
- for (const n2 of t2)
11654
- x.push(n2);
11687
+ for (const n3 of t2)
11688
+ x.push(n3);
11655
11689
  return c2 && x.push(M2), x;
11656
11690
  };
11657
11691
  var confirm = (i) => {
@@ -11718,58 +11752,58 @@ ${styleText2("reset", styleText2("dim", `Press ${styleText2(["gray", "bgWhite",
11718
11752
  render() {
11719
11753
  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)}
11720
11754
  ` : ""}${a2}
11721
- `, o2 = this.value ?? [], p2 = (n2, l) => {
11722
- if (n2.disabled)
11723
- return u3(n2, "disabled");
11724
- const s = o2.includes(n2.value);
11725
- return l && s ? u3(n2, "active-selected") : s ? u3(n2, "selected") : u3(n2, l ? "active" : "inactive");
11755
+ `, o2 = this.value ?? [], p2 = (n3, l) => {
11756
+ if (n3.disabled)
11757
+ return u3(n3, "disabled");
11758
+ const s = o2.includes(n3.value);
11759
+ return l && s ? u3(n3, "active-selected") : s ? u3(n3, "selected") : u3(n3, l ? "active" : "inactive");
11726
11760
  };
11727
11761
  switch (this.state) {
11728
11762
  case "submit": {
11729
- 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)} ` : "");
11763
+ 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)} ` : "");
11730
11764
  return `${r2}${l}`;
11731
11765
  }
11732
11766
  case "cancel": {
11733
- const n2 = this.options.filter(({ value: s }) => o2.includes(s)).map((s) => u3(s, "cancelled")).join(styleText2("dim", ", "));
11734
- if (n2.trim() === "")
11767
+ const n3 = this.options.filter(({ value: s }) => o2.includes(s)).map((s) => u3(s, "cancelled")).join(styleText2("dim", ", "));
11768
+ if (n3.trim() === "")
11735
11769
  return `${r2}${styleText2("gray", S_BAR)}`;
11736
- const l = wrapTextWithPrefix(i.output, n2, t2 ? `${styleText2("gray", S_BAR)} ` : "");
11770
+ const l = wrapTextWithPrefix(i.output, n3, t2 ? `${styleText2("gray", S_BAR)} ` : "");
11737
11771
  return `${r2}${l}${t2 ? `
11738
11772
  ${styleText2("gray", S_BAR)}` : ""}`;
11739
11773
  }
11740
11774
  case "error": {
11741
- const n2 = t2 ? `${styleText2("yellow", S_BAR)} ` : "", l = this.error.split(`
11775
+ const n3 = t2 ? `${styleText2("yellow", S_BAR)} ` : "", l = this.error.split(`
11742
11776
  `).map(($, C2) => C2 === 0 ? `${t2 ? `${styleText2("yellow", S_BAR_END)} ` : ""}${styleText2("yellow", $)}` : ` ${$}`).join(`
11743
11777
  `), s = r2.split(`
11744
11778
  `).length, h2 = l.split(`
11745
11779
  `).length + 1;
11746
- return `${r2}${n2}${limitOptions({
11780
+ return `${r2}${n3}${limitOptions({
11747
11781
  output: i.output,
11748
11782
  options: this.options,
11749
11783
  cursor: this.cursor,
11750
11784
  maxItems: i.maxItems,
11751
- columnPadding: n2.length,
11785
+ columnPadding: n3.length,
11752
11786
  rowPadding: s + h2,
11753
11787
  style: p2
11754
11788
  }).join(`
11755
- ${n2}`)}
11789
+ ${n3}`)}
11756
11790
  ${l}
11757
11791
  `;
11758
11792
  }
11759
11793
  default: {
11760
- const n2 = t2 ? `${styleText2("cyan", S_BAR)} ` : "", l = r2.split(`
11794
+ const n3 = t2 ? `${styleText2("cyan", S_BAR)} ` : "", l = r2.split(`
11761
11795
  `).length, s = v ? formatInstructionFooter(MULTISELECT_INSTRUCTIONS, t2) : t2 ? [styleText2("cyan", S_BAR_END)] : [], h2 = s.join(`
11762
11796
  `), $ = s.length + 1;
11763
- return `${r2}${n2}${limitOptions({
11797
+ return `${r2}${n3}${limitOptions({
11764
11798
  output: i.output,
11765
11799
  options: this.options,
11766
11800
  cursor: this.cursor,
11767
11801
  maxItems: i.maxItems,
11768
- columnPadding: n2.length,
11802
+ columnPadding: n3.length,
11769
11803
  rowPadding: l + $,
11770
11804
  style: p2
11771
11805
  }).join(`
11772
- ${n2}`)}
11806
+ ${n3}`)}
11773
11807
  ${h2}
11774
11808
  `;
11775
11809
  }
@@ -11801,18 +11835,18 @@ var C2 = (o2, e, s) => {
11801
11835
  hard: true,
11802
11836
  trim: false
11803
11837
  }, i = wrapAnsi(o2, e, a2).split(`
11804
- `), 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);
11838
+ `), 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);
11805
11839
  return wrapAnsi(o2, g2, a2);
11806
11840
  };
11807
11841
  var note = (o2 = "", e = "", s) => {
11808
11842
  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(`
11809
- `).map(c2), ""], n2 = dist_default2(e), t2 = Math.max(g2.reduce((m3, F) => {
11843
+ `).map(c2), ""], n3 = dist_default2(e), t2 = Math.max(g2.reduce((m3, F) => {
11810
11844
  const O = dist_default2(F);
11811
11845
  return O > m3 ? O : m3;
11812
- }, 0), n2) + 2, h2 = g2.map((m3) => `${styleText2("gray", S_BAR)} ${m3}${" ".repeat(t2 - dist_default2(m3))}${styleText2("gray", S_BAR)}`).join(`
11846
+ }, 0), n3) + 2, h2 = g2.map((m3) => `${styleText2("gray", S_BAR)} ${m3}${" ".repeat(t2 - dist_default2(m3))}${styleText2("gray", S_BAR)}`).join(`
11813
11847
  `), T3 = i ? `${styleText2("gray", S_BAR)}
11814
11848
  ` : "", l$1 = i ? S_CONNECT_LEFT : S_CORNER_BOTTOM_LEFT;
11815
- 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)}
11849
+ 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)}
11816
11850
  ${h2}
11817
11851
  ${styleText2("gray", l$1 + S_BAR_H.repeat(t2 + 2) + S_CORNER_BOTTOM_RIGHT)}
11818
11852
  `);
@@ -11826,6 +11860,69 @@ var SELECT_INSTRUCTIONS = [
11826
11860
  `${styleText2("dim", "↑/↓")} to navigate`,
11827
11861
  `${styleText2("dim", "Enter:")} confirm`
11828
11862
  ];
11863
+ var c2 = (t2, o2) => t2.includes(`
11864
+ `) ? t2.split(`
11865
+ `).map((d) => o2(d)).join(`
11866
+ `) : o2(t2);
11867
+ var select = (t2) => {
11868
+ const o2 = (n3, m3) => {
11869
+ if (n3 === undefined)
11870
+ return "";
11871
+ const s = n3.label ?? String(n3.value);
11872
+ switch (m3) {
11873
+ case "disabled":
11874
+ return `${styleText2("gray", S_RADIO_INACTIVE)} ${c2(s, (i) => styleText2("gray", i))}${n3.hint ? ` ${styleText2("dim", `(${n3.hint ?? "disabled"})`)}` : ""}`;
11875
+ case "selected":
11876
+ return `${c2(s, (i) => styleText2("dim", i))}`;
11877
+ case "active":
11878
+ return `${styleText2("green", S_RADIO_ACTIVE)} ${s}${n3.hint ? ` ${styleText2("dim", `(${n3.hint})`)}` : ""}`;
11879
+ case "cancelled":
11880
+ return `${c2(s, (i) => styleText2(["strikethrough", "dim"], i))}`;
11881
+ default:
11882
+ return `${styleText2("dim", S_RADIO_INACTIVE)} ${c2(s, (i) => styleText2("dim", i))}`;
11883
+ }
11884
+ }, d = t2.showInstructions ?? true;
11885
+ return new n$1({
11886
+ options: t2.options,
11887
+ signal: t2.signal,
11888
+ input: t2.input,
11889
+ output: t2.output,
11890
+ initialValue: t2.initialValue,
11891
+ render() {
11892
+ 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)}
11893
+ ` : ""}${i}
11894
+ `;
11895
+ switch (this.state) {
11896
+ case "submit": {
11897
+ const r2 = n3 ? `${styleText2("gray", S_BAR)} ` : "", a2 = wrapTextWithPrefix(t2.output, o2(this.options[this.cursor], "selected"), r2);
11898
+ return `${u4}${a2}`;
11899
+ }
11900
+ case "cancel": {
11901
+ const r2 = n3 ? `${styleText2("gray", S_BAR)} ` : "", a2 = wrapTextWithPrefix(t2.output, o2(this.options[this.cursor], "cancelled"), r2);
11902
+ return `${u4}${a2}${n3 ? `
11903
+ ${styleText2("gray", S_BAR)}` : ""}`;
11904
+ }
11905
+ default: {
11906
+ const r2 = n3 ? `${styleText2("cyan", S_BAR)} ` : "", a2 = u4.split(`
11907
+ `).length, p2 = d ? formatInstructionFooter(SELECT_INSTRUCTIONS, n3) : n3 ? [styleText2("cyan", S_BAR_END)] : [], b2 = p2.join(`
11908
+ `), f2 = p2.length + 1;
11909
+ return `${u4}${r2}${limitOptions({
11910
+ output: t2.output,
11911
+ cursor: this.cursor,
11912
+ options: this.options,
11913
+ maxItems: t2.maxItems,
11914
+ columnPadding: r2.length,
11915
+ rowPadding: a2 + f2,
11916
+ style: (g2, x) => o2(g2, g2.disabled ? "disabled" : x ? "active" : "inactive")
11917
+ }).join(`
11918
+ ${r2}`)}
11919
+ ${b2}
11920
+ `;
11921
+ }
11922
+ }
11923
+ }
11924
+ }).prompt();
11925
+ };
11829
11926
  var i = `${styleText2("gray", S_BAR)} `;
11830
11927
 
11831
11928
  // src/market.ts
@@ -11836,7 +11933,7 @@ import { openSync, writeSync } from "node:fs";
11836
11933
  import { ReadStream } from "node:tty";
11837
11934
  var QUERY_CODES = ["10", "11", "12", "17", ...Array.from({ length: 16 }, (_2, i2) => `4;${i2}`)];
11838
11935
  function paletteOsc(entry) {
11839
- const osc4 = entry.ansi.map((c2, i2) => `;${i2};${c2}`).join("");
11936
+ const osc4 = entry.ansi.map((c3, i2) => `;${i2};${c3}`).join("");
11840
11937
  return [
11841
11938
  `\x1B]11;${entry.background}\x1B\\`,
11842
11939
  `\x1B]10;${entry.foreground}\x1B\\`,
@@ -11892,7 +11989,7 @@ async function queryTerminalColors() {
11892
11989
  // src/ansi.ts
11893
11990
  function rgb2(hex2) {
11894
11991
  const h2 = hex2.replace("#", "");
11895
- return [h2.slice(0, 2), h2.slice(2, 4), h2.slice(4, 6)].map((c2) => Number.parseInt(c2, 16)).join(";");
11992
+ return [h2.slice(0, 2), h2.slice(2, 4), h2.slice(4, 6)].map((c3) => Number.parseInt(c3, 16)).join(";");
11896
11993
  }
11897
11994
  function ansiChip(text, bg2) {
11898
11995
  return `\x1B[48;2;${rgb2(bg2)}m ${text} \x1B[0m`;
@@ -11904,7 +12001,7 @@ function ansiFg(color2) {
11904
12001
  return `\x1B[38;2;${rgb2(color2)}m`;
11905
12002
  }
11906
12003
  function ansiSwatch(colors, bg2) {
11907
- return `\x1B[48;2;${rgb2(bg2)}m${colors.map((c2) => `\x1B[38;2;${rgb2(c2)}m▄`).join("")} \x1B[0m`;
12004
+ return `\x1B[48;2;${rgb2(bg2)}m${colors.map((c3) => `\x1B[38;2;${rgb2(c3)}m▄`).join("")} \x1B[0m`;
11908
12005
  }
11909
12006
 
11910
12007
  // src/palette-prompt.ts
@@ -12048,12 +12145,12 @@ class PalettePrompt extends V {
12048
12145
  if (names.length === 0) {
12049
12146
  return;
12050
12147
  }
12051
- const add = names.some((n2) => !this.picked.has(n2));
12052
- for (const n2 of names) {
12148
+ const add = names.some((n3) => !this.picked.has(n3));
12149
+ for (const n3 of names) {
12053
12150
  if (add) {
12054
- this.picked.add(n2);
12151
+ this.picked.add(n3);
12055
12152
  } else {
12056
- this.picked.delete(n2);
12153
+ this.picked.delete(n3);
12057
12154
  }
12058
12155
  }
12059
12156
  }
@@ -12240,8 +12337,8 @@ function runAdd(names) {
12240
12337
  const home = configHome();
12241
12338
  const catalog = readCatalog(home);
12242
12339
  const state = readInstalled(home);
12243
- const already = names.filter((n2) => state.palettes.includes(n2));
12244
- const fresh = names.filter((n2) => !state.palettes.includes(n2));
12340
+ const already = names.filter((n3) => state.palettes.includes(n3));
12341
+ const fresh = names.filter((n3) => !state.palettes.includes(n3));
12245
12342
  if (fresh.length === 0) {
12246
12343
  console.log(`already installed: ${already.join(", ")}`);
12247
12344
  return;
@@ -12258,12 +12355,12 @@ function runRemove(names) {
12258
12355
  const home = configHome();
12259
12356
  const catalog = readCatalog(home);
12260
12357
  const state = readInstalled(home);
12261
- const gone = names.filter((n2) => state.palettes.includes(n2));
12358
+ const gone = names.filter((n3) => state.palettes.includes(n3));
12262
12359
  if (gone.length === 0) {
12263
12360
  console.log(`not installed: ${names.join(", ")}`);
12264
12361
  return;
12265
12362
  }
12266
- const next = { ...state, palettes: state.palettes.filter((n2) => !gone.includes(n2)) };
12363
+ const next = { ...state, palettes: state.palettes.filter((n3) => !gone.includes(n3)) };
12267
12364
  sync(home, catalog, next);
12268
12365
  forget(home, catalog, state.terminals, gone);
12269
12366
  writeInstalled(home, next);
@@ -12279,7 +12376,8 @@ function runDefault(name) {
12279
12376
  if (!state.palettes.includes(name)) {
12280
12377
  throw new Error(`${name} is not installed — \`ttheme add ${name}\` first`);
12281
12378
  }
12282
- const next = { ...state, startup: name };
12379
+ const { keepTheme: _2, ...rest } = state;
12380
+ const next = { ...rest, startup: name };
12283
12381
  sync(home, catalog, next);
12284
12382
  writeInstalled(home, next);
12285
12383
  console.log(`new tabs open with ${name} — reload your terminal config to pick it up`);
@@ -12335,8 +12433,8 @@ async function runBrowse() {
12335
12433
  console.log("nothing changed");
12336
12434
  return;
12337
12435
  }
12338
- const dropped = state.palettes.filter((n2) => !wanted.includes(n2));
12339
- const added = wanted.filter((n2) => !state.palettes.includes(n2));
12436
+ const dropped = state.palettes.filter((n3) => !wanted.includes(n3));
12437
+ const added = wanted.filter((n3) => !state.palettes.includes(n3));
12340
12438
  if (added.length === 0 && dropped.length === 0) {
12341
12439
  console.log("nothing changed");
12342
12440
  return;
@@ -12375,9 +12473,10 @@ function planInit(opts, paths) {
12375
12473
  copyDir(copies, join8(paths.root, "shell", "adapters"), join8(home, "adapters"));
12376
12474
  const edits = [{ file: join8(paths.zdotdir, ".zshrc"), block: zshrcBlock(home) }];
12377
12475
  const configPath = join8(home, "config.zsh");
12476
+ const seeded = configFile(existsSync5(configPath) ? readFileSync7(configPath, "utf8") : "");
12378
12477
  const settings2 = {
12379
12478
  file: configPath,
12380
- content: configFile(existsSync5(configPath) ? readFileSync7(configPath, "utf8") : "")
12479
+ content: opts.wear ? withSetting(seeded, "TTHEME_TAB_PALETTE", opts.wear === "rotate" ? "seq" : "off") : seeded
12381
12480
  };
12382
12481
  const notes = [];
12383
12482
  if (opts.terminals.includes("ghostty")) {
@@ -12390,16 +12489,20 @@ function planInit(opts, paths) {
12390
12489
  }
12391
12490
  }
12392
12491
  notes.push("wezterm and iterm2: import the palettes you install from the release archive");
12393
- const installed = { terminals: opts.terminals, palettes: opts.palettes };
12492
+ const installed = {
12493
+ terminals: opts.terminals,
12494
+ palettes: opts.palettes,
12495
+ ...opts.wear === "keep" ? { keepTheme: true } : {}
12496
+ };
12394
12497
  return { copies, edits, settings: settings2, catalog: loadManifest(paths.root), installed, notes };
12395
12498
  }
12396
12499
  function applyInit(plan) {
12397
- for (const c2 of plan.copies) {
12398
- mkdirSync6(dirname5(c2.to), { recursive: true });
12399
- rmSync6(c2.to, { force: true });
12400
- copyFileSync(c2.from, c2.to);
12401
- if (c2.executable) {
12402
- chmodSync(c2.to, 493);
12500
+ for (const c3 of plan.copies) {
12501
+ mkdirSync6(dirname5(c3.to), { recursive: true });
12502
+ rmSync6(c3.to, { force: true });
12503
+ copyFileSync(c3.from, c3.to);
12504
+ if (c3.executable) {
12505
+ chmodSync(c3.to, 493);
12403
12506
  }
12404
12507
  }
12405
12508
  mkdirSync6(dirname5(plan.settings.file), { recursive: true });
@@ -12429,9 +12532,26 @@ async function askTerminals(detected, preselected) {
12429
12532
  required: true
12430
12533
  }));
12431
12534
  }
12535
+ function wearSummary(wear, startup) {
12536
+ if (wear === "keep") {
12537
+ return "leave the terminal colors as they are";
12538
+ }
12539
+ return wear === "rotate" ? `paint every new tab with the next palette, this tab with ${startup} now` : `paint every tab with ${startup}, this one now`;
12540
+ }
12541
+ async function askWear(startup) {
12542
+ return accepted(await select({
12543
+ message: "how should the terminal wear them?",
12544
+ options: [
12545
+ { value: "default", label: "default", hint: `every tab wears ${startup}` },
12546
+ { value: "rotate", label: "rotate", hint: "every new tab wears the next palette" },
12547
+ { value: "keep", label: "keep", hint: "leave my terminal colors alone" }
12548
+ ],
12549
+ initialValue: "default"
12550
+ }));
12551
+ }
12432
12552
  function verify(plan) {
12433
12553
  const missing = [
12434
- ...plan.copies.filter((c2) => !existsSync5(c2.to)).map((c2) => c2.to),
12554
+ ...plan.copies.filter((c3) => !existsSync5(c3.to)).map((c3) => c3.to),
12435
12555
  ...existsSync5(plan.settings.file) ? [] : [plan.settings.file],
12436
12556
  ...plan.edits.filter((e) => !readFileSync7(e.file, "utf8").includes("# ttheme begin")).map((e) => e.file)
12437
12557
  ];
@@ -12453,13 +12573,23 @@ function paintStartup(catalog, installed) {
12453
12573
  process.stdout.write(paletteOsc(startup));
12454
12574
  return true;
12455
12575
  }
12456
- function receipt(plan, opts, painted) {
12576
+ function wearLines(wear, startup, painted) {
12577
+ if (wear === "keep") {
12578
+ return [
12579
+ "terminal colors left alone — paint a tab with `ttheme <palette>`",
12580
+ " `ttheme default <palette>` sets the terminal default"
12581
+ ];
12582
+ }
12583
+ return [
12584
+ `default ${startup}${painted ? " — this tab wears it already" : ""}`,
12585
+ wear === "rotate" ? "new tabs rotate through the palettes — change with `ttheme config`" : `new tabs all wear ${startup} — change with \`ttheme config\``
12586
+ ];
12587
+ }
12588
+ function receipt(plan, opts, wear, painted) {
12457
12589
  const series = seriesOf(plan.catalog, opts.palettes);
12458
- const startup = startupPalette(plan.installed);
12459
12590
  note([
12460
12591
  `${series.join(", ")} (${opts.palettes.length})`,
12461
- `startup ${startup}${painted ? " — this tab wears it already" : ""}`,
12462
- "new tabs rotate through the palettes — change with `ttheme config`"
12592
+ ...wearLines(wear, startupPalette(plan.installed), painted)
12463
12593
  ].join(`
12464
12594
  `), `installed ${opts.palettes.length} palettes`);
12465
12595
  const next = ["exec zsh the ttheme command in this tab"];
@@ -12526,13 +12656,16 @@ async function runInit(flags = {}) {
12526
12656
  cancel("nothing changed");
12527
12657
  process.exit(1);
12528
12658
  }
12529
- const opts = { terminals, palettes };
12659
+ const startup = startupPalette({ terminals, palettes });
12660
+ const wear = await askWear(startup);
12661
+ const opts = { terminals, palettes, wear };
12530
12662
  const plan = planInit(opts, paths);
12531
12663
  note([
12532
12664
  `install ${palettes.length} palettes — ${seriesOf(plan.catalog, palettes).join(", ")}`,
12533
12665
  `copy ${plan.copies.length} files under ${configHome2}`,
12534
12666
  `write ${plan.settings.file}`,
12535
- ...plan.edits.map((e) => `edit ${e.file}`)
12667
+ ...plan.edits.map((e) => `edit ${e.file}`),
12668
+ wearSummary(wear, startup)
12536
12669
  ].join(`
12537
12670
  `), `wiring ${terminals.join(", ")}`);
12538
12671
  if (!accepted(await confirm({ message: "apply these changes?" }))) {
@@ -12541,7 +12674,7 @@ async function runInit(flags = {}) {
12541
12674
  }
12542
12675
  applyInit(plan);
12543
12676
  verify(plan);
12544
- receipt(plan, opts, paintStartup(plan.catalog, plan.installed));
12677
+ receipt(plan, opts, wear, wear !== "keep" && paintStartup(plan.catalog, plan.installed));
12545
12678
  }
12546
12679
 
12547
12680
  // src/cli.ts
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.1.4",
2
+ "version": "0.1.5",
3
3
  "gate": [
4
4
  {
5
5
  "rule": "foreground",
@@ -1,13 +1,13 @@
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
4
+ typeset -g TTHEME_VERSION=0.1.5
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
8
8
 
9
- # new tabs: seq rotates through the palettes, off keeps the configured terminal theme (default seq)
10
- : ${TTHEME_TAB_PALETTE:=seq}
9
+ # new tabs: off keeps the configured terminal theme, seq rotates through the palettes (default off)
10
+ : ${TTHEME_TAB_PALETTE:=off}
11
11
 
12
12
  # the palette notice under "Last login:": 1 shows it, 0 silences it (default 1)
13
13
  : ${TTHEME_ANNOUNCE:=1}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kecan0406/ttheme",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "ttheme": "bin/ttheme.js"
@@ -2,7 +2,7 @@
2
2
 
3
3
  source ${0:A:h}/ttheme.zsh
4
4
 
5
- if [[ ${TTHEME_TAB_PALETTE:-seq} != off ]]; then
5
+ if [[ ${TTHEME_TAB_PALETTE:-off} != off ]]; then
6
6
  __tt_next
7
7
  __tt_apply "$REPLY"
8
8
  export TTHEME_SPEC=$REPLY
package/shell/ttheme.zsh CHANGED
@@ -36,7 +36,7 @@ __tt_pins_load() {
36
36
 
37
37
  __tt_pins_load
38
38
 
39
- : ${TTHEME_TAB_PALETTE:=seq}
39
+ : ${TTHEME_TAB_PALETTE:=off}
40
40
 
41
41
  : ${TTHEME_ANNOUNCE:=1}
42
42
 
@@ -143,6 +143,7 @@ __tt_dir_sync() {
143
143
  spec=${TTHEME_PALETTE[$TTHEME_PINS[$REPLY]]}
144
144
  elif [[ $TTHEME_SPEC == "$TTHEME_PIN_SPEC" ]]; then
145
145
  spec=$TTHEME_BASE_SPEC
146
+ [[ -n $spec ]] || { __tt_osc_reset; TTHEME_SPEC= }
146
147
  fi
147
148
  TTHEME_PIN=$REPLY TTHEME_PIN_SPEC=$spec
148
149
  [[ -n $spec && $spec != "$TTHEME_SPEC" ]] || return 0
@@ -225,11 +226,16 @@ __tt_unpin() {
225
226
 
226
227
  __tt_keep() {
227
228
  __tt_cli default "$1" > /dev/null || return 1
228
- __tt_reload
229
+ local note="default · new tabs open with $1"
230
+ if (( $+functions[__tt_reload] )); then
231
+ __tt_reload
232
+ else
233
+ note+=" — reload your terminal config to pick it up"
234
+ fi
229
235
  if __tt_color; then
230
- printf '\033[2mdefault · new tabs open with %s\033[0m\n' "$1"
236
+ printf '\033[2m%s\033[0m\n' "$note"
231
237
  else
232
- print -r -- "default · new tabs open with $1"
238
+ print -r -- "$note"
233
239
  fi
234
240
  }
235
241
 
@@ -281,6 +287,7 @@ __tt_help() {
281
287
  ttheme homura paint this tab (a unique prefix works: ttheme ho)
282
288
  ttheme preview browse live — focus repaints, enter keeps (this tab or default), esc restores, ? lists keys
283
289
  ttheme next advance this tab to the next palette
290
+ ttheme default make a palette the one new tabs open with
284
291
  ttheme pin pick a palette for this directory — cd into it repaints, cd out restores
285
292
  ttheme unpin drop the palette pinned to this directory
286
293
  ttheme config edit settings in $EDITOR — they apply in new tabs
@@ -383,10 +390,6 @@ __tt_next() {
383
390
  }
384
391
 
385
392
  __tt_rotate() {
386
- if [[ $TTHEME_TAB_PALETTE == off ]]; then
387
- print -u2 "ttheme next: TTHEME_TAB_PALETTE is off"
388
- return 1
389
- fi
390
393
  local REPLY
391
394
  __tt_next
392
395
  __tt_apply "$REPLY"
@@ -583,9 +586,9 @@ __tt_pv_head() {
583
586
 
584
587
  __tt_pv_foot() {
585
588
  setopt localoptions extendedglob
586
- local b=$'\e[1m' d=$'\e[2m' z=$'\e[0m' y=$'\e[33m' on=$'\e[7;1m'$ac badge="" lead="" note="" right="" line plain
589
+ local b=$'\e[1m' d=$'\e[2m' z=$'\e[0m' y=$'\e[33m' on=$'\e[7;1m'$ac badge="" lead="" note="" right="" text line plain
587
590
  local -a kk=() kl=() seg=()
588
- local -i end=$1 i lwid rwid
591
+ local -i end=$1 i lwid rwid room
589
592
  (( color )) || b= d= z= y= on=
590
593
  if (( help )); then
591
594
  badge=KEYS right=$b"? esc"$z$d" close"$z
@@ -617,9 +620,9 @@ __tt_pv_foot() {
617
620
  fi
618
621
  kk+=(space '=' enter)
619
622
  if (( bgoff[$tune] )); then kl+=(show default keep); else kl+=(hide default keep); fi
620
- __tt_pv_bg_findable $tune && { kk+=(f); kl+=(find) }
621
623
  __tt_pv_bg_images $tune
622
624
  (( REPLY > 1 )) && { kk+=(', .' D); kl+=("image ×$REPLY" remove) }
625
+ __tt_pv_bg_findable $tune && { kk+=(f); kl+=(find) }
623
626
  right=$b"esc"$z$d" undo"$z
624
627
  elif (( conf )); then
625
628
  badge=CONFIG kk=(↑↓ ←→ enter) kl=(setting value save)
@@ -653,7 +656,13 @@ __tt_pv_foot() {
653
656
  fi
654
657
  fi
655
658
  if (( msgt > 0 )) && [[ -z $pick ]]; then
656
- lead=$y$msg$z note="" kk=() kl=()
659
+ text=$msg
660
+ room=$(( end - (${#badge} ? ${#badge} + 4 : 0) ))
661
+ if (( ${(m)#text} > room )); then
662
+ while [[ -n $text ]] && (( ${(m)#text} > room - 1 )); do text=${text[1,-2]}; done
663
+ text+=…
664
+ fi
665
+ lead=$y$text$z note="" kk=() kl=()
657
666
  fi
658
667
  while :; do
659
668
  seg=()
@@ -1349,7 +1358,7 @@ __tt_preview() {
1349
1358
  local conf=0 cf=1
1350
1359
  local -a plabel=(" this tab " " default ") csnap=()
1351
1360
  local -a cvars=(TTHEME_TAB_PALETTE TTHEME_ANNOUNCE TTHEME_FX TTHEME_SORT) clabel=("new tabs" announce "search fx" sort)
1352
- local -a cchoice=("seq off" "1 0" "typewriter decode glitch" "abc series") cshow=("seq off" "on off" "typewriter decode glitch" "abc series")
1361
+ local -a cchoice=("off seq" "1 0" "typewriter decode glitch" "abc series") cshow=("off seq" "on off" "typewriter decode glitch" "abc series")
1353
1362
  local -A cnote=(
1354
1363
  seq "new tabs rotate through palettes" off "new tabs keep the terminal theme"
1355
1364
  1 "shows the palette notice" 0 "silences the palette notice"
@@ -1426,6 +1435,12 @@ ttheme() {
1426
1435
 
1427
1436
  case $1 in
1428
1437
  next) __tt_rotate; return ;;
1438
+ default)
1439
+ [[ -n $2 ]] || { print -u2 "ttheme default: name a palette — see \`ttheme help\`"; return 1 }
1440
+ local REPLY
1441
+ __tt_resolve "$2" || return 1
1442
+ __tt_keep "$REPLY"
1443
+ return ;;
1429
1444
  preview) __tt_preview; return ;;
1430
1445
  pin) __tt_preview pin; return ;;
1431
1446
  unpin) __tt_unpin; return ;;
@@ -1442,7 +1457,7 @@ ttheme() {
1442
1457
 
1443
1458
  if (( $+functions[compdef] )); then
1444
1459
  __tt_complete() {
1445
- (( CURRENT == 2 )) && compadd -- preview next pin unpin config help browse list add remove update
1460
+ (( CURRENT == 2 )) && compadd -- preview next default pin unpin config help browse list add remove update
1446
1461
  compadd -- $TTHEME_ORDER
1447
1462
  }
1448
1463
  compdef __tt_complete ttheme
@@ -1458,9 +1473,6 @@ if __tt_active; then
1458
1473
  for k in ${(k)TTHEME_PALETTE}; do
1459
1474
  [[ ${TTHEME_PALETTE[$k]%% *} == "$REPLY" ]] && { TTHEME_SPEC=$TTHEME_PALETTE[$k]; break }
1460
1475
  done
1461
- [[ -n $TTHEME_SPEC ]] || TTHEME_SPEC="$REPLY ${TTHEME_PALETTE[neutral]#* }"
1462
- else
1463
- TTHEME_SPEC="- ${TTHEME_PALETTE[neutral]#* }"
1464
1476
  fi
1465
1477
  else
1466
1478
  __tt_next