feat: Shell 类运行时显示 source 命令(继承环境变量)
- CommandCard 支持 primary/secondary 两种样式 - bash/zsh 使用 source <(curl URL),sh 使用 . <(curl URL),fish 使用 curl URL | source - 非 Shell 类运行时(python3/node/ruby/php)不显示 source 命令 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -2,9 +2,11 @@ import { useState } from 'react'
|
||||
|
||||
interface Props {
|
||||
command: string
|
||||
label?: string
|
||||
variant?: 'primary' | 'secondary'
|
||||
}
|
||||
|
||||
export default function CommandCard({ command }: Props) {
|
||||
export default function CommandCard({ command, label = '运行命令', variant = 'primary' }: Props) {
|
||||
const [copied, setCopied] = useState(false)
|
||||
|
||||
const handleCopy = () => {
|
||||
@@ -14,18 +16,38 @@ export default function CommandCard({ command }: Props) {
|
||||
})
|
||||
}
|
||||
|
||||
const isPrimary = variant === 'primary'
|
||||
|
||||
return (
|
||||
<div className="bg-gray-900 border border-blue-500/30 rounded-lg overflow-hidden">
|
||||
<div className="flex items-center justify-between px-4 py-2 bg-blue-500/10 border-b border-blue-500/20">
|
||||
<span className="text-xs text-blue-400 font-medium">运行命令</span>
|
||||
<div className={`rounded-lg overflow-hidden border ${
|
||||
isPrimary
|
||||
? 'bg-gray-900 border-blue-500/30'
|
||||
: 'bg-gray-900/50 border-gray-700'
|
||||
}`}>
|
||||
<div className={`flex items-center justify-between px-4 py-2 border-b ${
|
||||
isPrimary
|
||||
? 'bg-blue-500/10 border-blue-500/20'
|
||||
: 'bg-gray-800/50 border-gray-700'
|
||||
}`}>
|
||||
<span className={`text-xs font-medium ${
|
||||
isPrimary ? 'text-blue-400' : 'text-gray-400'
|
||||
}`}>
|
||||
{label}
|
||||
</span>
|
||||
</div>
|
||||
<div className="p-4 flex items-center gap-3">
|
||||
<code className="flex-1 text-sm font-mono text-green-400 break-all select-all">
|
||||
<code className={`flex-1 text-sm font-mono break-all select-all ${
|
||||
isPrimary ? 'text-green-400' : 'text-gray-300'
|
||||
}`}>
|
||||
{command}
|
||||
</code>
|
||||
<button
|
||||
onClick={handleCopy}
|
||||
className="shrink-0 px-3 py-1.5 bg-blue-600 hover:bg-blue-700 rounded text-xs font-medium transition-colors"
|
||||
className={`shrink-0 px-3 py-1.5 rounded text-xs font-medium transition-colors ${
|
||||
isPrimary
|
||||
? 'bg-blue-600 hover:bg-blue-700'
|
||||
: 'bg-gray-700 hover:bg-gray-600'
|
||||
}`}
|
||||
>
|
||||
{copied ? '已复制' : '复制'}
|
||||
</button>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CreateScriptResponse } from '../types'
|
||||
import { CreateScriptResponse, isShellRuntime, getSourceCommand } from '../types'
|
||||
import CommandCard from './CommandCard'
|
||||
|
||||
interface Props {
|
||||
@@ -8,6 +8,8 @@ interface Props {
|
||||
|
||||
export default function ResultCard({ result, onReset }: Props) {
|
||||
const detailUrl = `${window.location.origin}/s/${result.id}`
|
||||
const showSource = isShellRuntime(result.runtime)
|
||||
const sourceCommand = showSource ? getSourceCommand(result.url, result.runtime) : null
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
@@ -18,6 +20,19 @@ export default function ResultCard({ result, onReset }: Props) {
|
||||
|
||||
<CommandCard command={result.command} />
|
||||
|
||||
{showSource && sourceCommand && (
|
||||
<div>
|
||||
<p className="text-xs text-gray-500 mb-2">
|
||||
如果脚本设置了环境变量(如代理),请使用以下命令在当前 shell 中执行:
|
||||
</p>
|
||||
<CommandCard
|
||||
command={sourceCommand}
|
||||
label="继承环境变量"
|
||||
variant="secondary"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="bg-gray-800/50 border border-gray-700 rounded-lg p-4 space-y-3">
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="text-gray-400">脚本 ID</span>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useParams, Link } from 'react-router-dom'
|
||||
import ScriptViewer from '../components/ScriptViewer'
|
||||
import CommandCard from '../components/CommandCard'
|
||||
import { getScript } from '../lib/api'
|
||||
import { ScriptDetail as ScriptDetailType } from '../types'
|
||||
import { ScriptDetail as ScriptDetailType, isShellRuntime, getSourceCommand } from '../types'
|
||||
|
||||
export default function ScriptDetail() {
|
||||
const { id } = useParams<{ id: string }>()
|
||||
@@ -41,6 +41,10 @@ export default function ScriptDetail() {
|
||||
}
|
||||
|
||||
const command = `curl ${window.location.origin}/raw/${script.id} | ${script.runtime}`
|
||||
const showSource = isShellRuntime(script.runtime)
|
||||
const sourceCommand = showSource
|
||||
? getSourceCommand(`${window.location.origin}/raw/${script.id}`, script.runtime)
|
||||
: null
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -64,6 +68,18 @@ export default function ScriptDetail() {
|
||||
|
||||
<div className="mt-8">
|
||||
<CommandCard command={command} />
|
||||
{showSource && sourceCommand && (
|
||||
<div className="mt-4">
|
||||
<p className="text-xs text-gray-500 mb-2">
|
||||
如果脚本设置了环境变量(如代理),请使用以下命令在当前 shell 中执行:
|
||||
</p>
|
||||
<CommandCard
|
||||
command={sourceCommand}
|
||||
label="继承环境变量"
|
||||
variant="secondary"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mt-8 text-center">
|
||||
|
||||
@@ -37,3 +37,18 @@ export const EXPIRES_OPTIONS: { value: ExpiresIn; label: string }[] = [
|
||||
{ value: '7d', label: '7 天' },
|
||||
{ value: '30d', label: '30 天' },
|
||||
]
|
||||
|
||||
const SOURCE_TEMPLATES: Record<string, string> = {
|
||||
bash: 'source <(curl {url})',
|
||||
zsh: 'source <(curl {url})',
|
||||
sh: '. <(curl {url})',
|
||||
fish: 'curl {url} | source',
|
||||
}
|
||||
|
||||
export function isShellRuntime(runtime: string): boolean {
|
||||
return runtime in SOURCE_TEMPLATES
|
||||
}
|
||||
|
||||
export function getSourceCommand(url: string, runtime: string): string {
|
||||
return (SOURCE_TEMPLATES[runtime] ?? 'source <(curl {url})').replace('{url}', url)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user