[eric] marketplace: plain words everywhere; Install, Open, Installed, Bundles, Everything, Browse, Type, and a review sheet that says Install

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C9zwUaHucUgrdxvK8FvjYT
This commit is contained in:
ciregenz
2026-09-03 10:19:40 -07:00
co-authored by Claude Fable 5.1
parent e2fd38d17a
commit 3c1b28cffd
10 changed files with 25 additions and 25 deletions
@@ -44,7 +44,7 @@ const ImportModal: React.FC<Props> = ({ preflight, open, committing, onConfirm,
<>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', px: 3, pt: 2.5, pb: 1 }}>
<Typography sx={{ fontSize: '1rem', fontWeight: 700, color: c.text.primary }}>
Add {preflight.summary.root.name}?
Install {preflight.summary.root.name}?
</Typography>
<IconButton size="small" onClick={onClose} sx={{ color: c.text.tertiary }}>
<CloseIcon sx={{ fontSize: 18 }} />
@@ -55,7 +55,7 @@ const ImportModal: React.FC<Props> = ({ preflight, open, committing, onConfirm,
{preflight.review && preflight.review.findings.length > 0 && <ReviewFindings review={preflight.review} />}
{preflight.conflicts.length > 0 && (
<Typography sx={{ fontSize: '0.75rem', color: c.text.muted, mt: 1.5 }}>
Some items already exist and will be added as copies.
Some of this already exists; copies will be added.
</Typography>
)}
<Box sx={{ display: 'flex', justifyContent: 'flex-end', gap: 1, mt: 2 }}>
@@ -80,7 +80,7 @@ const ImportModal: React.FC<Props> = ({ preflight, open, committing, onConfirm,
boxShadow: 'none',
}}
>
Add to OpenSwarm
Install
</Button>
</Box>
</Box>
@@ -110,7 +110,7 @@ const DirectoryPackagesTab: React.FC<{ onOpenSkill?: (skillId: string) => void }
setInstalls(await recordInstall(recordFor(listingId, rootType, rootId, version)));
} catch (e: unknown) {
setInstalls((prev) => ({ ...prev, [listingId]: { ...recordFor(listingId, rootType, rootId, version), installed_at: Date.now() / 1000 } }));
setToast({ message: e instanceof Error ? e.message : "Installed, but the store couldn't remember it.", severity: 'error' });
setToast({ message: e instanceof Error ? e.message : 'Installed, but it may show Install again after a restart.', severity: 'error' });
}
};
@@ -121,7 +121,7 @@ const DirectoryPackagesTab: React.FC<{ onOpenSkill?: (skillId: string) => void }
await finish(listingId, res.root_type, res.root_id);
setConfirm(null);
} catch (e: unknown) {
setToast({ message: e instanceof Error ? e.message : "We couldn't finish the install.", severity: 'error' });
setToast({ message: e instanceof Error ? e.message : "Couldn't finish installing.", severity: 'error' });
} finally {
setCommitting(false);
}
@@ -134,7 +134,7 @@ const DirectoryPackagesTab: React.FC<{ onOpenSkill?: (skillId: string) => void }
if (marketplaceNeedsConfirm(preflight)) setConfirm({ preflight, listingId });
else await commit(preflight, listingId);
} catch (e: unknown) {
setToast({ message: e instanceof Error ? e.message : "We couldn't download this package.", severity: 'error' });
setToast({ message: e instanceof Error ? e.message : "Couldn't download it. Try again.", severity: 'error' });
} finally {
setInstallingId(null);
}
@@ -143,7 +143,7 @@ const DirectoryPackagesTab: React.FC<{ onOpenSkill?: (skillId: string) => void }
const installBundle = async (bundle: Listing) => {
const members = resolveBundleMembers(bundle, listings).filter((m) => m.download_url);
if (members.length === 0) {
setToast({ message: 'This bundle has no installable packages yet.', severity: 'error' });
setToast({ message: 'Nothing in this bundle can be installed yet.', severity: 'error' });
return;
}
// One at a time: each member gets its own review, and a bundle must not be a way to skip one.
@@ -177,7 +177,7 @@ const DirectoryPackagesTab: React.FC<{ onOpenSkill?: (skillId: string) => void }
if (visible.length === 0 && visibleBundles.length === 0) {
return (
<Typography sx={{ fontSize: '0.9375rem', color: c.text.tertiary, pt: 6, textAlign: 'center' }}>
{error ? error : 'No packages match that search yet.'}
{error ? error : 'Nothing matches that search.'}
</Typography>
);
}
@@ -185,7 +185,7 @@ const DirectoryPackagesTab: React.FC<{ onOpenSkill?: (skillId: string) => void }
<>
{visibleBundles.length > 0 && (
<Box sx={{ mb: 3 }}>
{sectionLabel('Collections')}
{sectionLabel('Bundles')}
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
{visibleBundles.map((bundle) => (
<PackageBundleCard
@@ -198,7 +198,7 @@ const DirectoryPackagesTab: React.FC<{ onOpenSkill?: (skillId: string) => void }
</Box>
</Box>
)}
{visible.length > 0 && sectionLabel(visibleBundles.length > 0 ? 'All packages' : '', visible.length)}
{visible.length > 0 && sectionLabel(visibleBundles.length > 0 ? 'Everything' : '', visible.length)}
<Box
sx={{
display: 'grid',
@@ -227,16 +227,16 @@ const DirectoryPackagesTab: React.FC<{ onOpenSkill?: (skillId: string) => void }
return (
<Box sx={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: 0, gap: 1.75 }}>
<DirectoryFilterBar
searchPlaceholder="Search packages, tags, authors"
searchPlaceholder="Search"
query={query}
onQuery={setQuery}
filterSections={[{ label: 'Kind', options: kindOptions }]}
filterSections={[{ label: 'Type', options: kindOptions }]}
filterSelected={kinds}
onToggleFilter={(value) => setKinds((prev) => (prev.includes(value) ? prev.filter((k) => k !== value) : [...prev, value]))}
sortOptions={[
{ value: 'newest', label: 'Newest' },
{ value: 'name', label: 'Name' },
{ value: 'kind', label: 'Kind' },
{ value: 'kind', label: 'Type' },
]}
sortValue={sort}
onSort={setSort}
@@ -250,7 +250,7 @@ const DirectoryPackagesTab: React.FC<{ onOpenSkill?: (skillId: string) => void }
/>
{source === 'cache' && (
<Typography sx={{ fontSize: '0.8125rem', color: c.text.muted }}>
Showing the last catalog we loaded; the marketplace could not be reached just now.
Showing the last list we loaded. The store can't be reached right now.
</Typography>
)}
<Box sx={{ flex: 1, minHeight: 0, overflowY: 'auto', pr: 0.5 }}>{body()}</Box>
@@ -80,7 +80,7 @@ const MarketplaceBody: React.FC = () => {
<Box sx={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: 0, bgcolor: c.bg.surface }}>
<Box sx={{ display: 'flex', flex: 1, minHeight: 0, pt: 1.5 }}>
<Box sx={{ width: 210, minWidth: 210, px: 2, pt: 0.5, display: 'flex', flexDirection: 'column', gap: 0.5 }}>
{railRow('packages', 'Packages', <Inventory2OutlinedIcon sx={{ fontSize: 19, color: c.text.secondary }} />)}
{railRow('packages', 'Browse', <Inventory2OutlinedIcon sx={{ fontSize: 19, color: c.text.secondary }} />)}
{railRow('connectors', 'Connectors', <GridViewOutlinedIcon sx={{ fontSize: 19, color: c.text.secondary }} />)}
<Box sx={{ height: '1px', bgcolor: c.border.subtle, mx: 1.5, my: 1 }} />
{railRow('my-skills', 'My skills', <FolderSpecialOutlinedIcon sx={{ fontSize: 19, color: c.text.secondary }} />)}
@@ -13,7 +13,7 @@ interface Props {
size?: 'sm' | 'md';
}
// The one action a package has, the way the App Store draws it: Get, a spinner while it lands, then Open. It swallows the click so a card underneath never opens its sheet.
// The one action a package has, the way the App Store draws it: Install, a spinner while it lands, then Open. It swallows the click so a card underneath never opens its sheet.
export default function InstallPill({ state, disabled, onGet, onOpen, size = 'md' }: Props) {
const c = useClaudeTokens();
const sm = size === 'sm';
@@ -51,7 +51,7 @@ export default function InstallPill({ state, disabled, onGet, onOpen, size = 'md
disableElevation
sx={{ ...base, bgcolor: c.accent.primary, color: c.text.inverse, '&:hover': { bgcolor: c.accent.hover }, '&.Mui-disabled': { bgcolor: c.accent.primary, color: c.text.inverse, opacity: state === 'installing' ? 1 : 0.5 } }}
>
{state === 'installing' ? <CircularProgress size={14} thickness={5} sx={{ color: 'inherit' }} /> : 'Get'}
{state === 'installing' ? <CircularProgress size={14} thickness={5} sx={{ color: 'inherit' }} /> : 'Install'}
</Button>
);
}
@@ -74,7 +74,7 @@ export default function PackageBundleCard({ bundle, members, onOpen }: Props) {
{bundle.title}
</Typography>
<Typography sx={{ fontSize: '0.75rem', color: c.text.muted, flexShrink: 0 }}>
{members.length} {members.length === 1 ? 'package' : 'packages'}
{members.length} {members.length === 1 ? 'item' : 'items'}
</Typography>
</Stack>
<Typography
@@ -95,7 +95,7 @@ export default function PackageCard({ listing, state, onOpen, onGet, onOpenInsta
minHeight: '2.6em',
}}
>
{listing.description || 'No description provided.'}
{listing.description || 'No description yet.'}
</Typography>
<PackageTagRow tags={tags} onTag={onTag} sx={{ mt: 'auto' }} />
@@ -52,7 +52,7 @@ test('Packages is the default marketplace tab and the old skills store is gone',
const dir = path.join(process.cwd(), 'src/app/pages/Directory');
const body = fs.readFileSync(path.join(dir, 'MarketplaceBody.tsx'), 'utf8');
assert.match(body, /useState<DirectoryTab>\('packages'\)/);
assert.match(body, /railRow\('packages', 'Packages'/);
assert.match(body, /railRow\('packages', 'Browse'/);
assert.doesNotMatch(body, /DirectorySkillsTab/);
assert.equal(fs.existsSync(path.join(dir, 'DirectorySkillsTab.tsx')), false);
const open = fs.readFileSync(path.join(dir, 'openMarketplace.ts'), 'utf8');
@@ -67,7 +67,7 @@ export default function PackageBundleDialog({ bundle, members, stateOf, onOpenIn
{bundle.title}
</Typography>
<Typography sx={{ mt: 0.4, fontSize: '0.8125rem', color: c.text.muted }}>
{members.length} {members.length === 1 ? 'package' : 'packages'}{bundle.author ? ` · ${bundle.author}` : ''}
{members.length} {members.length === 1 ? 'item' : 'items'}{bundle.author ? ` · ${bundle.author}` : ''}
</Typography>
</Box>
<Button
@@ -77,7 +77,7 @@ export default function PackageBundleDialog({ bundle, members, stateOf, onOpenIn
disableElevation
sx={{ ...pill, bgcolor: c.accent.primary, color: c.text.inverse, '&:hover': { bgcolor: c.accent.hover }, '&.Mui-disabled': { bgcolor: c.bg.secondary, color: c.text.muted } }}
>
{installing ? <CircularProgress size={14} thickness={5} sx={{ color: 'inherit' }} /> : allInstalled ? 'Installed' : 'Get all'}
{installing ? <CircularProgress size={14} thickness={5} sx={{ color: 'inherit' }} /> : allInstalled ? 'Installed' : 'Install all'}
</Button>
</Stack>
@@ -92,7 +92,7 @@ export default function PackageBundleDialog({ bundle, members, stateOf, onOpenIn
Included
</Typography>
{members.length === 0 ? (
<Typography sx={{ color: c.text.tertiary, fontSize: '0.875rem' }}>The packages in this bundle are no longer available.</Typography>
<Typography sx={{ color: c.text.tertiary, fontSize: '0.875rem' }}>The items in this bundle are no longer available.</Typography>
) : (
<Stack spacing={0.75}>
{members.map((m) => {
@@ -12,7 +12,7 @@ export async function stagePackageInstall(listingId: string): Promise<ImportPref
body: JSON.stringify({ id: listingId }),
});
if (!res.ok) {
let detail = "We couldn't download this package.";
let detail = "Couldn't download it. Try again.";
try {
const body = (await res.json()) as { detail?: string };
if (body.detail) detail = body.detail;
@@ -54,7 +54,7 @@ export async function recordInstall(rec: Omit<InstallRecord, 'installed_at'>): P
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ...rec, installed_at: 0 }),
});
if (!res.ok) throw new Error("Installed, but the store couldn't remember it.");
if (!res.ok) throw new Error('Installed, but it may show Install again after a restart.');
const body = (await res.json()) as { installs: Record<string, InstallRecord> };
return body.installs || {};
}