From 374ded021441c504c93308087264a187dd83ae28 Mon Sep 17 00:00:00 2001 From: ciregenz Date: Tue, 16 Jun 2026 05:08:09 -0700 Subject: [PATCH] [eric] share: minimal import modal, name lives in the title, one type+counts line, inline need chips, single-line trust note --- backend/apps/swarm/review.py | 2 +- .../src/app/components/share/ImportModal.tsx | 25 +-- .../src/app/components/share/IncludesList.tsx | 183 ++++++------------ 3 files changed, 68 insertions(+), 142 deletions(-) diff --git a/backend/apps/swarm/review.py b/backend/apps/swarm/review.py index fb8f0d00..b94a6e99 100644 --- a/backend/apps/swarm/review.py +++ b/backend/apps/swarm/review.py @@ -30,5 +30,5 @@ def scan_app_files(files: dict[str, bytes]) -> ReviewSummary: verdict = "warn" if findings else "clean" if runnable: verdict = "warn" - findings.insert(0, "This app runs code on your computer when you open it. Only import apps you trust.") + findings.insert(0, "This app runs code on your computer. Only import apps you trust.") return ReviewSummary(verdict=verdict, findings=findings, scanned_files=scanned) diff --git a/frontend/src/app/components/share/ImportModal.tsx b/frontend/src/app/components/share/ImportModal.tsx index 7025a5b8..08d6c3d4 100644 --- a/frontend/src/app/components/share/ImportModal.tsx +++ b/frontend/src/app/components/share/ImportModal.tsx @@ -56,26 +56,11 @@ const ImportModal: React.FC = ({ preflight, open, committing, onConfirm, {preflight.review && preflight.review.findings.length > 0 && ( - - - - {preflight.review.findings.map((f, i) => ( - - {f} - - ))} - + + + + {preflight.review.findings.join(' ')} + )} {preflight.conflicts.length > 0 && ( diff --git a/frontend/src/app/components/share/IncludesList.tsx b/frontend/src/app/components/share/IncludesList.tsx index 1a9cdb61..a1d13824 100644 --- a/frontend/src/app/components/share/IncludesList.tsx +++ b/frontend/src/app/components/share/IncludesList.tsx @@ -1,9 +1,8 @@ -// The "what's inside this bundle" panel, shared by the Share and Import modals: -// the root entity, the dependencies pulled in with it, and any environment -// requirements (an Action the importer must enable themselves). Long bundles -// (a dashboard pulls in every agent) read as a wall, so the contents collapse -// to a one-line count by default and expand on demand. Requirements always -// show, they're the part the importer has to act on. +// The "what's inside this bundle" panel, shared by the Share and Import modals. +// Kept deliberately spare: the bundle's name already lives in the modal title, so +// here it's just one line of type + counts, the requirements as small icon chips, +// and an optional expand for the full contents. No boxes, the modal's whitespace +// does the grouping. import React, { useState } from 'react'; import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; @@ -17,14 +16,6 @@ import { useClaudeTokens } from '@/shared/styles/ThemeContext'; import { BundleSummary } from './shareTypes'; -// One glyph per requirement kind, so a row of needs reads as icons + names -// instead of a stack of explanatory sentences. The sentence moves to a hover. -const REQ_ICON: Record = { - mcp_action: , - api_key: , - builtin_mode: , -}; - const KIND_LABEL: Record = { skill: 'Skill', app: 'App', @@ -34,6 +25,13 @@ const KIND_LABEL: Record = { session: 'Agent', }; +// One glyph per requirement kind, so needs read as icons + names, not sentences. +const REQ_ICON: Record = { + mcp_action: , + api_key: , + builtin_mode: , +}; + const pluralize = (label: string, n: number): string => (n === 1 ? label : `${label}s`); const IncludesList: React.FC<{ summary: BundleSummary }> = ({ summary }) => { @@ -41,7 +39,7 @@ const IncludesList: React.FC<{ summary: BundleSummary }> = ({ summary }) => { const [expanded, setExpanded] = useState(false); const includes = summary.includes; - // One quiet line: "9 agents · 1 app", in the bundle's own type order. + // "9 agents · 1 app", in the bundle's own type order. const order: string[] = []; const byType = new Map(); for (const it of includes) { @@ -51,121 +49,64 @@ const IncludesList: React.FC<{ summary: BundleSummary }> = ({ summary }) => { const countLine = order .map((t) => `${byType.get(t)} ${pluralize((KIND_LABEL[t] || t).toLowerCase(), byType.get(t) || 0)}`) .join(' · '); - - const Row: React.FC<{ tag: string; name: string; detail?: string; faded?: boolean }> = ({ - tag, - name, - detail, - faded, - }) => ( - - - {tag} - - - {name} - - {detail && ( - {detail} - )} - - ); + const rootLabel = KIND_LABEL[summary.root.type] || summary.root.type; return ( - - - - {includes.length > 0 && !expanded && ( - setExpanded(true)} - sx={{ - display: 'flex', - alignItems: 'center', - justifyContent: 'space-between', - gap: 1, - py: 0.55, - cursor: 'pointer', - '&:hover .show-toggle': { color: c.accent.primary }, - }} - > - {countLine} - - Show - + + {/* Lead: the bundle's type + a one-line count. The name is in the title. */} + + + {rootLabel} + {countLine && ` · ${countLine}`} + + {includes.length > 0 && ( + setExpanded((v) => !v)} + sx={{ display: 'flex', alignItems: 'center', gap: 0.25, color: c.text.tertiary, cursor: 'pointer', '&:hover': { color: c.accent.primary } }} + > + {expanded ? 'Hide' : 'Show'} + + )} + + + {expanded && includes.length > 0 && ( + + {includes.map((it, i) => ( + + + {KIND_LABEL[it.type] || it.type} + + + {it.name} + + + ))} )} - {includes.length > 0 && expanded && ( - <> - {includes.map((it, i) => ( - - ))} - setExpanded(false)} - sx={{ py: 0.4, cursor: 'pointer', color: c.text.tertiary, '&:hover': { color: c.accent.primary } }} - > - Hide - - - )} - {summary.requirements.length > 0 && ( - - + + Needs - - {summary.requirements.map((r, i) => ( - - - {REQ_ICON[r.kind] || } - - {r.label} - - - - ))} - + {summary.requirements.map((r, i) => ( + + + {REQ_ICON[r.kind] || } + + {r.label} + + + + ))} )}