rshow 0.0.5 → 0.0.6

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/dist/main.js CHANGED
@@ -13,7 +13,7 @@ import process from "node:process";
13
13
  // src/version.ts
14
14
  import { readFileSync } from "node:fs";
15
15
  import { fileURLToPath } from "node:url";
16
- var VERSION = true ? "0.0.5" : versionFromPackage();
16
+ var VERSION = true ? "0.0.6" : versionFromPackage();
17
17
 
18
18
  // src/binary.ts
19
19
  var ASSET_MANIFEST = "assets.json";
@@ -969,6 +969,7 @@ commands:
969
969
  delete <slug> --yes delete an artifact
970
970
  login [--url u] save the API token for this account
971
971
  logout [--url u] remove the saved token for a server
972
+ completion <shell> print a completion script for bash, zsh or fish
972
973
 
973
974
  global:
974
975
  --json print the API reply JSON
@@ -1243,6 +1244,19 @@ var LOGOUT_USAGE = `usage: rshow logout [--url u]
1243
1244
 
1244
1245
  Removes the token this server has from every store that holds it: the macOS
1245
1246
  Keychain, the Linux keyring and the 0600 credentials file.`;
1247
+ var COMPLETION_USAGE = `usage: rshow completion <bash|zsh|fish>
1248
+
1249
+ Prints a completion script for that shell on stdout. It lists every command
1250
+ and its long options, and it completes files and folders for the commands that
1251
+ take one. It is static: a completion never calls the API.
1252
+
1253
+ load it once per machine:
1254
+ bash rshow completion bash > ~/.local/share/bash-completion/completions/rshow
1255
+ zsh rshow completion zsh > ~/.local/share/zsh/site-functions/_rshow
1256
+ (that folder must be on $fpath, before compinit)
1257
+ fish rshow completion fish > ~/.config/fish/completions/rshow.fish
1258
+
1259
+ The curl installer writes all three, and an npm install runs one of them.`;
1246
1260
  var PROJECT_USAGE = `usage: rshow project <create|list|show|share|delete> [options]
1247
1261
 
1248
1262
  project create <slug> [--title t] [--description d]
@@ -8388,7 +8402,8 @@ var USAGE = {
8388
8402
  pin: PIN_USAGE,
8389
8403
  delete: DELETE_USAGE,
8390
8404
  login: LOGIN_USAGE,
8391
- logout: LOGOUT_USAGE
8405
+ logout: LOGOUT_USAGE,
8406
+ completion: COMPLETION_USAGE
8392
8407
  };
8393
8408
  var WHEN = {
8394
8409
  ok: "the work was done.",
@@ -8716,6 +8731,328 @@ async function run18(argv, ctx) {
8716
8731
  ctx.out.line(`wrote ${path24.join(out, "exit-codes.md")}`);
8717
8732
  }
8718
8733
 
8734
+ // src/commands/completion.ts
8735
+ var SHELLS = ["bash", "zsh", "fish"];
8736
+ var GLOBAL_OPTIONS = [
8737
+ { name: "--json", description: "print the API reply JSON" },
8738
+ { name: "--help", description: "show this help" },
8739
+ { name: "--version", description: "print rshow <version>" }
8740
+ ];
8741
+ var SKILL_TARGETS2 = ["claude", "pi", "codex"];
8742
+ var SPECS = {
8743
+ publish: { usage: PUBLISH_USAGE, path: true },
8744
+ check: { usage: CHECK_USAGE, path: true },
8745
+ handoff: { usage: HANDOFF_USAGE, path: true },
8746
+ compare: { usage: COMPARE_USAGE },
8747
+ preview: { usage: PREVIEW_USAGE, path: true },
8748
+ config: { usage: CONFIG_USAGE, path: true },
8749
+ init: { usage: INIT_USAGE },
8750
+ doctor: { usage: DOCTOR_USAGE, path: true },
8751
+ theme: { usage: THEME_USAGE },
8752
+ "theme new": { usage: THEME_NEW_USAGE },
8753
+ "theme check": { usage: THEME_CHECK_USAGE, path: true },
8754
+ "theme preview": { usage: THEME_PREVIEW_USAGE, path: true },
8755
+ "theme publish": { usage: THEME_PUBLISH_USAGE, path: true },
8756
+ "theme list": { usage: THEME_LIST_USAGE },
8757
+ skills: { usage: SKILLS_USAGE },
8758
+ "skills install": { usage: SKILLS_INSTALL_USAGE, choices: SKILL_TARGETS2 },
8759
+ "skills uninstall": { usage: SKILLS_UNINSTALL_USAGE, choices: SKILL_TARGETS2 },
8760
+ "skills status": { usage: SKILLS_STATUS_USAGE, choices: SKILL_TARGETS2 },
8761
+ project: { usage: PROJECT_USAGE },
8762
+ "project create": { usage: PROJECT_CREATE_USAGE },
8763
+ "project list": { usage: PROJECT_LIST_USAGE },
8764
+ "project show": { usage: PROJECT_SHOW_USAGE },
8765
+ "project share": { usage: PROJECT_SHARE_USAGE },
8766
+ "project delete": { usage: PROJECT_DELETE_USAGE },
8767
+ tag: { usage: TAG_USAGE },
8768
+ list: { usage: LIST_USAGE },
8769
+ show: { usage: SHOW_USAGE },
8770
+ share: { usage: SHARE_USAGE },
8771
+ pin: { usage: PIN_USAGE },
8772
+ delete: { usage: DELETE_USAGE },
8773
+ login: { usage: LOGIN_USAGE },
8774
+ logout: { usage: LOGOUT_USAGE },
8775
+ completion: { usage: COMPLETION_USAGE, choices: [...SHELLS] }
8776
+ };
8777
+ function optionsOf(usage) {
8778
+ const out = [];
8779
+ for (const found of usageLine(usage).matchAll(/--([a-z][a-z0-9-]*)(?:\s+([^[\]\s|]+))?/g)) {
8780
+ out.push({ name: `--${found[1]}`, value: found[2] });
8781
+ }
8782
+ return out;
8783
+ }
8784
+ function summaryOf2(name) {
8785
+ const words = name.split(" ");
8786
+ const parent = SPECS[words[0]];
8787
+ const text = words.length === 1 || parent === void 0 ? GENERAL_USAGE : parent.usage;
8788
+ for (const line of text.split("\n")) {
8789
+ if (!/^ {2}\S/.test(line)) continue;
8790
+ const parts2 = line.trim().split(/ {2,}/);
8791
+ const spec = (parts2[0] ?? "").split(" ");
8792
+ if (spec[0] !== words[0]) continue;
8793
+ if (words.length === 2 && spec[1] !== words[1]) continue;
8794
+ if (parts2.length > 1) return parts2.slice(1).join(" ").trim();
8795
+ let index = 1;
8796
+ while (index < spec.length && /^(?:<[^>]+>|\[[^\]]*\]|--?\S+)$/.test(spec[index])) index += 1;
8797
+ const summary = spec.slice(index).join(" ").trim();
8798
+ return summary.startsWith("[") ? "" : summary;
8799
+ }
8800
+ return "";
8801
+ }
8802
+ function specs() {
8803
+ return Object.entries(SPECS).map(([name, entry]) => ({
8804
+ words: name.split(" "),
8805
+ options: optionsOf(entry.usage),
8806
+ path: entry.path === true,
8807
+ choices: entry.choices ?? [],
8808
+ summary: summaryOf2(name)
8809
+ }));
8810
+ }
8811
+ function childrenOf(all, parent) {
8812
+ return all.filter((spec) => spec.words.length === 2 && spec.words[0] === parent);
8813
+ }
8814
+ function quoted2(text) {
8815
+ return `'${text.replace(/'/g, "'\\''")}'`;
8816
+ }
8817
+ function described(name, summary) {
8818
+ return quoted2(summary === "" ? name : `${name}:${summary}`);
8819
+ }
8820
+ function bashScript() {
8821
+ const all = specs();
8822
+ const top = all.filter((spec) => spec.words.length === 1);
8823
+ const arms = all.map((spec) => {
8824
+ const key2 = quoted2(spec.words.join(" "));
8825
+ const children = childrenOf(all, spec.words[0]);
8826
+ const names = spec.options.map((option) => option.name);
8827
+ const values = spec.options.filter((option) => option.value !== void 0).map((option) => option.name);
8828
+ const parts2 = [];
8829
+ const choices = spec.words.length === 1 && children.length > 0 ? children.map((child) => child.words[1]) : spec.choices;
8830
+ if (choices.length > 0) parts2.push(`choices='${choices.join(" ")}'`);
8831
+ if (names.length > 0) parts2.push(`opts="$opts ${names.join(" ")}"`);
8832
+ if (values.length > 0) parts2.push(`vals='${values.join(" ")}'`);
8833
+ if (spec.path) parts2.push("paths=1");
8834
+ return ` ${key2}) ${parts2.length === 0 ? ":" : parts2.join("; ")} ;;`;
8835
+ });
8836
+ return [
8837
+ "# bash completion for rshow, printed by `rshow completion bash`.",
8838
+ "# Load it with",
8839
+ "# rshow completion bash > ~/.local/share/bash-completion/completions/rshow",
8840
+ "",
8841
+ `_rshow_commands='${top.map((spec) => spec.words[0]).join(" ")}'`,
8842
+ `_rshow_options='${GLOBAL_OPTIONS.map((option) => option.name).join(" ")}'`,
8843
+ "",
8844
+ "_rshow() {",
8845
+ " local cur prev cmd sub key word opts vals paths choices i",
8846
+ " COMPREPLY=()",
8847
+ ' cur="${COMP_WORDS[COMP_CWORD]}"',
8848
+ " prev=''",
8849
+ ' if [ "$COMP_CWORD" -gt 0 ]; then',
8850
+ ' prev="${COMP_WORDS[COMP_CWORD-1]}"',
8851
+ " fi",
8852
+ "",
8853
+ " i=1",
8854
+ ' while [ "$i" -lt "$COMP_CWORD" ]; do',
8855
+ ' word="${COMP_WORDS[i]}"',
8856
+ ' case "$word" in',
8857
+ " -*) ;;",
8858
+ " *)",
8859
+ ' if [ -z "$cmd" ]; then',
8860
+ ' cmd="$word"',
8861
+ ' elif [ -z "$sub" ]; then',
8862
+ ' sub="$word"',
8863
+ " fi",
8864
+ " ;;",
8865
+ " esac",
8866
+ " i=$((i + 1))",
8867
+ " done",
8868
+ "",
8869
+ ' if [ -z "$cmd" ]; then',
8870
+ ' COMPREPLY=( $(compgen -W "$_rshow_commands $_rshow_options" -- "$cur") )',
8871
+ " return 0",
8872
+ " fi",
8873
+ "",
8874
+ ' key="$cmd"',
8875
+ ' if [ -n "$sub" ]; then',
8876
+ ' key="$cmd $sub"',
8877
+ " fi",
8878
+ "",
8879
+ ' opts="$_rshow_options"',
8880
+ " vals=''",
8881
+ " paths=''",
8882
+ " choices=''",
8883
+ ' case "$key" in',
8884
+ ...arms,
8885
+ " *) return 0 ;;",
8886
+ " esac",
8887
+ "",
8888
+ " # A flag that takes a value completes nothing: the value is a slug or a",
8889
+ " # folder, and this script never asks the API.",
8890
+ ' if [ -n "$vals" ]; then',
8891
+ ' case " $vals " in',
8892
+ ' *" $prev "*) return 0 ;;',
8893
+ " esac",
8894
+ " fi",
8895
+ "",
8896
+ ' case "$cur" in',
8897
+ " -*)",
8898
+ ' COMPREPLY=( $(compgen -W "$opts" -- "$cur") )',
8899
+ " return 0",
8900
+ " ;;",
8901
+ " esac",
8902
+ "",
8903
+ ' if [ -n "$choices" ]; then',
8904
+ ' COMPREPLY=( $(compgen -W "$choices" -- "$cur") )',
8905
+ " return 0",
8906
+ " fi",
8907
+ ' if [ -n "$paths" ]; then',
8908
+ ' COMPREPLY=( $(compgen -f -- "$cur") )',
8909
+ " return 0",
8910
+ " fi",
8911
+ " return 0",
8912
+ "}",
8913
+ "",
8914
+ "complete -F _rshow rshow"
8915
+ ].join("\n");
8916
+ }
8917
+ function zshArguments(spec) {
8918
+ const parts2 = GLOBAL_OPTIONS.map((option) => quoted2(`${option.name}[${option.description}]`));
8919
+ for (const option of spec.options) {
8920
+ parts2.push(quoted2(option.value === void 0 ? option.name : `${option.name}=`));
8921
+ }
8922
+ if (spec.choices.length > 0) parts2.push(quoted2(`*: :(${spec.choices.join(" ")})`));
8923
+ else if (spec.path) parts2.push(quoted2("*:file or folder:_files"));
8924
+ return `_arguments ${parts2.join(" ")}`;
8925
+ }
8926
+ function zshScript() {
8927
+ const all = specs();
8928
+ const top = all.filter((spec) => spec.words.length === 1);
8929
+ const groups = top.filter((spec) => childrenOf(all, spec.words[0]).length > 0);
8930
+ const arms = all.map((spec) => {
8931
+ const children = childrenOf(all, spec.words[0]);
8932
+ const key2 = quoted2(spec.words.join(" "));
8933
+ if (spec.words.length === 1 && children.length > 0) {
8934
+ const subs = children.map((child) => ` ${described(child.words[1], child.summary)}`);
8935
+ return [
8936
+ ` ${key2})`,
8937
+ " subs=(",
8938
+ ...subs,
8939
+ " )",
8940
+ ` _describe -t commands ${quoted2(`${spec.words[0]} command`)} subs`,
8941
+ " ;;"
8942
+ ].join("\n");
8943
+ }
8944
+ return ` ${key2}) ${zshArguments(spec)} ;;`;
8945
+ });
8946
+ return [
8947
+ "#compdef rshow",
8948
+ "",
8949
+ "# Completion for rshow, printed by `rshow completion zsh`.",
8950
+ "# Load it with",
8951
+ "# rshow completion zsh > ~/.local/share/zsh/site-functions/_rshow",
8952
+ "# and keep that folder on $fpath, before compinit.",
8953
+ "",
8954
+ "_rshow() {",
8955
+ " local -a commands subs groups",
8956
+ " local state key",
8957
+ "",
8958
+ " groups=(",
8959
+ ...groups.map((spec) => ` ${quoted2(spec.words[0])}`),
8960
+ " )",
8961
+ "",
8962
+ " _arguments -C \\",
8963
+ ...GLOBAL_OPTIONS.map((option) => ` ${quoted2(`${option.name}[${option.description}]`)} \\`),
8964
+ " '1: :->command' \\",
8965
+ " '*:: :->args' && return 0",
8966
+ "",
8967
+ " case $state in",
8968
+ " command)",
8969
+ " commands=(",
8970
+ ...top.map((spec) => ` ${described(spec.words[0], spec.summary)}`),
8971
+ " )",
8972
+ " _describe -t commands 'rshow command' commands",
8973
+ " ;;",
8974
+ " args)",
8975
+ " key=$words[1]",
8976
+ " if (( $groups[(Ie)$words[1]] )); then",
8977
+ " case $words[2] in",
8978
+ " ''|-*) ;;",
8979
+ ' *) key="$words[1] $words[2]" ;;',
8980
+ " esac",
8981
+ " fi",
8982
+ " case $key in",
8983
+ ...arms,
8984
+ " esac",
8985
+ " ;;",
8986
+ " esac",
8987
+ "}",
8988
+ "",
8989
+ "# Sourced from a shell that runs compinit as well as autoloaded from $fpath.",
8990
+ "if (( $+functions[compdef] )); then",
8991
+ " compdef _rshow rshow",
8992
+ "fi"
8993
+ ].join("\n");
8994
+ }
8995
+ function fishScript() {
8996
+ const all = specs();
8997
+ const top = all.filter((spec) => spec.words.length === 1);
8998
+ const condition = (words) => words.map((word) => `__fish_seen_subcommand_from ${word}`).join("; and ");
8999
+ const lines = [
9000
+ "# fish completion for rshow, printed by `rshow completion fish`.",
9001
+ "# Load it with",
9002
+ "# rshow completion fish > ~/.config/fish/completions/rshow.fish",
9003
+ "",
9004
+ "complete -c rshow -f",
9005
+ "",
9006
+ "# commands"
9007
+ ];
9008
+ for (const spec of top) {
9009
+ const summary = spec.summary === "" ? "" : ` -d ${quoted2(spec.summary)}`;
9010
+ lines.push(`complete -c rshow -n __fish_use_subcommand -a ${quoted2(spec.words[0])}${summary}`);
9011
+ }
9012
+ for (const spec of all.filter((entry) => entry.words.length === 2)) {
9013
+ const parent = spec.words[0];
9014
+ const siblings = childrenOf(all, parent).map((child) => child.words[1]);
9015
+ const summary = spec.summary === "" ? "" : ` -d ${quoted2(spec.summary)}`;
9016
+ lines.push(
9017
+ `complete -c rshow -n ${quoted2(
9018
+ `__fish_seen_subcommand_from ${parent}; and not __fish_seen_subcommand_from ${siblings.join(" ")}`
9019
+ )} -a ${quoted2(spec.words[1])}${summary}`
9020
+ );
9021
+ }
9022
+ lines.push("", "# options");
9023
+ for (const option of GLOBAL_OPTIONS) {
9024
+ lines.push(`complete -c rshow -l ${option.name.slice(2)} -d ${quoted2(option.description)}`);
9025
+ }
9026
+ for (const spec of all) {
9027
+ const seen = quoted2(condition(spec.words));
9028
+ for (const option of spec.options) {
9029
+ const value = option.value === void 0 ? "" : " -r";
9030
+ lines.push(`complete -c rshow -n ${seen} -l ${option.name.slice(2)}${value}`);
9031
+ }
9032
+ if (spec.choices.length > 0) lines.push(`complete -c rshow -n ${seen} -a ${quoted2(spec.choices.join(" "))}`);
9033
+ else if (spec.path) lines.push(`complete -c rshow -n ${seen} -F`);
9034
+ }
9035
+ return lines.join("\n");
9036
+ }
9037
+ function script(shell) {
9038
+ if (shell === "bash") return bashScript();
9039
+ if (shell === "zsh") return zshScript();
9040
+ return fishScript();
9041
+ }
9042
+ function isShell(value) {
9043
+ return SHELLS.includes(value);
9044
+ }
9045
+ async function run19(argv, ctx) {
9046
+ const args = parseCommandArgs(argv, {}, 0, 1, COMPLETION_USAGE);
9047
+ const shell = args.positionals[0];
9048
+ if (shell === void 0) {
9049
+ throw new UsageError(`completion needs a shell: bash, zsh or fish (try: ${usageLine(COMPLETION_USAGE)})`);
9050
+ }
9051
+ if (!isShell(shell)) throw new UsageError(`unknown shell: ${shell} (use bash, zsh or fish)`);
9052
+ ctx.out.raw(script(shell));
9053
+ return EXIT.ok;
9054
+ }
9055
+
8719
9056
  // src/commands/login.ts
