Skip to content

Commit 64db1c7

Browse files
committed
feat(desktop): Phase 4 — projects cards deny CTA + unbound empty state
Signed-off-by: crimsonsunset <jsangio1@gmail.com>
1 parent f2efc81 commit 64db1c7

4 files changed

Lines changed: 47 additions & 7 deletions

File tree

apps/desktop/src/features/workspaces/WorkspacesPage.tsx

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1049,7 +1049,11 @@ function EntryCard({
10491049
</div>
10501050
<div className="flex min-w-0 flex-1 flex-col">
10511051
<div className="mb-1.5 flex min-h-[1.375rem] flex-wrap items-center gap-2">
1052-
{entry.kind === 'unmapped-live' && <Pill tone="amber">{t('card.unmapped')}</Pill>}
1052+
{entry.kind === 'unmapped-live' && (
1053+
<Pill tone="amber" title={t('card.deniedTooltip')}>
1054+
{t('card.badgeLiveUnbound')}
1055+
</Pill>
1056+
)}
10531057
{entry.kind === 'live-elsewhere' && (
10541058
<Pill tone="info">{t('card.badgeBoundElsewhere')}</Pill>
10551059
)}
@@ -1078,6 +1082,21 @@ function EntryCard({
10781082
>
10791083
{hasLabel ? entry.root : '\u00A0'}
10801084
</p>
1085+
{entry.kind === 'unmapped-live' && (
1086+
<Button
1087+
variant="primary"
1088+
size="sm"
1089+
className="mt-3 w-full"
1090+
title={t('card.deniedTooltip')}
1091+
onClick={(e) => {
1092+
e.stopPropagation();
1093+
onClick();
1094+
}}
1095+
data-testid="workspace-entry-bind-cta"
1096+
>
1097+
{t('card.deniedCta')}
1098+
</Button>
1099+
)}
10811100
</div>
10821101
</div>
10831102

@@ -1443,6 +1462,15 @@ export function EffectiveFeaturesContent({
14431462
}
14441463
if (!data) return null;
14451464

1465+
if (data.source === 'unbound') {
1466+
return (
1467+
<div className="text-center py-8 text-[rgb(var(--muted))]">
1468+
<Package className="h-8 w-8 mx-auto mb-2 opacity-50" />
1469+
<p className="text-sm">{t('effective.unboundEmpty')}</p>
1470+
</div>
1471+
);
1472+
}
1473+
14461474
const allAvailable = totalCount > 0 && availableCount === totalCount;
14471475
const partialAvailable = availableCount > 0 && availableCount < totalCount;
14481476

apps/desktop/src/features/workspaces/workspace-binding-panel.component.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -862,7 +862,7 @@ export function WorkspaceBindingPanel() {
862862
</Pill>
863863
)}
864864
{mode === 'create-from-live' && !payload.collisionClientId && (
865-
<Pill tone="amber">{t('card.unmapped')}</Pill>
865+
<Pill tone="amber">{t('card.badgeLiveUnbound')}</Pill>
866866
)}
867867
{mode === 'edit' && binding && <Pill tone="neutral">{t('card.offline')}</Pill>}
868868
</>
@@ -887,6 +887,14 @@ export function WorkspaceBindingPanel() {
887887
</div>
888888
) : (
889889
<>
890+
{mode === 'create-from-live' && !payload.collisionClientId && (
891+
<div
892+
className="rounded-xl border border-amber-200/80 dark:border-amber-800/50 bg-amber-50/80 dark:bg-amber-900/15 p-4"
893+
data-testid="workspace-binding-no-tools-banner"
894+
>
895+
<p className="text-sm text-amber-900 dark:text-amber-100">{t('sheet.noToolsBanner')}</p>
896+
</div>
897+
)}
890898
{mode === 'create-from-live' && payload.clientId && showMachineCallout && (
891899
<div
892900
className="rounded-xl border-2 border-amber-300/80 dark:border-amber-700/60 bg-amber-50/80 dark:bg-amber-900/15 p-4 space-y-3"

apps/desktop/src/lib/api/workspaceBindings.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ export interface EffectiveFeatureSetSummary {
187187

188188
export interface WorkspaceEffectiveFeatures {
189189
workspace_root: string;
190-
/** `binding` when a saved WorkspaceBinding matched; `unbound` when no binding matched — an unbound folder falls back to the default Space's Starter FS, so `feature_sets` is what a live session here actually sees until the user attaches a binding. */
190+
/** `binding` when a saved WorkspaceBinding matched; `unbound` when no binding matched — caller has zero backend tools by default. Bind to enable access. */
191191
source: 'binding' | 'unbound';
192192
binding_id: string | null;
193193
space_id: string;

apps/desktop/src/locales/en/workspaces.json

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"title": "Projects",
3-
"subtitle": "Each binding tells mcpmux which Space and feature set a folder routes into. Folders without a binding fall back to the default Space.",
3+
"subtitle": "Each binding tells mcpmux which Space and feature set a folder routes into. Folders without a binding have no backend tools until you bind them.",
44
"searchPlaceholder": "Search by path, space, or feature set…",
55
"filter": {
66
"all": "All",
@@ -35,7 +35,7 @@
3535
},
3636
"confirm": {
3737
"removeTitle": "Remove binding",
38-
"removeMessage": "Sessions matching \"{{path}}\" will fall back to the default Space. You can recreate the binding anytime.",
38+
"removeMessage": "Sessions matching \"{{path}}\" will have no backend tools until you create a new binding.",
3939
"removeLabel": "Remove"
4040
},
4141
"card": {
@@ -44,6 +44,8 @@
4444
"offline": "Offline",
4545
"live": "Live",
4646
"badgeLiveUnbound": "UNBOUND",
47+
"deniedCta": "Bind to enable tools",
48+
"deniedTooltip": "This folder has no tools until you bind it to a workspace",
4749
"badgeBoundElsewhere": "BOUND ELSEWHERE",
4850
"notConfigured": "Not configured",
4951
"addBinding": "Add binding",
@@ -107,7 +109,8 @@
107109
"sourceBinding": "binding",
108110
"sourceUnbound": "unbound",
109111
"sourceBindingTooltip": "A workspace binding matched this folder — live sessions reporting it route here.",
110-
"sourceUnboundTooltip": "No binding matches this folder. A live roots-capable session would be denied; the FeatureSet shown is a preview of what binding here would expose.",
112+
"sourceUnboundTooltip": "No backend tools are active — this folder has no binding yet",
113+
"unboundEmpty": "No tools on this session yet. Bind this folder to enable access.",
111114
"availableOf": "{{available}} of {{total}} available",
112115
"allReady": "All ready",
113116
"partial": "Partial",
@@ -195,7 +198,8 @@
195198
"titleCollision": "Create a binding for this client",
196199
"titleNew": "This folder is using your Starter set",
197200
"descCollision": "This path is already bound for another MCP client. Save a client-scoped binding so this machine gets its own tool set.",
198-
"descNew": "You just opened this folder in a connected app. It's already configured with your default Starter tools. Pick a different Space or feature set below to change what it gets, or close to keep the Starter.",
201+
"descNew": "No backend tools are active on this session. Creating a binding enables tool access.",
202+
"noToolsBanner": "No backend tools are active on this session. Creating a binding enables tool access.",
199203
"space": "Space",
200204
"toolSet": "Tool set",
201205
"noFeatureSets": "No feature sets in this space yet.",

0 commit comments

Comments
 (0)