From 0ab66c48547aa6eb16c5babac2c616c7f7f2b9eb Mon Sep 17 00:00:00 2001 From: Nick Wesselman <27013789+nickwesselman@users.noreply.github.com> Date: Mon, 13 Apr 2026 11:03:55 -0400 Subject: [PATCH] DRY up shortcut and link rendering in DevSessionUI Extract devStatusShortcuts array with shortcutLabel, linkLabel, url, condition, and action fields. Both the keyboard shortcut hints and the URL list are now rendered by iterating over the same collection, eliminating duplicated conditions and labels. Co-Authored-By: Claude Opus 4.6 --- .../dev/ui/components/DevSessionUI.tsx | 151 ++++++++---------- .../services/dev/ui/components/TabPanel.tsx | 2 +- 2 files changed, 72 insertions(+), 81 deletions(-) diff --git a/packages/app/src/cli/services/dev/ui/components/DevSessionUI.tsx b/packages/app/src/cli/services/dev/ui/components/DevSessionUI.tsx index 4c612ca9ee6..0b5e53a4d0a 100644 --- a/packages/app/src/cli/services/dev/ui/components/DevSessionUI.tsx +++ b/packages/app/src/cli/services/dev/ui/components/DevSessionUI.tsx @@ -1,5 +1,5 @@ import {Spinner} from './Spinner.js' -import {TabPanel, Tab} from './TabPanel.js' +import {TabPanel, Tab, TabShortcut} from './TabPanel.js' import metadata from '../../../../metadata.js' import { DevSessionStatus, @@ -22,6 +22,12 @@ import {treeKill} from '@shopify/cli-kit/node/tree-kill' import {postRunHookHasCompleted} from '@shopify/cli-kit/node/hooks/postrun' import {Writable} from 'stream' +interface DevStatusShortcut extends TabShortcut { + shortcutLabel: string + linkLabel: string + url?: string +} + interface DevSesionUIProps { processes: OutputProcess[] abortController: AbortController @@ -119,46 +125,59 @@ const DevSessionUI: FunctionComponent = ({ } } + const devStatusShortcuts: DevStatusShortcut[] = [ + { + key: 'p', + shortcutLabel: 'Open app preview', + linkLabel: 'Preview', + url: status.previewURL, + condition: () => Boolean(status.isReady && status.previewURL), + action: async () => { + await metadata.addPublicMetadata(() => ({ + cmd_dev_preview_url_opened: true, + })) + if (status.previewURL) { + await openURL(status.previewURL) + } + }, + }, + { + key: 'c', + shortcutLabel: 'Open Dev Console for extension previews', + linkLabel: 'Dev Console', + url: buildDevConsoleURL(shopFqdn), + condition: () => Boolean(status.isReady && status.appEmbedded === false && status.hasExtensions), + action: async () => { + await metadata.addPublicMetadata(() => ({ + cmd_dev_preview_url_opened: true, + })) + await openURL(buildDevConsoleURL(shopFqdn)) + }, + }, + { + key: 'g', + shortcutLabel: 'Open GraphiQL (Admin API)', + linkLabel: 'GraphiQL', + url: status.graphiqlURL, + condition: () => Boolean(status.isReady && status.graphiqlURL), + action: async () => { + await metadata.addPublicMetadata(() => ({ + cmd_dev_graphiql_opened: true, + })) + if (status.graphiqlURL) { + await openURL(status.graphiqlURL) + } + }, + }, + ] + + const activeShortcuts = devStatusShortcuts.filter((shortcut) => shortcut.condition?.() ?? true) + const tabs: {[key: string]: Tab} = { // eslint-disable-next-line id-length d: { label: 'Dev status', - shortcuts: [ - { - key: 'p', - condition: () => Boolean(status.isReady && status.previewURL), - action: async () => { - await metadata.addPublicMetadata(() => ({ - cmd_dev_preview_url_opened: true, - })) - if (status.previewURL) { - await openURL(status.previewURL) - } - }, - }, - { - key: 'g', - condition: () => Boolean(status.isReady && status.graphiqlURL), - action: async () => { - await metadata.addPublicMetadata(() => ({ - cmd_dev_graphiql_opened: true, - })) - if (status.graphiqlURL) { - await openURL(status.graphiqlURL) - } - }, - }, - { - key: 'c', - condition: () => Boolean(status.isReady && status.appEmbedded === false && status.hasExtensions), - action: async () => { - await metadata.addPublicMetadata(() => ({ - cmd_dev_preview_url_opened: true, - })) - await openURL(buildDevConsoleURL(shopFqdn)) - }, - }, - ], + shortcuts: devStatusShortcuts, content: ( <> {status.statusMessage && ( @@ -166,38 +185,18 @@ const DevSessionUI: FunctionComponent = ({ {getStatusIndicator(status.statusMessage.type)} {status.statusMessage.message} )} - {canUseShortcuts && ( + {canUseShortcuts && activeShortcuts.length > 0 && ( - {status.isReady && status.previewURL ? ( - - {figures.pointerSmall} (p){' '} - {terminalSupportsHyperlinks() ? ( - - ) : ( - 'Open app preview' - )} - - ) : null} - {status.isReady && !status.appEmbedded && status.hasExtensions ? ( - - {figures.pointerSmall} (c){' '} - {terminalSupportsHyperlinks() ? ( - - ) : ( - 'Open Dev Console for extension previews' - )} - - ) : null} - {status.isReady && status.graphiqlURL ? ( - - {figures.pointerSmall} (g){' '} - {terminalSupportsHyperlinks() ? ( - + {activeShortcuts.map((shortcut) => ( + + {figures.pointerSmall} ({shortcut.key}){' '} + {terminalSupportsHyperlinks() && shortcut.url ? ( + ) : ( - 'Open GraphiQL (Admin API)' + shortcut.shortcutLabel )} - ) : null} + ))} )} @@ -207,21 +206,13 @@ const DevSessionUI: FunctionComponent = ({ <> {status.isReady && !(canUseShortcuts && terminalSupportsHyperlinks()) && ( <> - {status.previewURL ? ( - - Preview URL: - - ) : null} - {status.appEmbedded === false && status.hasExtensions ? ( - - Dev Console URL: - - ) : null} - {status.graphiqlURL ? ( - - GraphiQL URL: - - ) : null} + {activeShortcuts + .filter((shortcut) => shortcut.url) + .map((shortcut) => ( + + {shortcut.linkLabel} URL: + + ))} )} diff --git a/packages/app/src/cli/services/dev/ui/components/TabPanel.tsx b/packages/app/src/cli/services/dev/ui/components/TabPanel.tsx index 0c726d9f8ab..3415f7b6027 100644 --- a/packages/app/src/cli/services/dev/ui/components/TabPanel.tsx +++ b/packages/app/src/cli/services/dev/ui/components/TabPanel.tsx @@ -8,7 +8,7 @@ export interface Tab { action?: () => Promise } -interface TabShortcut { +export interface TabShortcut { key: string condition?: () => boolean action: () => Promise