mirror of
http://10.0.2.1:3031/sauer/claude-code.git
synced 2026-06-30 16:26:58 +10:00
162 lines
21 KiB
TypeScript
162 lines
21 KiB
TypeScript
|
|
import * as React from 'react';
|
||
|
|
import { useEffect, useRef, useState } from 'react';
|
||
|
|
import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent } from 'src/services/analytics/index.js';
|
||
|
|
import { ConfigurableShortcutHint } from '../../components/ConfigurableShortcutHint.js';
|
||
|
|
import { Byline } from '../../components/design-system/Byline.js';
|
||
|
|
import { KeyboardShortcutHint } from '../../components/design-system/KeyboardShortcutHint.js';
|
||
|
|
import { Spinner } from '../../components/Spinner.js';
|
||
|
|
import TextInput from '../../components/TextInput.js';
|
||
|
|
import { Box, Text } from '../../ink.js';
|
||
|
|
import { toError } from '../../utils/errors.js';
|
||
|
|
import { logError } from '../../utils/log.js';
|
||
|
|
import { clearAllCaches } from '../../utils/plugins/cacheUtils.js';
|
||
|
|
import { addMarketplaceSource, saveMarketplaceToSettings } from '../../utils/plugins/marketplaceManager.js';
|
||
|
|
import { parseMarketplaceInput } from '../../utils/plugins/parseMarketplaceInput.js';
|
||
|
|
import type { ViewState } from './types.js';
|
||
|
|
type Props = {
|
||
|
|
inputValue: string;
|
||
|
|
setInputValue: (value: string) => void;
|
||
|
|
cursorOffset: number;
|
||
|
|
setCursorOffset: (offset: number) => void;
|
||
|
|
error: string | null;
|
||
|
|
setError: (error: string | null) => void;
|
||
|
|
result: string | null;
|
||
|
|
setResult: (result: string | null) => void;
|
||
|
|
setViewState: (state: ViewState) => void;
|
||
|
|
onAddComplete?: () => void | Promise<void>;
|
||
|
|
cliMode?: boolean;
|
||
|
|
};
|
||
|
|
export function AddMarketplace({
|
||
|
|
inputValue,
|
||
|
|
setInputValue,
|
||
|
|
cursorOffset,
|
||
|
|
setCursorOffset,
|
||
|
|
error,
|
||
|
|
setError,
|
||
|
|
result,
|
||
|
|
setResult,
|
||
|
|
setViewState,
|
||
|
|
onAddComplete,
|
||
|
|
cliMode = false
|
||
|
|
}: Props): React.ReactNode {
|
||
|
|
const hasAttemptedAutoAdd = useRef(false);
|
||
|
|
const [isLoading, setLoading] = useState(false);
|
||
|
|
const [progressMessage, setProgressMessage] = useState<string>('');
|
||
|
|
const handleAdd = async () => {
|
||
|
|
const input = inputValue.trim();
|
||
|
|
if (!input) {
|
||
|
|
setError('Please enter a marketplace source');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const parsed = await parseMarketplaceInput(input);
|
||
|
|
if (!parsed) {
|
||
|
|
setError('Invalid marketplace source format. Try: owner/repo, https://..., or ./path');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check if parseMarketplaceInput returned an error
|
||
|
|
if ('error' in parsed) {
|
||
|
|
setError(parsed.error);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
setError(null);
|
||
|
|
try {
|
||
|
|
setLoading(true);
|
||
|
|
setProgressMessage('');
|
||
|
|
const {
|
||
|
|
name,
|
||
|
|
resolvedSource
|
||
|
|
} = await addMarketplaceSource(parsed, message => {
|
||
|
|
setProgressMessage(message);
|
||
|
|
});
|
||
|
|
saveMarketplaceToSettings(name, {
|
||
|
|
source: resolvedSource
|
||
|
|
});
|
||
|
|
clearAllCaches();
|
||
|
|
let sourceType = parsed.source;
|
||
|
|
if (parsed.source === 'github') {
|
||
|
|
sourceType = parsed.repo as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS;
|
||
|
|
}
|
||
|
|
logEvent('tengu_marketplace_added', {
|
||
|
|
source_type: sourceType as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS
|
||
|
|
});
|
||
|
|
if (onAddComplete) {
|
||
|
|
await onAddComplete();
|
||
|
|
}
|
||
|
|
setProgressMessage('');
|
||
|
|
setLoading(false);
|
||
|
|
if (cliMode) {
|
||
|
|
// In CLI mode, set result to trigger completion
|
||
|
|
setResult(`Successfully added marketplace: ${name}`);
|
||
|
|
} else {
|
||
|
|
// In interactive mode, switch to browse view
|
||
|
|
setViewState({
|
||
|
|
type: 'browse-marketplace',
|
||
|
|
targetMarketplace: name
|
||
|
|
});
|
||
|
|
}
|
||
|
|
} catch (err) {
|
||
|
|
const error = toError(err);
|
||
|
|
logError(error);
|
||
|
|
setError(error.message);
|
||
|
|
setProgressMessage('');
|
||
|
|
setLoading(false);
|
||
|
|
if (cliMode) {
|
||
|
|
// In CLI mode, set result with error to trigger completion
|
||
|
|
setResult(`Error: ${error.message}`);
|
||
|
|
} else {
|
||
|
|
setResult(null);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// Auto-add if inputValue is provided
|
||
|
|
useEffect(() => {
|
||
|
|
if (inputValue && !hasAttemptedAutoAdd.current && !error && !result) {
|
||
|
|
hasAttemptedAutoAdd.current = true;
|
||
|
|
void handleAdd();
|
||
|
|
}
|
||
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||
|
|
// biome-ignore lint/correctness/useExhaustiveDependencies: intentional
|
||
|
|
}, []); // Only run once on mount
|
||
|
|
|
||
|
|
return <Box flexDirection="column">
|
||
|
|
<Box flexDirection="column" paddingX={1} borderStyle="round">
|
||
|
|
<Box marginBottom={1}>
|
||
|
|
<Text bold>Add Marketplace</Text>
|
||
|
|
</Box>
|
||
|
|
<Box flexDirection="column">
|
||
|
|
<Text>Enter marketplace source:</Text>
|
||
|
|
<Text dimColor>Examples:</Text>
|
||
|
|
<Text dimColor> · owner/repo (GitHub)</Text>
|
||
|
|
<Text dimColor> · git@github.com:owner/repo.git (SSH)</Text>
|
||
|
|
<Text dimColor> · https://example.com/marketplace.json</Text>
|
||
|
|
<Text dimColor> · ./path/to/marketplace</Text>
|
||
|
|
<Box marginTop={1}>
|
||
|
|
<TextInput value={inputValue} onChange={setInputValue} onSubmit={handleAdd} columns={80} cursorOffset={cursorOffset} onChangeCursorOffset={setCursorOffset} focus showCursor />
|
||
|
|
</Box>
|
||
|
|
</Box>
|
||
|
|
{isLoading && <Box marginTop={1}>
|
||
|
|
<Spinner />
|
||
|
|
<Text>
|
||
|
|
{progressMessage || 'Adding marketplace to configuration…'}
|
||
|
|
</Text>
|
||
|
|
</Box>}
|
||
|
|
{error && <Box marginTop={1}>
|
||
|
|
<Text color="error">{error}</Text>
|
||
|
|
</Box>}
|
||
|
|
{result && <Box marginTop={1}>
|
||
|
|
<Text>{result}</Text>
|
||
|
|
</Box>}
|
||
|
|
</Box>
|
||
|
|
<Box marginLeft={3}>
|
||
|
|
<Text dimColor italic>
|
||
|
|
<Byline>
|
||
|
|
<KeyboardShortcutHint shortcut="Enter" action="add" />
|
||
|
|
<ConfigurableShortcutHint action="confirm:no" context="Settings" fallback="Esc" description="cancel" />
|
||
|
|
</Byline>
|
||
|
|
</Text>
|
||
|
|
</Box>
|
||
|
|
</Box>;
|
||
|
|
}
|
||
|
|
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUVmZmVjdCIsInVzZVJlZiIsInVzZVN0YXRlIiwiQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyIsImxvZ0V2ZW50IiwiQ29uZmlndXJhYmxlU2hvcnRjdXRIaW50IiwiQnlsaW5lIiwiS2V5Ym9hcmRTaG9ydGN1dEhpbnQiLCJTcGlubmVyIiwiVGV4dElucHV0IiwiQm94IiwiVGV4dCIsInRvRXJyb3IiLCJsb2dFcnJvciIsImNsZWFyQWxsQ2FjaGVzIiwiYWRkTWFya2V0cGxhY2VTb3VyY2UiLCJzYXZlTWFya2V0cGxhY2VUb1NldHRpbmdzIiwicGFyc2VNYXJrZXRwbGFjZUlucHV0IiwiVmlld1N0YXRlIiwiUHJvcHMiLCJpbnB1dFZhbHVlIiwic2V0SW5wdXRWYWx1ZSIsInZhbHVlIiwiY3Vyc29yT2Zmc2V0Iiwic2V0Q3Vyc29yT2Zmc2V0Iiwib2Zmc2V0IiwiZXJyb3IiLCJzZXRFcnJvciIsInJlc3VsdCIsInNldFJlc3VsdCIsInNldFZpZXdTdGF0ZSIsInN0YXRlIiwib25BZGRDb21wbGV0ZSIsIlByb21pc2UiLCJjbGlNb2RlIiwiQWRkTWFya2V0cGxhY2UiLCJSZWFjdE5vZGUiLCJoYXNBdHRlbXB0ZWRBdXRvQWRkIiwiaXNMb2FkaW5nIiwic2V0TG9hZGluZyIsInByb2dyZXNzTWVzc2FnZSIsInNldFByb2dyZXNzTWVzc2FnZSIsImhhbmRsZUFkZCIsImlucHV0IiwidHJpbSIsInBhcnNlZCIsIm5hbWUiLCJyZXNvbHZlZFNvdXJjZSIsIm1lc3NhZ2UiLCJzb3VyY2UiLCJzb3VyY2VUeXBlIiwicmVwbyIsInNvdXJjZV90eXBlIiwidHlwZSIsInRhcmdldE1hcmtldHBsYWNlIiwiZXJyIiwiY3VycmVudCJdLCJzb3VyY2VzIjpbIkFkZE1hcmtldHBsYWNlLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHVzZUVmZmVjdCwgdXNlUmVmLCB1c2VTdGF0ZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHtcbiAgdHlwZSBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICBsb2dFdmVudCxcbn0gZnJvbSAnc3JjL3NlcnZpY2VzL2FuYWx5dGljcy9pbmRleC5qcydcbmltcG9ydCB7IENvbmZpZ3VyYWJsZVNob3J0Y3V0SGludCB9IGZyb20gJy4uLy4uL2NvbXBvbmVudHMvQ29uZmlndXJhYmxlU2hvcnRjdXRIaW50LmpzJ1xuaW1wb3J0IHsgQnlsaW5lIH0gZnJvbSAnLi4vLi4vY29tcG9uZW50cy9kZXNpZ24tc3lzdGVtL0J5bGluZS5qcydcbmltcG9ydCB7IEtleWJvYXJkU2hvcnRjdXRIaW50IH0gZnJvbSAnLi4vLi4vY29tcG9uZW50cy9kZXNpZ24tc3lzdGVtL0tleWJvYXJkU2hvcnRjdXRIaW50LmpzJ1xuaW1wb3J0IHsgU3Bpbm5lciB9IGZyb20gJy4uLy4uL2NvbXBvbmVudHMvU3Bpbm5lci5qcydcbmltcG9ydCBUZXh0SW5wdXQgZnJvbSAnLi4vLi4vY29tcG9uZW50cy9UZXh0SW5wdXQuanMnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQgeyB0b0Vycm9yIH0gZnJvbSAnLi4vLi4vdXRpbHMvZXJyb3JzLmpzJ1xuaW1wb3J0IHsgbG9nRXJyb3IgfSBmcm9tICcuLi8uLi91dGlscy9sb2cuanMnXG5pbXBvcnQgeyBjbGVhckFsbENhY2hlcyB9IGZyb20gJy4uLy4uL3V0aWxzL3BsdWdpbnMvY2FjaGVVdGlscy5qcydcbmltcG9ydCB7XG4gIGFkZE1hcmtldHBsYWNlU291cmNlLFxuICBzYXZlTWFya2V0cGxhY2VUb1NldHRpbmdzLFxufSBmcm9tICcuLi8uLi91dGlscy9wbHVnaW5zL21hcmtldHBsYWNlTWFuYWdlci5qcydcbmltcG9ydCB7IHBhcnNlTWFya2V0cGxhY2VJbnB1dCB9IGZyb20gJy4uLy4uL3V0aWxzL3BsdWdpbnMvcGFyc2VNYXJrZXRwbGFjZUlucHV0LmpzJ1xuaW1wb3J0IHR5cGUgeyBWaWV3U3RhdGUgfSBmcm9tICcuL3R5cGVzLmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICBpbnB1dFZhbHVlOiBzdHJpbmdcbiAgc2V0SW5wdXRWYWx1ZTogKHZhbHVlOiBzdHJpbmcpID0+IHZvaWRcbiAgY3Vyc29yT2Zmc2V0OiBudW1iZXJcbiAgc2V0Q3Vyc29yT2Zmc2V0OiAob2Zmc2V0OiBudW1iZXIpID0+IHZvaWRcbiAgZXJyb3I6IHN0cmluZyB8IG51bGxcbiAgc2V0RXJyb3I6IChlcnJvcjogc3RyaW5nIHwgbnVsbCkgPT4gdm9pZFxuICByZXN1bHQ6IHN0cmluZyB8IG51bGxcbiAgc2V0UmVzdWx0OiAocmVzdWx0OiBzdHJpbmcgfCBudWxsKSA9PiB2b2lkXG4gIHNldFZpZXdTdGF0ZTogKHN0YXRlOiBWaWV3U3RhdGUpID0+IHZvaWRcbiAgb25BZGRDb21wbGV0ZT86ICgpID0+IHZvaWQgfCBQcm9taXNlPHZvaWQ+XG4gIGNsaU1vZGU/OiBib29sZWFuXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBBZGRNYXJrZXRwbGFjZSh7XG4gIGlucHV0VmFsdWUsXG4gIHNldElucHV0VmFsdWUsXG4gIGN1cnNvck9mZnNldCxcbiAgc2V0Q3Vyc29yT2Zmc2V0LFxuICBlcnJvcixcbiAgc2V0RXJyb3IsXG4gIHJlc3VsdCxcbiAgc2V0UmVzdWx0LFxuICBzZXRWaWV3U3RhdGUsXG4gIG9uQWRkQ29tcGxldGUsXG4gIGNsaU1vZGUgPSBmYWxzZSxcbn06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgaGFzQXR0ZW1wdGVkQXV0b0FkZCA9IHVzZVJlZihmYWxzZSlcbiAgY29uc3QgW2lzTG9hZGluZywgc2V0TG9hZGluZ10gPSB1c2VTdGF0ZShmYWxzZSlcbiAgY29uc3QgW3Byb2dyZXNzTWVzc2FnZSwgc2V0UHJvZ3Jlc3NNZXNzYWdlXSA9IHVzZVN0YXRlPHN0cmluZz4oJycpXG5cbiAgY29uc3QgaGFuZGxlQWRkID0gYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IGlucHV0ID0gaW5wdXRWYWx1ZS50cmltKClcbiAgICBpZiAoIWlucHV0KSB7XG4gICAgICBzZXRFcnJvcignUGxlYXNlIGVudGVyIGEgbWFya2V0cGxhY2Ugc291cmNlJylcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIGNvbnN0IHBhcnNlZCA9IGF3YWl0IHBhcnNlTWFya2V0cGxhY2VJbnB1dChpbnB1dClcbiAgICBpZiAoIXBhcnNlZCkge1xuICAgICAgc2V0RXJyb3IoXG4gICAgICAgICdJbnZhbGlkIG1hcmtldHBsYWNlIHNvdXJjZSBmb3JtYXQuIFRyeTogb3duZXIvcmV
|