tuidoscope 0.1.13 → 0.1.14

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.
Files changed (3) hide show
  1. package/README.md +14 -0
  2. package/dist/index.js +631 -99
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -26,6 +26,16 @@ A centralized TUI management application for running multiple TUI applications i
26
26
  - **Highly Configurable**: Customize themes, keybinds, and application lists via YAML.
27
27
  - **Path Expansion**: Supports `~` and `<CONFIG_DIR>` tokens in working directory paths.
28
28
 
29
+ ## Documentation
30
+
31
+ For detailed guides, see the [`docs/`](./docs/) directory:
32
+
33
+ - [Getting Started](./docs/getting-started.md) - Installation and first run
34
+ - [Configuration](./docs/configuration.md) - YAML config reference
35
+ - [Keybindings](./docs/keybindings.md) - Leader key system and shortcuts
36
+ - [Apps](./docs/apps.md) - App configuration examples
37
+ - [Troubleshooting](./docs/troubleshooting.md) - Common issues and solutions
38
+
29
39
  ## Tech Stack
30
40
 
31
41
  - **Runtime**: [Bun](https://bun.sh/)
@@ -135,6 +145,10 @@ theme:
135
145
  - Runtime application configuration (Add/Edit).
136
146
  - Path expansion for working directories.
137
147
 
148
+ ## Contributing
149
+
150
+ Contributions are welcome! Please feel free to open an issue or submit a pull request on [GitHub](https://github.com/shuv1337/tuidoscope).
151
+
138
152
  ## Acknowledgments
139
153
 
140
154
  This project is built directly on top of:
package/dist/index.js CHANGED
@@ -8892,89 +8892,6 @@ var TabList = (props) => {
8892
8892
  })();
8893
8893
  };
8894
8894
 
8895
- // src/components/TerminalPane.tsx
8896
- var TerminalPane = (props) => {
8897
- const contentWidth = () => Math.max(1, props.width - 2);
8898
- const contentHeight = () => Math.max(1, props.height - 3);
8899
- return (() => {
8900
- var _el$ = createElement("box");
8901
- setProp(_el$, "flexDirection", "column");
8902
- setProp(_el$, "flexGrow", 1);
8903
- setProp(_el$, "borderStyle", "single");
8904
- insert(_el$, createComponent2(Show, {
8905
- get when() {
8906
- return props.runningApp;
8907
- },
8908
- get fallback() {
8909
- return (() => {
8910
- var _el$2 = createElement("box"), _el$3 = createElement("text");
8911
- insertNode(_el$2, _el$3);
8912
- setProp(_el$2, "flexGrow", 1);
8913
- setProp(_el$2, "justifyContent", "center");
8914
- setProp(_el$2, "alignItems", "center");
8915
- insertNode(_el$3, createTextNode(`No app selected. Press Ctrl+T to add one.`));
8916
- effect((_$p) => setProp(_el$3, "fg", props.theme.muted, _$p));
8917
- return _el$2;
8918
- })();
8919
- },
8920
- children: (app) => (() => {
8921
- var _el$5 = createElement("box"), _el$6 = createElement("box"), _el$7 = createElement("text"), _el$8 = createElement("b"), _el$9 = createElement("text"), _el$0 = createTextNode(` (`), _el$10 = createTextNode(`)`), _el$11 = createElement("box"), _el$12 = createElement("ghostty-terminal");
8922
- insertNode(_el$5, _el$6);
8923
- insertNode(_el$5, _el$11);
8924
- setProp(_el$5, "flexDirection", "column");
8925
- setProp(_el$5, "flexGrow", 1);
8926
- insertNode(_el$6, _el$7);
8927
- insertNode(_el$6, _el$9);
8928
- setProp(_el$6, "height", 1);
8929
- setProp(_el$6, "flexDirection", "row");
8930
- insertNode(_el$7, _el$8);
8931
- insert(_el$8, () => app().entry.name);
8932
- insertNode(_el$9, _el$0);
8933
- insertNode(_el$9, _el$10);
8934
- insert(_el$9, () => app().status, _el$10);
8935
- insertNode(_el$11, _el$12);
8936
- setProp(_el$11, "overflow", "hidden");
8937
- setProp(_el$12, "showCursor", true);
8938
- effect((_p$) => {
8939
- var _v$3 = props.theme.accent, _v$4 = props.theme.muted, _v$5 = contentWidth(), _v$6 = contentHeight(), _v$7 = app().buffer, _v$8 = contentWidth(), _v$9 = contentHeight(), _v$0 = {
8940
- width: contentWidth(),
8941
- height: contentHeight()
8942
- };
8943
- _v$3 !== _p$.e && (_p$.e = setProp(_el$7, "fg", _v$3, _p$.e));
8944
- _v$4 !== _p$.t && (_p$.t = setProp(_el$9, "fg", _v$4, _p$.t));
8945
- _v$5 !== _p$.a && (_p$.a = setProp(_el$11, "width", _v$5, _p$.a));
8946
- _v$6 !== _p$.o && (_p$.o = setProp(_el$11, "height", _v$6, _p$.o));
8947
- _v$7 !== _p$.i && (_p$.i = setProp(_el$12, "ansi", _v$7, _p$.i));
8948
- _v$8 !== _p$.n && (_p$.n = setProp(_el$12, "cols", _v$8, _p$.n));
8949
- _v$9 !== _p$.s && (_p$.s = setProp(_el$12, "rows", _v$9, _p$.s));
8950
- _v$0 !== _p$.h && (_p$.h = setProp(_el$12, "style", _v$0, _p$.h));
8951
- return _p$;
8952
- }, {
8953
- e: undefined,
8954
- t: undefined,
8955
- a: undefined,
8956
- o: undefined,
8957
- i: undefined,
8958
- n: undefined,
8959
- s: undefined,
8960
- h: undefined
8961
- });
8962
- return _el$5;
8963
- })()
8964
- }));
8965
- effect((_p$) => {
8966
- var _v$ = props.height, _v$2 = props.isFocused ? props.theme.primary : props.theme.muted;
8967
- _v$ !== _p$.e && (_p$.e = setProp(_el$, "height", _v$, _p$.e));
8968
- _v$2 !== _p$.t && (_p$.t = setProp(_el$, "borderColor", _v$2, _p$.t));
8969
- return _p$;
8970
- }, {
8971
- e: undefined,
8972
- t: undefined
8973
- });
8974
- return _el$;
8975
- })();
8976
- };
8977
-
8978
8895
  // src/lib/keybinds.ts
