launch and manage windows terminal instances with raycast
raycast raycast-extension

implement aliases #2

closed opened by woof.monster targeting main from feature/alias

an attempt to add support for profile aliases, very work in progress and has a lot of issues.

(these do not have any effect in the actual windows terminal or show them in root search) (supersedes #1)

Labels
new-feature
branch

None yet.

assignee

None yet.

Participants 1
AT URI
at://did:plc:fhg4ndnjwmxfkarwaqml7kyo/sh.tangled.repo.pull/3mdlf2xq7mt22
+176 -11
Diff #0
+152 -11
src/open-profile.tsx
··· 1 - import { Action, ActionPanel, Icon, Keyboard, List, closeMainWindow, getPreferenceValues } from "@raycast/api"; 1 + import { 2 + Action, 3 + ActionPanel, 4 + Form, 5 + Icon, 6 + Keyboard, 7 + List, 8 + LocalStorage, 9 + closeMainWindow, 10 + getPreferenceValues, 11 + showToast, 12 + useNavigation, 13 + } from "@raycast/api"; 14 + import { useForm } from "@raycast/utils"; 2 15 import { execFile } from "node:child_process"; 3 16 import fs from "node:fs"; 4 17 import os from "node:os"; 5 - import { useState } from "react"; 18 + import { useEffect, useState } from "react"; 6 19 import { Preferences } from "./types/preferences"; 7 - import { Profile, WindowsTerminalSettings, Folder } from "./types/windows-terminal"; 20 + import { Profile, WindowsTerminalSettings, Folder, FolderEntry } from "./types/windows-terminal"; 21 + import { getAllProfilePreferences, getProfilePreferences } from "./utils/profile-preferences"; 22 + import React from "react"; 8 23 9 24 const preferences = getPreferenceValues<Preferences>(); 10 25 const PROFILES = JSON.parse( 11 26 fs.readFileSync(`C:\\Users\\${os.userInfo().username}${preferences.release}`, "utf8"), 12 27 ) as WindowsTerminalSettings; 13 28 14 - function Actions(props: { profile: Profile }) { 29 + // TEACH ME HOW TO SUSPEND 30 + // ------------------------------------------------------------------------- // 31 + // A component was suspended by an uncached promise. Creating promises inside a 32 + // Client Component or hook is not yet supported, except via a 33 + // Suspense-compatible library or framework. 34 + // ------------------------------------------------------------------------- // 35 + // this does work despite the error, though. 36 + const EditAlias = React.memo(async (props: { profile: Profile }) => { 37 + console.log("INVOK"); 38 + const { pop } = useNavigation(); 39 + const [getAlias, setAlias] = useState<string>(""); 40 + 41 + const { handleSubmit, itemProps, setValue } = useForm<{ alias: string }>({ 42 + async onSubmit(values: { alias: string }) { 43 + await LocalStorage.setItem(props.profile.guid, JSON.stringify({ alias: values.alias })); 44 + pop(); 45 + showToast({ title: `Alias set for ${props.profile.name}` }); 46 + }, 47 + validation: { 48 + alias(value) { 49 + if (!value) { 50 + return "An alias is required"; 51 + } else if (value.length >= 12) { 52 + return "Alias is too long"; 53 + } 54 + }, 55 + }, 56 + }); 57 + 58 + useEffect(() => { 59 + async function load() { 60 + const profile = await getProfilePreferences(props.profile.guid); 61 + setAlias(profile.alias); 62 + // setValue("alias", getAlias); 63 + console.log(getAlias); 64 + } 65 + load(); 66 + }, [props.profile.guid]); 67 + 68 + return ( 69 + <Form 70 + actions={ 71 + <ActionPanel> 72 + <Action.SubmitForm 73 + icon={Icon.Pencil} 74 + title={getAlias ? "Update Alias" : "Set Alias"} 75 + onSubmit={handleSubmit} 76 + /> 77 + {getAlias ? ( 78 + <Action.SubmitForm 79 + icon={Icon.Trash} 80 + title="Delete Alias" 81 + shortcut={Keyboard.Shortcut.Common.Remove} 82 + style={Action.Style.Destructive} 83 + onSubmit={async () => { 84 + await LocalStorage.removeItem(props.profile.guid); 85 + // when the profile preferences gets extended, we will use this instead 86 + // await LocalStorage.setItem(props.profile.guid, JSON.stringify({ alias: "" })); 87 + pop(); 88 + showToast({ title: `Deleted alias for ${props.profile.name}` }); 89 + }} 90 + /> 91 + ) : null} 92 + </ActionPanel> 93 + } 94 + > 95 + <Form.TextField placeholder={`Edit alias for ${props.profile.name}...`} autoFocus={true} {...itemProps.alias} /> 96 + <Form.Description 97 + text={`This will be the new alias for ${props.profile.name}. You will be able to search for this profile in this extension using the alias you have set. This will not sync to Windows Terminal, or make it accessible in root search.`} 98 + /> 99 + </Form> 100 + ); 101 + }); 102 + 103 + function Actions(props: { profile: Profile; alias: string }) { 104 + const { push } = useNavigation(); 15 105 return ( 16 106 <ActionPanel title={props.profile.name}> 17 107 <Action ··· 49 139 }} 50 140 /> 51 141 )} 142 + <ActionPanel.Section> 143 + <Action 144 + icon={Icon.TextCursor} 145 + title={props.alias ? "Update Aliasโ€ฆ" : "Set Aliasโ€ฆ"} 146 + shortcut={{ modifiers: ["ctrl", "shift"], key: "," }} 147 + onAction={() => { 148 + push(<EditAlias profile={props.profile} />); 149 + }} 150 + /> 151 + </ActionPanel.Section> 52 152 <ActionPanel.Section> 53 153 <Action.Open 54 154 icon={Icon.Code} ··· 126 226 127 227 export default function Command() { 128 228 const [getFilter, setFilter] = useState("all"); 229 + const [getAliases, setAliases] = useState<{ [key: string]: { alias: string } }>({}); 230 + 231 + useEffect(() => { 232 + async function load() { 233 + const profiles = await getAllProfilePreferences(); 234 + setAliases(profiles); 235 + } 236 + load(); 237 + }, []); 129 238 130 239 return ( 131 240 <List ··· 231 340 keywords={ 232 341 item.guid === "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}" || // Windows PowerShell 1.0 (comes with Windows) 233 342 item.guid === "{574e775e-4f2a-5b96-ac1e-a2962a402336}" // Windows Powershell 7.0+ 234 - ? ["pwsh", "ps", "posh"] 343 + ? ["pwsh", "ps", "posh", getAliases[item.guid] ? getAliases[item.guid].alias : ""] 235 344 : item.guid === "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}" 236 - ? ["cmd"] 237 - : [] 345 + ? ["cmd", getAliases[item.guid] ? getAliases[item.guid].alias : ""] 346 + : [ 347 + getAliases[item.guid] ? getAliases[item.guid].alias : "" 348 + ] 349 + } 350 + accessories={getAliases[item.guid] ? [{ tag: { value: getAliases[item.guid].alias } }] : undefined} 351 + actions={ 352 + <Actions profile={item} alias={getAliases[item.guid] ? getAliases[item.guid].alias : ""} /> 238 353 } 239 - actions={<Actions profile={item} />} 240 354 /> 241 355 ))} 242 356 </List.Section> ··· 247 361 {PROFILES.profiles.list 248 362 .filter((item) => item.hidden !== true && item.source === "Windows.Terminal.SSH") 249 363 .map((item) => ( 250 - <List.Item key={item.guid} icon={Icon.Network} title={item.name} actions={<Actions profile={item} />} /> 364 + <List.Item 365 + key={item.guid} 366 + icon={Icon.Network} 367 + title={item.name} 368 + keywords={[getAliases[item.guid] ? getAliases[item.guid].alias : ""]} 369 + accessories={getAliases[item.guid] ? [{ tag: { value: getAliases[item.guid].alias } }] : undefined} 370 + actions={ 371 + <Actions profile={item} alias={getAliases[item.guid] ? getAliases[item.guid].alias : ""} /> 372 + } 373 + /> 251 374 ))} 252 375 </List.Section> 253 376 ) : null} ··· 258 381 {PROFILES.profiles.list 259 382 .filter((item) => item.hidden !== true && item.source === "Windows.Terminal.VisualStudio") 260 383 .map((item) => ( 261 - <List.Item key={item.guid} icon={Icon.Hammer} title={item.name} actions={<Actions profile={item} />} /> 384 + <List.Item 385 + key={item.guid} 386 + icon={Icon.Hammer} 387 + title={item.name} 388 + keywords={[getAliases[item.guid] ? getAliases[item.guid].alias : ""]} 389 + accessories={getAliases[item.guid] ? [{ tag: { value: getAliases[item.guid].alias } }] : undefined} 390 + actions={ 391 + <Actions profile={item} alias={getAliases[item.guid] ? getAliases[item.guid].alias : ""} /> 392 + } 393 + /> 262 394 ))} 263 395 </List.Section> 264 396 ) : null} ··· 274 406 item.hidden !== true && (item.source === "Microsoft.WSL" || item.source === "Windows.Terminal.Wsl"), 275 407 ) 276 408 .map((item) => ( 277 - <List.Item key={item.guid} icon={Icon.HardDrive} title={item.name} actions={<Actions profile={item} />} /> 409 + <List.Item 410 + key={item.guid} 411 + icon={Icon.HardDrive} 412 + title={item.name} 413 + keywords={[getAliases[item.guid] ? getAliases[item.guid].alias : ""]} 414 + accessories={getAliases[item.guid] ? [{ tag: { value: getAliases[item.guid].alias } }] : undefined} 415 + actions={ 416 + <Actions profile={item} alias={getAliases[item.guid] ? getAliases[item.guid].alias : ""} /> 417 + } 418 + /> 278 419 ))} 279 420 </List.Section> 280 421 ) : null}
+24
src/utils/profile-preferences.ts
··· 1 + import { LocalStorage } from "@raycast/api"; 2 + 3 + export async function getProfilePreferences(guid: string): Promise<{ alias: string }> { 4 + const profile = await LocalStorage.getItem<string>(guid); 5 + 6 + if (profile === undefined) { 7 + return { 8 + alias: "", 9 + }; 10 + } else { 11 + return JSON.parse(profile); 12 + } 13 + } 14 + 15 + export async function getAllProfilePreferences(): Promise<{ [key: string]: { alias: string } }> { 16 + const profiles = await LocalStorage.allItems<{ [key: string]: string }>(); 17 + const reconstructedProfiles: { [key: string]: { alias: string } } = {}; 18 + 19 + for (const i in profiles) { 20 + reconstructedProfiles[i] = JSON.parse(profiles[i]); 21 + } 22 + 23 + return reconstructedProfiles; 24 + }

History

4 rounds 2 comments
sign up or login to add to the discussion
7 commits
expand
aliases proof of concept
add better handling of built-in keywords for powershell core
resolve type errors in folder view
added better handling for azure cloud shell profiles
change note from regression to bug in changelog
merge from main branch
add support for aliases in folders
expand 1 comment

development on aliases has been abandoned due to extreme complexity and high implications, focus is now shifting to adding more shorthands as keywords to more profiles instead

closed without merging
6 commits
expand
aliases proof of concept
add better handling of built-in keywords for powershell core
resolve type errors in folder view
added better handling for azure cloud shell profiles
change note from regression to bug in changelog
merge from main branch
expand 0 comments
7 commits
expand
aliases proof of concept
resolve merge conflicts
resolve type errors in folder view
added better handling for azure cloud shell profiles
change note from regression to bug in changelog
merge from main branch
add support for aliases in folders
expand 0 comments
woof.monster submitted #0
1 commit
expand
aliases proof of concept
expand 1 comment

current issues

  • raycast rerenders the alias editor view every time the text entry value changes via user input
  • aliases require relaunching to take effect
  • using the escape key to exit the view is sometimes blocked by the text entry
  • presetting the text entry value results in more issues somehow