Skip to content

Commit 57a5227

Browse files
committed
autocomplete ALL vals
1 parent 32f6add commit 57a5227

File tree

2 files changed

+63
-30
lines changed

2 files changed

+63
-30
lines changed

src/cmd/lib/clone.ts

Lines changed: 16 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Command } from "@cliffy/command";
22
import { Input } from "@cliffy/prompt/input";
33
import { colors } from "@cliffy/ansi/colors";
4-
import sdk, { getCurrentUser } from "~/sdk.ts";
4+
import sdk, { getCurrentUser, typeaheadValNames, valNameToVal } from "~/sdk.ts";
55
import VTClient from "~/vt/vt/VTClient.ts";
66
import { relative } from "@std/path";
77
import { doWithSpinner, getClonePath } from "~/cmd/utils.ts";
@@ -55,41 +55,27 @@ export const cloneCmd = new Command()
5555

5656
// If no Val URI is provided, show interactive Val selection
5757
if (!valUri) {
58-
const vals = await doWithSpinner(
59-
"Loading vals...",
60-
async (spinner) => {
61-
const [allVals, _] = await arrayFromAsyncN(
62-
sdk.me.vals.list({ limit: 100 }),
63-
400,
64-
);
65-
spinner.stop();
66-
return allVals;
67-
},
68-
);
69-
70-
if (vals.length === 0) {
71-
console.log(colors.yellow("You don't have any Vals yet."));
72-
return;
73-
}
74-
75-
// Map vals to name format for selection
76-
const valNames = vals
77-
// Only show vals owned by the user (not orgs that the user is in)
78-
.filter((p) => p.author.id === user.id)
79-
.map((p) => p.name);
80-
58+
let suggestions = new Set();
8159
const selectedVal = await Input.prompt({
8260
message: "Choose a Val to clone",
8361
list: true,
8462
info: true,
85-
suggestions: valNames,
63+
validate: (input) => {
64+
const split = input.split("/");
65+
return suggestions.has(input) && (split.length === 2) &&
66+
split[1].length !== 0;
67+
},
68+
suggestions: async (prefix) => {
69+
suggestions = new Set(
70+
await typeaheadValNames(prefix || `${user.username}/`),
71+
);
72+
return Array.from(suggestions) as (string | number)[];
73+
},
8674
});
8775

88-
const val = vals.find((p) => p.name === selectedVal);
89-
if (!val) {
90-
console.log(colors.red("Val not found"));
91-
return;
92-
}
76+
const parts = selectedVal.split("/");
77+
let [handle, valName] = parts;
78+
const val = await valNameToVal(handle, valName);
9379

9480
ownerName = val.author.username || user.username!;
9581
valName = val.name;

src/sdk.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,20 @@ export async function branchNameToBranch(
8888
throw new Deno.errors.NotFound(`Branch "${branchName}" not found in Val`);
8989
}
9090

91+
/**
92+
* Converts a val name to its corresponding val data.
93+
*
94+
* @param username The username of the Val owner
95+
* @param valName The name of the Val to look up
96+
* @returns Promise resolving to the Val data
97+
*/
98+
export async function valNameToVal(
99+
username: string,
100+
valName: string,
101+
) {
102+
return await sdk.alias.username.valName.retrieve(username, valName);
103+
}
104+
91105
/**
92106
* Checks if a file exists at the specified path in a val
93107
*
@@ -328,4 +342,37 @@ export async function fileIdToValFile(
328342
return await sdk.files.retrieve(fileId);
329343
}
330344

345+
/**
346+
* Get typeahead for Val names or organization names.
347+
*
348+
* @param prefix - A string in the format "org" or "org/val" to get typeahead suggestions
349+
* @returns Promise resolving to an array of typeahead suggestions
350+
*/
351+
export const typeaheadValNames = memoize(async (
352+
prefix: string,
353+
): Promise<string[]> => {
354+
const parts = prefix.split("/");
355+
356+
if (parts.length === 1) {
357+
// Typeahead for organization name
358+
const response = await fetch(
359+
`https://api.val.town/v2/orgs/typeahead/${parts[0]}`,
360+
{ headers: { "x-vt-version": String(manifest.version) } },
361+
);
362+
const data = await response.json() as { items: string[] };
363+
return data.items;
364+
} else {
365+
// Typeahead for val name (org/val)
366+
const [org, val] = parts;
367+
const response = await fetch(
368+
`https://api.val.town/v2/vals/typeahead/${org}/${val}`,
369+
{
370+
headers: { "x-vt-version": String(manifest.version) },
371+
},
372+
);
373+
const data = await response.json() as { items: string[] };
374+
return data.items.map((valName) => `${org}/${valName}`);
375+
}
376+
});
377+
331378
export default sdk;

0 commit comments

Comments
 (0)