8979
8896
  function parseKeybind(keybind) {
8980
8897
  const parts = keybind.toLowerCase().split("+");
@@ -9072,6 +8989,91 @@ function leaderKeyToSequence(leaderKey) {
9072
8989
  return null;
9073
8990
  }
9074
8991
 
8992
+ // src/components/TerminalPane.tsx
8993
+ var TerminalPane = (props) => {
8994
+ const contentWidth = () => Math.max(1, props.width - 2);
8995
+ const contentHeight = () => Math.max(1, props.height - 3);
8996
+ return (() => {
8997
+ var _el$ = createElement("box");
8998
+ setProp(_el$, "flexDirection", "column");
8999
+ setProp(_el$, "flexGrow", 1);
9000
+ setProp(_el$, "borderStyle", "single");
9001
+ insert(_el$, createComponent2(Show, {
9002
+ get when() {
9003
+ return props.runningApp;
9004
+ },
9005
+ get fallback() {
9006
+ return (() => {
9007
+ var _el$2 = createElement("box"), _el$3 = createElement("text"), _el$4 = createTextNode(`No app selected. Press `), _el$5 = createTextNode(` to add one.`);
9008
+ insertNode(_el$2, _el$3);
9009
+ setProp(_el$2, "flexGrow", 1);
9010
+ setProp(_el$2, "justifyContent", "center");
9011
+ setProp(_el$2, "alignItems", "center");
9012
+ insertNode(_el$3, _el$4);
9013
+ insertNode(_el$3, _el$5);
9014
+ insert(_el$3, () => formatLeaderKeybind(props.leaderKey, props.newTabBinding), _el$5);
9015
+ effect((_$p) => setProp(_el$3, "fg", props.theme.muted, _$p));
9016
+ return _el$2;
9017
+ })();
9018
+ },
9019
+ children: (app) => (() => {
9020
+ var _el$6 = createElement("box"), _el$7 = createElement("box"), _el$8 = createElement("text"), _el$9 = createElement("b"), _el$0 = createElement("text"), _el$1 = createTextNode(` (`), _el$11 = createTextNode(`)`), _el$12 = createElement("box"), _el$13 = createElement("ghostty-terminal");
9021
+ insertNode(_el$6, _el$7);
9022
+ insertNode(_el$6, _el$12);
9023
+ setProp(_el$6, "flexDirection", "column");
9024
+ setProp(_el$6, "flexGrow", 1);
9025
+ insertNode(_el$7, _el$8);
9026
+ insertNode(_el$7, _el$0);
9027
+ setProp(_el$7, "height", 1);
9028
+ setProp(_el$7, "flexDirection", "row");
9029
+ insertNode(_el$8, _el$9);
9030
+ insert(_el$9, () => app().entry.name);
9031
+ insertNode(_el$0, _el$1);
9032
+ insertNode(_el$0, _el$11);
9033
+ insert(_el$0, () => app().status, _el$11);
9034
+ insertNode(_el$12, _el$13);
9035
+ setProp(_el$12, "overflow", "hidden");
9036
+ setProp(_el$13, "showCursor", true);
9037
+ effect((_p$) => {
9038
+ var _v$3 = props.theme.accent, _v$4 = props.theme.muted, _v$5 = contentWidth(), _v$6 = contentHeight(), _v$7 = app().buffer, _v$8 = contentWidth(), _v$9 = contentHeight(), _v$0 = {
9039
+ width: contentWidth(),
9040
+ height: contentHeight()
9041
+ };
9042
+ _v$3 !== _p$.e && (_p$.e = setProp(_el$8, "fg", _v$3, _p$.e));
9043
+ _v$4 !== _p$.t && (_p$.t = setProp(_el$0, "fg", _v$4, _p$.t));
9044
+ _v$5 !== _p$.a && (_p$.a = setProp(_el$12, "width", _v$5, _p$.a));
9045
+ _v$6 !== _p$.o && (_p$.o = setProp(_el$12, "height", _v$6, _p$.o));
9046
+ _v$7 !== _p$.i && (_p$.i = setProp(_el$13, "ansi", _v$7, _p$.i));
9047
+ _v$8 !== _p$.n && (_p$.n = setProp(_el$13, "cols", _v$8, _p$.n));
9048
+ _v$9 !== _p$.s && (_p$.s = setProp(_el$13, "rows", _v$9, _p$.s));
9049
+ _v$0 !== _p$.h && (_p$.h = setProp(_el$13, "style", _v$0, _p$.h));
9050
+ return _p$;
9051
+ }, {
9052
+ e: undefined,
9053
+ t: undefined,
9054
+ a: undefined,
9055
+ o: undefined,
9056
+ i: undefined,
9057
+ n: undefined,
9058
+ s: undefined,
9059
+ h: undefined
9060
+ });
9061
+ return _el$6;
9062
+ })()
9063
+ }));
9064
+ effect((_p$) => {
9065
+ var _v$ = props.height, _v$2 = props.isFocused ? props.theme.primary : props.theme.muted;
9066
+ _v$ !== _p$.e && (_p$.e = setProp(_el$, "height", _v$, _p$.e));
9067
+ _v$2 !== _p$.t && (_p$.t = setProp(_el$, "borderColor", _v$2, _p$.t));
9068
+ return _p$;
9069
+ }, {
9070
+ e: undefined,
9071
+ t: undefined
9072
+ });
9073
+ return _el$;
9074
+ })();
9075
+ };
9076
+
9075
9077
  // src/components/StatusBar.tsx
9076
9078
  var StatusBar = (props) => {
9077
9079
  return (() => {
@@ -11214,6 +11216,39 @@ var APP_PRESETS = [
11214
11216
  category: "utility"
11215
11217
  }
11216
11218
  ];
11219
+ // src/components/onboarding/keybindingPresets.ts
11220
+ var LEADER_PRESETS = [
11221
+ {
11222
+ id: "tmux",
11223
+ key: "ctrl+a",
11224
+ name: "Ctrl+A",
11225
+ description: "tmux-style (recommended)"
11226
+ },
11227
+ {
11228
+ id: "tmux-alt",
11229
+ key: "ctrl+b",
11230
+ name: "Ctrl+B",
11231
+ description: "tmux alternate"
11232
+ },
11233
+ {
11234
+ id: "screen",
11235
+ key: "ctrl+\\",
11236
+ name: "Ctrl+\\",
11237
+ description: "GNU Screen style"
11238
+ },
11239
+ {
11240
+ id: "desktop",
11241
+ key: "alt+space",
11242
+ name: "Alt+Space",
11243
+ description: "Desktop-style"
11244
+ },
11245
+ {
11246
+ id: "custom",
11247
+ key: "",
11248
+ name: "Custom...",
11249
+ description: "Choose your own"
11250
+ }
11251
+ ];
11217
11252
  // src/components/onboarding/WelcomeStep.tsx
11218
11253
  var WelcomeStep = (props) => {
11219
11254
  useKeyboard((event) => {
@@ -11293,6 +11328,263 @@ var WelcomeStep = (props) => {
11293
11328
  return _el$;
11294
11329
  })();
11295
11330
  };
11331
+ // src/lib/key-capture.ts
11332
+ function eventToKeybindString(event) {
11333
+ const parts = [];
11334
+ if (event.ctrl)
11335
+ parts.push("ctrl");
11336
+ if (event.option)
11337
+ parts.push("alt");
11338
+ if (event.shift)
11339
+ parts.push("shift");
11340
+ if (event.meta)
11341
+ parts.push("meta");
11342
+ let keyName = event.name.toLowerCase();
11343
+ if (keyName === "return")
11344
+ keyName = "enter";
11345
+ if (keyName === " ")
11346
+ keyName = "space";
11347
+ parts.push(keyName);
11348
+ return parts.join("+");
11349
+ }
11350
+ function isValidLeaderKey(keybind) {
11351
+ const parts = keybind.toLowerCase().split("+");
11352
+ const key = parts[parts.length - 1];
11353
+ const hasCtrl = parts.includes("ctrl");
11354
+ const hasAlt = parts.includes("alt");
11355
+ const hasMeta = parts.includes("meta") || parts.includes("cmd");
11356
+ const hasModifier = hasCtrl || hasAlt || hasMeta;
11357
+ if (!hasModifier) {
11358
+ return false;
11359
+ }
11360
+ const reservedKeys = ["enter", "return", "escape", "tab", "backspace"];
11361
+ if (reservedKeys.includes(key)) {
11362
+ return false;
11363
+ }
11364
+ return true;
11365
+ }
11366
+
11367
+ // src/components/onboarding/KeybindingStep.tsx
11368
+ var KeybindingStep = (props) => {
11369
+ const [focusedIndex, setFocusedIndex] = createSignal(0);
11370
+ const [isCapturing, setIsCapturing] = createSignal(false);
11371
+ const [capturedKey, setCapturedKey] = createSignal("");
11372
+ const getSelectedIndex = () => {
11373
+ const idx = LEADER_PRESETS.findIndex((p) => p.key === props.selectedLeaderKey);
11374
+ return idx === -1 ? LEADER_PRESETS.length - 1 : idx;
11375
+ };
11376
+ const isSelected = (preset) => {
11377
+ if (preset.id === "custom") {
11378
+ return !LEADER_PRESETS.slice(0, -1).some((p) => p.key === props.selectedLeaderKey);
11379
+ }
11380
+ return preset.key === props.selectedLeaderKey;
11381
+ };
11382
+ useKeyboard((event) => {
11383
+ if (isCapturing()) {
11384
+ if (event.name === "escape") {
11385
+ setIsCapturing(false);
11386
+ setCapturedKey("");
11387
+ event.preventDefault();
11388
+ return;
11389
+ }
11390
+ if (event.name === "return" || event.name === "enter") {
11391
+ if (capturedKey() && isValidLeaderKey(capturedKey())) {
11392
+ props.onSelectLeader(capturedKey());
11393
+ setIsCapturing(false);
11394
+ setCapturedKey("");
11395
+ }
11396
+ event.preventDefault();
11397
+ return;
11398
+ }
11399
+ if (event.ctrl || event.option || event.meta) {
11400
+ const keybind = eventToKeybindString(event);
11401
+ if (isValidLeaderKey(keybind)) {
11402
+ setCapturedKey(keybind);
11403
+ }
11404
+ event.preventDefault();
11405
+ return;
11406
+ }
11407
+ event.preventDefault();
11408
+ return;
11409
+ }
11410
+ const maxIndex = LEADER_PRESETS.length - 1;
11411
+ if (event.name === "j" || event.name === "down") {
11412
+ setFocusedIndex((prev) => Math.min(prev + 1, maxIndex));
11413
+ event.preventDefault();
11414
+ return;
11415
+ }
11416
+ if (event.name === "k" || event.name === "up") {
11417
+ setFocusedIndex((prev) => Math.max(prev - 1, 0));
11418
+ event.preventDefault();
11419
+ return;
11420
+ }
11421
+ if (event.name === "return" || event.name === "enter" || event.name === "space") {
11422
+ const preset = LEADER_PRESETS[focusedIndex()];
11423
+ if (preset.id === "custom") {
11424
+ setIsCapturing(true);
11425
+ setCapturedKey("");
11426
+ } else {
11427
+ props.onSelectLeader(preset.key);
11428
+ if (event.name === "return" || event.name === "enter") {
11429
+ props.onNext();
11430
+ }
11431
+ }
11432
+ event.preventDefault();
11433
+ return;
11434
+ }
11435
+ if (event.name === "escape" || event.name === "backspace") {
11436
+ props.onBack();
11437
+ event.preventDefault();
11438
+ return;
11439
+ }
11440
+ });
11441
+ return (() => {
11442
+ var _el$ = createElement("box"), _el$2 = createElement("box"), _el$3 = createElement("text"), _el$4 = createElement("b"), _el$6 = createElement("box"), _el$7 = createElement("text"), _el$9 = createElement("box"), _el$0 = createElement("box"), _el$1 = createElement("text"), _el$11 = createElement("box");
11443
+ insertNode(_el$, _el$2);
11444
+ insertNode(_el$, _el$6);
11445
+ insertNode(_el$, _el$9);
11446
+ insertNode(_el$, _el$0);
11447
+ insertNode(_el$, _el$11);
11448
+ setProp(_el$, "width", "100%");
11449
+ setProp(_el$, "height", "100%");
11450
+ setProp(_el$, "flexDirection", "column");
11451
+ setProp(_el$, "justifyContent", "center");
11452
+ setProp(_el$, "alignItems", "center");
11453
+ insertNode(_el$2, _el$3);
11454
+ setProp(_el$2, "height", 1);
11455
+ insertNode(_el$3, _el$4);
11456
+ insertNode(_el$4, createTextNode(`Choose Your Leader Key`));
11457
+ insertNode(_el$6, _el$7);
11458
+ setProp(_el$6, "height", 1);
11459
+ insertNode(_el$7, createTextNode(`The leader key activates tuidoscope commands`));
11460
+ setProp(_el$9, "height", 1);
11461
+ insertNode(_el$0, _el$1);
11462
+ setProp(_el$0, "height", 1);
11463
+ insertNode(_el$1, createTextNode(`Example: Leader + n = next tab, Leader + q = quit`));
11464
+ setProp(_el$11, "height", 1);
11465
+ insert(_el$, (() => {
11466
+ var _c$ = memo2(() => !!isCapturing());
11467
+ return () => _c$() ? (() => {
11468
+ var _el$12 = createElement("box"), _el$13 = createElement("box"), _el$14 = createElement("text"), _el$15 = createElement("b"), _el$17 = createElement("box"), _el$18 = createElement("box"), _el$19 = createElement("text"), _el$20 = createTextNode(`Detected: `), _el$21 = createElement("b"), _el$22 = createElement("box"), _el$23 = createElement("box"), _el$24 = createElement("text");
11469
+ insertNode(_el$12, _el$13);
11470
+ insertNode(_el$12, _el$17);
11471
+ insertNode(_el$12, _el$18);
11472
+ insertNode(_el$12, _el$22);
11473
+ insertNode(_el$12, _el$23);
11474
+ setProp(_el$12, "flexDirection", "column");
11475
+ setProp(_el$12, "alignItems", "center");
11476
+ insertNode(_el$13, _el$14);
11477
+ setProp(_el$13, "height", 1);
11478
+ insertNode(_el$14, _el$15);
11479
+ insertNode(_el$15, createTextNode(`Press the key combination you want to use...`));
11480
+ setProp(_el$17, "height", 1);
11481
+ insertNode(_el$18, _el$19);
11482
+ setProp(_el$18, "height", 1);
11483
+ insertNode(_el$19, _el$20);
11484
+ insertNode(_el$19, _el$21);
11485
+ insert(_el$21, () => capturedKey() || "(waiting...)");
11486
+ setProp(_el$22, "height", 1);
11487
+ insertNode(_el$23, _el$24);
11488
+ setProp(_el$23, "height", 1);
11489
+ insert(_el$24, () => capturedKey() && isValidLeaderKey(capturedKey()) ? "Press Enter to confirm, Escape to cancel" : "Press a key with Ctrl, Alt, or Cmd modifier");
11490
+ effect((_p$) => {
11491
+ var _v$4 = props.theme.accent, _v$5 = props.theme.foreground, _v$6 = props.theme.muted;
11492
+ _v$4 !== _p$.e && (_p$.e = setProp(_el$14, "fg", _v$4, _p$.e));
11493
+ _v$5 !== _p$.t && (_p$.t = setProp(_el$19, "fg", _v$5, _p$.t));
11494
+ _v$6 !== _p$.a && (_p$.a = setProp(_el$24, "fg", _v$6, _p$.a));
11495
+ return _p$;
11496
+ }, {
11497
+ e: undefined,
11498
+ t: undefined,
11499
+ a: undefined
11500
+ });
11501
+ return _el$12;
11502
+ })() : [(() => {
11503
+ var _el$25 = createElement("box");
11504
+ setProp(_el$25, "flexDirection", "column");
11505
+ setProp(_el$25, "alignItems", "flex-start");
11506
+ insert(_el$25, createComponent2(For, {
11507
+ each: LEADER_PRESETS,
11508
+ children: (preset, index) => {
11509
+ const isFocused = () => focusedIndex() === index();
11510
+ const selected = () => isSelected(preset);
11511
+ return (() => {
11512
+ var _el$34 = createElement("box"), _el$35 = createElement("text"), _el$36 = createElement("text"), _el$37 = createElement("text"), _el$38 = createTextNode(` `), _el$39 = createElement("text"), _el$40 = createTextNode(` - `);
11513
+ insertNode(_el$34, _el$35);
11514
+ insertNode(_el$34, _el$36);
11515
+ insertNode(_el$34, _el$37);
11516
+ insertNode(_el$34, _el$39);
11517
+ setProp(_el$34, "height", 1);
11518
+ insert(_el$35, () => isFocused() ? "> " : " ");
11519
+ insert(_el$36, () => selected() ? "(*)" : "( )");
11520
+ insertNode(_el$37, _el$38);
11521
+ insert(_el$37, () => preset.name, null);
11522
+ insertNode(_el$39, _el$40);
11523
+ insert(_el$39, () => preset.description, null);
11524
+ effect((_p$) => {
11525
+ var _v$7 = isFocused() ? props.theme.accent : props.theme.muted, _v$8 = isFocused() ? props.theme.primary : props.theme.background, _v$9 = isFocused() ? props.theme.background : selected() ? "#22c55e" : props.theme.muted, _v$0 = isFocused() ? props.theme.primary : props.theme.background, _v$1 = isFocused() ? props.theme.background : props.theme.foreground, _v$10 = isFocused() ? props.theme.primary : props.theme.background, _v$11 = isFocused() ? props.theme.background : props.theme.muted, _v$12 = isFocused() ? props.theme.primary : props.theme.background;
11526
+ _v$7 !== _p$.e && (_p$.e = setProp(_el$35, "fg", _v$7, _p$.e));
11527
+ _v$8 !== _p$.t && (_p$.t = setProp(_el$35, "bg", _v$8, _p$.t));
11528
+ _v$9 !== _p$.a && (_p$.a = setProp(_el$36, "fg", _v$9, _p$.a));
11529
+ _v$0 !== _p$.o && (_p$.o = setProp(_el$36, "bg", _v$0, _p$.o));
11530
+ _v$1 !== _p$.i && (_p$.i = setProp(_el$37, "fg", _v$1, _p$.i));
11531
+ _v$10 !== _p$.n && (_p$.n = setProp(_el$37, "bg", _v$10, _p$.n));
11532
+ _v$11 !== _p$.s && (_p$.s = setProp(_el$39, "fg", _v$11, _p$.s));
11533
+ _v$12 !== _p$.h && (_p$.h = setProp(_el$39, "bg", _v$12, _p$.h));
11534
+ return _p$;
11535
+ }, {
11536
+ e: undefined,
11537
+ t: undefined,
11538
+ a: undefined,
11539
+ o: undefined,
11540
+ i: undefined,
11541
+ n: undefined,
11542
+ s: undefined,
11543
+ h: undefined
11544
+ });
11545
+ return _el$34;
11546
+ })();
11547
+ }
11548
+ }));
11549
+ return _el$25;
11550
+ })(), (() => {
11551
+ var _el$26 = createElement("box");
11552
+ setProp(_el$26, "height", 1);
11553
+ return _el$26;
11554
+ })(), (() => {
11555
+ var _el$27 = createElement("box"), _el$28 = createElement("text");
11556
+ insertNode(_el$27, _el$28);
11557
+ setProp(_el$27, "height", 1);
11558
+ insertNode(_el$28, createTextNode(`Tip: Choose a key that doesn't conflict with your terminal`));
11559
+ effect((_$p) => setProp(_el$28, "fg", props.theme.muted, _$p));
11560
+ return _el$27;
11561
+ })(), (() => {
11562
+ var _el$30 = createElement("box");
11563
+ setProp(_el$30, "height", 1);
11564
+ return _el$30;
11565
+ })(), (() => {
11566
+ var _el$31 = createElement("box"), _el$32 = createElement("text");
11567
+ insertNode(_el$31, _el$32);
11568
+ setProp(_el$31, "height", 1);
11569
+ insertNode(_el$32, createTextNode(`j/k: Navigate | Enter: Select | Esc/Backspace: Back`));
11570
+ effect((_$p) => setProp(_el$32, "fg", props.theme.primary, _$p));
11571
+ return _el$31;
11572
+ })()];
11573
+ })(), null);
11574
+ effect((_p$) => {
11575
+ var _v$ = props.theme.accent, _v$2 = props.theme.foreground, _v$3 = props.theme.muted;
11576
+ _v$ !== _p$.e && (_p$.e = setProp(_el$3, "fg", _v$, _p$.e));
11577
+ _v$2 !== _p$.t && (_p$.t = setProp(_el$7, "fg", _v$2, _p$.t));
11578
+ _v$3 !== _p$.a && (_p$.a = setProp(_el$1, "fg", _v$3, _p$.a));
11579
+ return _p$;
11580
+ }, {
11581
+ e: undefined,
11582
+ t: undefined,
11583
+ a: undefined
11584
+ });
11585
+ return _el$;
11586
+ })();
11587
+ };
11296
11588
  // src/components/onboarding/PresetSelectionStep.tsx
11297
11589
  var PresetSelectionStep = (props) => {
11298
11590
  const [focusedIndex, setFocusedIndex] = createSignal(0);
@@ -11927,10 +12219,11 @@ var ConfirmationStep = (props) => {
11927
12219
  },
11928
12220
  get children() {
11929
12221
  return [(() => {
11930
- var _el$21 = createElement("box"), _el$22 = createElement("text");
12222
+ var _el$21 = createElement("box"), _el$22 = createElement("text"), _el$23 = createTextNode(`No apps selected. Add more apps later with `);
11931
12223
  insertNode(_el$21, _el$22);
11932
12224
  setProp(_el$21, "height", 1);
11933
- insertNode(_el$22, createTextNode(`No apps selected. You can add apps later with Ctrl+T`));
12225
+ insertNode(_el$22, _el$23);
12226
+ insert(_el$22, () => formatLeaderKeybind(props.selectedLeaderKey, props.newTabBinding), null);
11934
12227
  effect((_$p) => setProp(_el$22, "fg", props.theme.muted, _$p));
11935
12228
  return _el$21;
11936
12229
  })(), (() => {
@@ -11968,10 +12261,14 @@ var OnboardingWizard = (props) => {
11968
12261
  const [currentStep, setCurrentStep] = createSignal("welcome");
11969
12262
  const [selectedPresets, setSelectedPresets] = createSignal(new Set);
11970
12263
  const [customApps, setCustomApps] = createSignal([]);
12264
+ const [selectedLeaderKey, setSelectedLeaderKey] = createSignal("ctrl+a");
11971
12265
  const goToStep = (step) => setCurrentStep(step);
11972
12266
  const handleNext = () => {
11973
12267
  switch (currentStep()) {
11974
12268
  case "welcome":
12269
+ goToStep("keybindings");
12270
+ break;
12271
+ case "keybindings":
11975
12272
  goToStep("presets");
11976
12273
  break;
11977
12274
  case "presets":
@@ -11991,10 +12288,16 @@ var OnboardingWizard = (props) => {
11991
12288
  goToStep("presets");
11992
12289
  break;
11993
12290
  case "presets":
12291
+ goToStep("keybindings");
12292
+ break;
12293
+ case "keybindings":
11994
12294
  goToStep("welcome");
11995
12295
  break;
11996
12296
  }
11997
12297
  };
12298
+ const handleSelectLeader = (key) => {
12299
+ setSelectedLeaderKey(key);
12300
+ };
11998
12301
  const togglePreset = (presetId) => {
11999
12302
  setSelectedPresets((prev) => {
12000
12303
  const next = new Set(prev);
@@ -12026,19 +12329,21 @@ var OnboardingWizard = (props) => {
12026
12329
  }
12027
12330
  }
12028
12331
  apps.push(...customApps());
12029
- props.onComplete(apps);
12332
+ props.onComplete(apps, selectedLeaderKey());
12030
12333
  };
12031
12334
  const handleSkip = () => {
12032
12335
  props.onSkip();
12033
12336
  };
12034
12337
  const stepNumber = () => {
12035
- const steps = ["welcome", "presets", "custom", "confirm"];
12338
+ const steps = ["welcome", "keybindings", "presets", "custom", "confirm"];
12036
12339
  return steps.indexOf(currentStep()) + 1;
12037
12340
  };
12038
12341
  const stepName = () => {
12039
12342
  switch (currentStep()) {
12040
12343
  case "welcome":
12041
12344
  return "Welcome";
12345
+ case "keybindings":
12346
+ return "Keybindings";
12042
12347
  case "presets":
12043
12348
  return "Select Apps";
12044
12349
  case "custom":
@@ -12048,11 +12353,11 @@ var OnboardingWizard = (props) => {
12048
12353
  }
12049
12354
  };
12050
12355
  const stepIndicator = () => {
12051
- const steps = ["welcome", "presets", "custom", "confirm"];
12356
+ const steps = ["welcome", "keybindings", "presets", "custom", "confirm"];
12052
12357
  return steps.map((step, i) => i < stepNumber() ? "[*]" : "[ ]").join("---");
12053
12358
  };
12054
12359
  return (() => {
12055
- var _el$ = createElement("box"), _el$2 = createElement("box"), _el$3 = createElement("text"), _el$4 = createTextNode(`Step `), _el$5 = createTextNode(` of 4: `), _el$6 = createElement("box"), _el$7 = createElement("text"), _el$8 = createElement("box");
12360
+ var _el$ = createElement("box"), _el$2 = createElement("box"), _el$3 = createElement("text"), _el$4 = createTextNode(`Step `), _el$5 = createTextNode(` of 5: `), _el$6 = createElement("box"), _el$7 = createElement("text"), _el$8 = createElement("box");
12056
12361
  insertNode(_el$, _el$2);
12057
12362
  insertNode(_el$, _el$6);
12058
12363
  insertNode(_el$, _el$8);
@@ -12087,6 +12392,23 @@ var OnboardingWizard = (props) => {
12087
12392
  onSkip: handleSkip
12088
12393
  });
12089
12394
  }
12395
+ }), createComponent2(Match, {
12396
+ get when() {
12397
+ return currentStep() === "keybindings";
12398
+ },
12399
+ get children() {
12400
+ return createComponent2(KeybindingStep, {
12401
+ get theme() {
12402
+ return props.theme;
12403
+ },
12404
+ get selectedLeaderKey() {
12405
+ return selectedLeaderKey();
12406
+ },
12407
+ onSelectLeader: handleSelectLeader,
12408
+ onNext: handleNext,
12409
+ onBack: handleBack
12410
+ });
12411
+ }
12090
12412
  }), createComponent2(Match, {
12091
12413
  get when() {
12092
12414
  return currentStep() === "presets";
@@ -12140,6 +12462,10 @@ var OnboardingWizard = (props) => {
12140
12462
  get customApps() {
12141
12463
  return customApps();
12142
12464
  },
12465
+ get selectedLeaderKey() {
12466
+ return selectedLeaderKey();
12467
+ },
12468
+ newTabBinding: "t",
12143
12469
  onConfirm: handleConfirm,
12144
12470
  onBack: handleBack
12145
12471
  });
@@ -12161,6 +12487,141 @@ var OnboardingWizard = (props) => {
12161
12487
  return _el$;
12162
12488
  })();
12163
12489
  };
12490
+ // src/components/LeaderHints.tsx
12491
+ var LeaderHints = (props) => {
12492
+ const bindingLabels = [{
12493
+ key: "next_tab",
12494
+ label: "Next Tab"
12495
+ }, {
12496
+ key: "prev_tab",
12497
+ label: "Prev Tab"
12498
+ }, {
12499
+ key: "new_tab",
12500
+ label: "New Tab"
12501
+ }, {
12502
+ key: "close_tab",
12503
+ label: "Close Tab"
12504
+ }, {
12505
+ key: "toggle_focus",
12506
+ label: "Focus"
12507
+ }, {
12508
+ key: "edit_app",
12509
+ label: "Edit"
12510
+ }, {
12511
+ key: "restart_app",
12512
+ label: "Restart"
12513
+ }, {
12514
+ key: "command_palette",
12515
+ label: "Palette"
12516
+ }, {
12517
+ key: "stop_app",
12518
+ label: "Stop"
12519
+ }, {
12520
+ key: "kill_all",
12521
+ label: "Kill All"
12522
+ }, {
12523
+ key: "quit",
12524
+ label: "Quit"
12525
+ }];
12526
+ const leftColumn = bindingLabels.slice(0, 6);
12527
+ const rightColumn = bindingLabels.slice(6);
12528
+ const formatKey = (key) => {
12529
+ if (key === "space")
12530
+ return "Space";
12531
+ if (key === "enter")
12532
+ return "Enter";
12533
+ return key;
12534
+ };
12535
+ return (() => {
12536
+ var _el$ = createElement("box"), _el$2 = createElement("box"), _el$3 = createElement("text"), _el$4 = createElement("b"), _el$6 = createElement("box"), _el$7 = createElement("box"), _el$8 = createElement("box"), _el$9 = createElement("box"), _el$0 = createElement("text");
12537
+ insertNode(_el$, _el$2);
12538
+ insertNode(_el$, _el$6);
12539
+ insertNode(_el$, _el$9);
12540
+ setProp(_el$, "position", "absolute");
12541
+ setProp(_el$, "bottom", "15%");
12542
+ setProp(_el$, "left", "25%");
12543
+ setProp(_el$, "width", "50%");
12544
+ setProp(_el$, "flexDirection", "column");
12545
+ setProp(_el$, "borderStyle", "double");
12546
+ insertNode(_el$2, _el$3);
12547
+ setProp(_el$2, "height", 1);
12548
+ setProp(_el$2, "justifyContent", "center");
12549
+ insertNode(_el$3, _el$4);
12550
+ insertNode(_el$4, createTextNode(` Leader Key Bindings `));
12551
+ insertNode(_el$6, _el$7);
12552
+ insertNode(_el$6, _el$8);
12553
+ setProp(_el$6, "flexDirection", "row");
12554
+ setProp(_el$6, "paddingLeft", 1);
12555
+ setProp(_el$6, "paddingRight", 1);
12556
+ setProp(_el$7, "flexDirection", "column");
12557
+ setProp(_el$7, "flexGrow", 1);
12558
+ insert(_el$7, createComponent2(For, {
12559
+ each: leftColumn,
12560
+ children: (binding) => (() => {
12561
+ var _el$10 = createElement("box"), _el$11 = createElement("text"), _el$12 = createElement("text"), _el$13 = createTextNode(`: `);
12562
+ insertNode(_el$10, _el$11);
12563
+ insertNode(_el$10, _el$12);
12564
+ setProp(_el$10, "height", 1);
12565
+ insert(_el$11, () => formatKey(props.bindings[binding.key]));
12566
+ insertNode(_el$12, _el$13);
12567
+ insert(_el$12, () => binding.label, null);
12568
+ effect((_p$) => {
12569
+ var _v$5 = props.theme.accent, _v$6 = props.theme.foreground;
12570
+ _v$5 !== _p$.e && (_p$.e = setProp(_el$11, "fg", _v$5, _p$.e));
12571
+ _v$6 !== _p$.t && (_p$.t = setProp(_el$12, "fg", _v$6, _p$.t));
12572
+ return _p$;
12573
+ }, {
12574
+ e: undefined,
12575
+ t: undefined
12576
+ });
12577
+ return _el$10;
12578
+ })()
12579
+ }));
12580
+ setProp(_el$8, "flexDirection", "column");
12581
+ setProp(_el$8, "flexGrow", 1);
12582
+ insert(_el$8, createComponent2(For, {
12583
+ each: rightColumn,
12584
+ children: (binding) => (() => {
12585
+ var _el$14 = createElement("box"), _el$15 = createElement("text"), _el$16 = createElement("text"), _el$17 = createTextNode(`: `);
12586
+ insertNode(_el$14, _el$15);
12587
+ insertNode(_el$14, _el$16);
12588
+ setProp(_el$14, "height", 1);
12589
+ insert(_el$15, () => formatKey(props.bindings[binding.key]));
12590
+ insertNode(_el$16, _el$17);
12591
+ insert(_el$16, () => binding.label, null);
12592
+ effect((_p$) => {
12593
+ var _v$7 = props.theme.accent, _v$8 = props.theme.foreground;
12594
+ _v$7 !== _p$.e && (_p$.e = setProp(_el$15, "fg", _v$7, _p$.e));
12595
+ _v$8 !== _p$.t && (_p$.t = setProp(_el$16, "fg", _v$8, _p$.t));
12596
+ return _p$;
12597
+ }, {
12598
+ e: undefined,
12599
+ t: undefined
12600
+ });
12601
+ return _el$14;
12602
+ })()
12603
+ }));
12604
+ insertNode(_el$9, _el$0);
12605
+ setProp(_el$9, "height", 1);
12606
+ setProp(_el$9, "justifyContent", "center");
12607
+ insertNode(_el$0, createTextNode(`Press any key...`));
12608
+ effect((_p$) => {
12609
+ var _v$ = props.theme.primary, _v$2 = props.theme.background, _v$3 = props.theme.accent, _v$4 = props.theme.muted;
12610
+ _v$ !== _p$.e && (_p$.e = setProp(_el$, "borderColor", _v$, _p$.e));
12611
+ _v$2 !== _p$.t && (_p$.t = setProp(_el$, "backgroundColor", _v$2, _p$.t));
12612
+ _v$3 !== _p$.a && (_p$.a = setProp(_el$3, "fg", _v$3, _p$.a));
12613
+ _v$4 !== _p$.o && (_p$.o = setProp(_el$0, "fg", _v$4, _p$.o));
12614
+ return _p$;
12615
+ }, {
12616
+ e: undefined,
12617
+ t: undefined,
12618
+ a: undefined,
12619
+ o: undefined
12620
+ });
12621
+ return _el$;
12622
+ })();
12623
+ };
12624
+
12164
12625
  // node_modules/solid-js/store/dist/store.js
12165
12626
  var $RAW = Symbol("store-raw");
12166
12627
  var $NODE = Symbol("store-node");
@@ -17123,6 +17584,8 @@ var App = (props) => {
17123
17584
  const [isQuitting, setIsQuitting] = createSignal(false);
17124
17585
  const [editingEntryId, setEditingEntryId] = createSignal(null);
17125
17586
  const [lastGTime, setLastGTime] = createSignal(0);
17587
+ const [showHints, setShowHints] = createSignal(false);
17588
+ let hintsTimeout = null;
17126
17589
  const getPtyDimensions = () => {
17127
17590
  const dims = terminalDims();
17128
17591
  const cols = dims.width - props.config.tab_width - 2;
@@ -17262,7 +17725,7 @@ var App = (props) => {
17262
17725
  tabsStore.setActiveTab(id);
17263
17726
  }
17264
17727
  };
17265
- const persistAppsConfig = async () => {
17728
+ const persistAppsConfig = async (keybindsOverride) => {
17266
17729
  const nextApps = appsStore.store.entries.map((entry) => ({
17267
17730
  name: entry.name,
17268
17731
  command: entry.command,
@@ -17274,9 +17737,13 @@ var App = (props) => {
17274
17737
  }));
17275
17738
  const nextConfig = {
17276
17739
  ...props.config,
17277
- apps: nextApps
17740
+ apps: nextApps,
17741
+ keybinds: keybindsOverride ?? props.config.keybinds
17278
17742
  };
17279
17743
  props.config.apps = nextApps;
17744
+ if (keybindsOverride) {
17745
+ props.config.keybinds = keybindsOverride;
17746
+ }
17280
17747
  try {
17281
17748
  await saveConfig(nextConfig);
17282
17749
  return true;
@@ -17285,11 +17752,39 @@ var App = (props) => {
17285
17752
  return false;
17286
17753
  }
17287
17754
  };
17288
- const handleWizardComplete = async (apps) => {
17755
+ const handleWizardComplete = async (apps, leaderKey) => {
17289
17756
  for (const appConfig of apps) {
17290
17757
  appsStore.addEntry(appConfig);
17291
17758
  }
17292
- const success = await persistAppsConfig();
17759
+ const keybindsConfig = {
17760
+ leader: {
17761
+ key: leaderKey,
17762
+ timeout: 1000,
17763
+ show_hints: true,
17764
+ hint_delay: 300
17765
+ },
17766
+ bindings: {
17767
+ next_tab: "n",
17768
+ prev_tab: "p",
17769
+ close_tab: "w",
17770
+ new_tab: "t",
17771
+ toggle_focus: "a",
17772
+ edit_app: "e",
17773
+ restart_app: "r",
17774
+ command_palette: "space",
17775
+ stop_app: "x",
17776
+ kill_all: "K",
17777
+ quit: "q"
17778
+ },
17779
+ direct: {
17780
+ navigate_up: "k",
17781
+ navigate_down: "j",
17782
+ select: "enter",
17783
+ go_top: "g",
17784
+ go_bottom: "G"
17785
+ }
17786
+ };
17787
+ const success = await persistAppsConfig(keybindsConfig);
17293
17788
  if (success) {
17294
17789
  setWizardCompleted(true);
17295
17790
  uiStore.showTemporaryMessage(`Added ${apps.length} app(s)`);
@@ -17411,11 +17906,19 @@ var App = (props) => {
17411
17906
  }
17412
17907
  };
17413
17908
  const handleLeaderBinding = createLeaderBindingHandler(leaderBindings, actionHandlers);
17909
+ const cancelLeader = () => {
17910
+ uiStore.setLeaderActive(false);
17911
+ if (hintsTimeout) {
17912
+ clearTimeout(hintsTimeout);
17913
+ hintsTimeout = null;
17914
+ }
17915
+ setShowHints(false);
17916
+ };
17414
17917
  useKeyboard((event) => {
17415
17918
  debugLog(`[App] key: ${event.name} modal: ${uiStore.store.activeModal} leader: ${uiStore.store.leaderActive} prevented: ${event.defaultPrevented}`);
17416
17919
  if (uiStore.store.activeModal) {
17417
17920
  if (uiStore.store.leaderActive) {
17418
- uiStore.setLeaderActive(false);
17921
+ cancelLeader();
17419
17922
  }
17420
17923
  if (event.name === "escape") {
17421
17924
  if (uiStore.store.activeModal === "edit-app") {
@@ -17436,7 +17939,7 @@ var App = (props) => {
17436
17939
  }
17437
17940
  if (uiStore.store.leaderActive) {
17438
17941
  if (event.name === "escape") {
17439
- uiStore.setLeaderActive(false);
17942
+ cancelLeader();
17440
17943
  event.preventDefault();
17441
17944
  return;
17442
17945
  }
@@ -17447,25 +17950,30 @@ var App = (props) => {
17447
17950
  activeApp.pty.write(sequence);
17448
17951
  }
17449
17952
  }
17450
- uiStore.setLeaderActive(false);
17953
+ cancelLeader();
17451
17954
  event.preventDefault();
17452
17955
  return;
17453
17956
  }
17454
17957
  const action = handleLeaderBinding(event);
17455
17958
  if (action !== null) {
17456
- uiStore.setLeaderActive(false);
17959
+ cancelLeader();
17457
17960
  event.preventDefault();
17458
17961
  return;
17459
17962
  }
17460
- uiStore.setLeaderActive(false);
17963
+ cancelLeader();
17461
17964
  event.preventDefault();
17462
17965
  return;
17463
17966
  }
17464
17967
  if (matchesLeaderKey(event, leaderConfig.key)) {
17465
17968
  uiStore.setLeaderActive(true);
17466
17969
  uiStore.startLeaderTimeout(() => {
17467
- uiStore.setLeaderActive(false);
17970
+ cancelLeader();
17468
17971
  }, leaderConfig.timeout);
17972
+ if (leaderConfig.show_hints) {
17973
+ hintsTimeout = setTimeout(() => {
17974
+ setShowHints(true);
17975
+ }, leaderConfig.hint_delay);
17976
+ }
17469
17977
  event.preventDefault();
17470
17978
  return;
17471
17979
  }
@@ -17652,6 +18160,12 @@ var App = (props) => {
17652
18160
  get theme() {
17653
18161
  return props.config.theme;
17654
18162
  },
18163
+ get leaderKey() {
18164
+ return props.config.keybinds.leader.key;
18165
+ },
18166
+ get newTabBinding() {
18167
+ return props.config.keybinds.bindings.new_tab;
18168
+ },
17655
18169
  onInput: handleTerminalInput
17656
18170
  }), null);
17657
18171
  insert(_el$, createComponent2(StatusBar, {
@@ -17746,6 +18260,24 @@ var App = (props) => {
17746
18260
  });
17747
18261
  }
17748
18262
  }), null);
18263
+ insert(_el$, createComponent2(Show, {
18264
+ get when() {
18265
+ return memo2(() => !!uiStore.store.leaderActive)() && showHints();
18266
+ },
18267
+ get children() {
18268
+ return createComponent2(LeaderHints, {
18269
+ get bindings() {
18270
+ return props.config.keybinds.bindings;
18271
+ },
18272
+ get leaderKey() {
18273
+ return props.config.keybinds.leader.key;
18274
+ },
18275
+ get theme() {
18276
+ return props.config.theme;
18277
+ }
18278
+ });
18279
+ }
18280
+ }), null);
17749
18281
  return _el$;
17750
18282
  }
17751
18283
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tuidoscope",
3
- "version": "0.1.13",
3
+ "version": "0.1.14",
4
4
  "description": "A centralized TUI management app for running TUI applications in embedded terminal windows",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",