8720
9057
  import process21 from "node:process";
8721
9058
  async function hiddenLine() {
@@ -8773,7 +9110,7 @@ function reasonOf(err) {
8773
9110
  const lines = text.split("\n").map((part) => part.trim()).filter((part) => part !== "");
8774
9111
  return lines.at(-1) ?? "";
8775
9112
  }
8776
- async function run19(argv, ctx, platform = process21.platform) {
9113
+ async function run20(argv, ctx, platform = process21.platform) {
8777
9114
  const args = parseCommandArgs(argv, { url: { type: "string" } }, 0, 0, LOGIN_USAGE);
8778
9115
  const settings = await ctx.config();
8779
9116
  const given = (stringValue(args, "url") ?? "").trim();
@@ -8802,7 +9139,7 @@ async function run19(argv, ctx, platform = process21.platform) {
8802
9139
 
8803
9140
  // src/commands/logout.ts
8804
9141
  import process22 from "node:process";
8805
- async function run20(argv, ctx, platform = process22.platform) {
9142
+ async function run21(argv, ctx, platform = process22.platform) {
8806
9143
  const args = parseCommandArgs(argv, { url: { type: "string" } }, 0, 0, LOGOUT_USAGE);
8807
9144
  const settings = await ctx.config();
8808
9145
  const given = (stringValue(args, "url") ?? "").trim();
@@ -8837,8 +9174,9 @@ var COMMANDS = {
8837
9174
  share: { usage: SHARE_USAGE, run: run11 },
8838
9175
  pin: { usage: PIN_USAGE, run: run12 },
8839
9176
  delete: { usage: DELETE_USAGE, run: run13 },
8840
- login: { usage: LOGIN_USAGE, run: run19 },
8841
- logout: { usage: LOGOUT_USAGE, run: run20 },
9177
+ login: { usage: LOGIN_USAGE, run: run20 },
9178
+ logout: { usage: LOGOUT_USAGE, run: run21 },
9179
+ completion: { usage: COMPLETION_USAGE, run: run19 },
8842
9180
  // Kept out of the general help: the docs site runs it, a person rarely does.
8843
9181
  docs: { usage: DOCS_USAGE, run: run18 }
8844
9182
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rshow",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
4
4
  "description": "Publish static artifacts to rshow",
5
5
  "license": "MIT",
6
6
  "homepage": "https://rslib.github.io/rshow",
@@ -110,6 +110,7 @@ Never present invented data as if somebody measured it.
110
110
 
111
111
  - `rshow check <dir>` exits 0, then `rshow preview <dir>`.
112
112
  - Look at the canvas, the focus view and Play mode, in both modes, at desktop width and at 360px.
113
+ - Press `?` on the page for the keys: `w` `a` `s` `d` or the arrows pan, `[` `]` step between screens, `Enter` opens one, `1` to `9` pick a state, so tell the person about that list.
113
114
  - Switch the skin in the toolbar and check the drawing still reads.
114
115
  - Print puts one artboard on a page with no tool chrome, which is what a review meeting wants on paper.
115
116
  - `rshow publish <dir> --slug <slug> --label "<what changed>"`, then read `rshow-implement` before anybody writes code from it.
@@ -112,6 +112,8 @@ Vary the rhythm: do not run more than three text slides (`two-col` or `bullets`)
112
112
  Press `Ctrl` and `P` for a print sheet when someone wants a pdf.
113
113
  - Press `t` to cycle the mode: system, light, dark, with the mode shown in the counter for a moment.
114
114
  The choice is remembered for every rshow document and print is always light.
115
+ - On a phone held upright the deck reads as a stack of slides, each one reflowed to the width of the screen at phone sizes with a rule and an `n / total` label between slides, and a `Present` button goes back to one slide at a time.
116
+ `Esc` comes back to reading, and the arrows, space and a `#n` link work in both.
115
117
 
116
118
  ## 5. Check, preview, publish
117
119