Skip to content

Commit 618b674

Browse files
njb90claude
andcommitted
feat: replace IDE segmented control with platform checklist in homescreen webview
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 3eac643 commit 618b674

2 files changed

Lines changed: 49 additions & 88 deletions

File tree

src/commands/configureAiTools.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ function registerConfigureAiTools(context: vscode.ExtensionContext): void {
104104
});
105105
if (!ideTarget) { return; }
106106

107-
const installedDirNames = await readInstalledSkillDirNames(rootUri, ideTarget.label, skills);
107+
const installedDirNames = await readInstalledSkillDirNames(rootUri, ideTarget.label as any, skills);
108108

109109
const pickedSkills = await vscode.window.showQuickPick(
110110
skills.map((s) => ({

src/webview/homescreenView.ts

Lines changed: 48 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { CloudinaryTreeDataProvider } from "../tree/treeDataProvider";
88
import { createWebviewDocument, getScriptUri } from "./webviewUtils";
99
import { escapeHtml } from "./utils/helpers";
1010
import {
11+
PlatformId,
1112
SkillInfo,
1213
MCP_SERVERS,
1314
detectEditor,
@@ -17,9 +18,11 @@ import {
1718
readInstalledSkillDirNames,
1819
readConfiguredMcpServerKeys,
1920
installForClaudeCode,
20-
installForCursor,
2121
installForCopilot,
22+
installForUniversal,
23+
installForWindsurf,
2224
installMcpServers,
25+
detectActivePlatforms,
2326
} from "../aiToolsService";
2427

2528
export class HomescreenViewProvider implements vscode.WebviewViewProvider {
@@ -64,7 +67,7 @@ export class HomescreenViewProvider implements vscode.WebviewViewProvider {
6467
});
6568

6669
webviewView.webview.onDidReceiveMessage(
67-
async (message: { command: string; skills?: string[]; ideTarget?: string; mcpServers?: string[] }) => {
70+
async (message: { command: string; skills?: string[]; platforms?: string[]; mcpServers?: string[] }) => {
6871
switch (message.command) {
6972
case "openGlobalConfig":
7073
vscode.commands.executeCommand("cloudinary.openGlobalConfig");
@@ -84,7 +87,7 @@ export class HomescreenViewProvider implements vscode.WebviewViewProvider {
8487
case "installAiTools":
8588
await this._handleInstallAiTools(
8689
message.skills ?? [],
87-
message.ideTarget ?? "Claude Code",
90+
message.platforms ?? [],
8891
message.mcpServers ?? []
8992
);
9093
break;
@@ -461,50 +464,6 @@ export class HomescreenViewProvider implements vscode.WebviewViewProvider {
461464
background: var(--vscode-panel-border, rgba(128,128,128,0.14));
462465
}
463466
464-
/* IDE segmented control */
465-
.hs-ai-ide {
466-
position: relative;
467-
display: flex;
468-
background: rgba(255,255,255,0.04);
469-
border: 1px solid var(--vscode-panel-border, rgba(128,128,128,0.14));
470-
border-radius: 5px;
471-
padding: 2px;
472-
margin-bottom: 7px;
473-
}
474-
.hs-ai-ide-pill {
475-
position: absolute;
476-
top: 2px;
477-
height: calc(100% - 4px);
478-
background: rgba(52,72,197,0.35);
479-
border: 1px solid rgba(52,72,197,0.5);
480-
border-radius: 3px;
481-
transition:
482-
left 0.15s cubic-bezier(0.4,0,0.2,1),
483-
width 0.15s cubic-bezier(0.4,0,0.2,1);
484-
pointer-events: none;
485-
}
486-
.hs-ai-ide-btn {
487-
flex: 1;
488-
padding: 3px 4px;
489-
font-size: 9px;
490-
font-weight: 600;
491-
letter-spacing: 0.2px;
492-
text-align: center;
493-
text-transform: uppercase;
494-
background: none;
495-
border: none;
496-
border-radius: 3px;
497-
color: var(--vscode-descriptionForeground);
498-
cursor: pointer;
499-
font-family: var(--vscode-font-family);
500-
position: relative;
501-
z-index: 1;
502-
transition: color 0.15s;
503-
white-space: nowrap;
504-
}
505-
.hs-ai-ide-btn.active { color: var(--vscode-foreground); }
506-
.hs-ai-ide-btn:hover:not(.active) { color: var(--vscode-foreground); opacity: 0.7; }
507-
508467
/* Checklist items */
509468
.hs-ai-item {
510469
display: flex;
@@ -590,6 +549,14 @@ export class HomescreenViewProvider implements vscode.WebviewViewProvider {
590549
}
591550
.hs-ai-item-status--ok::before { background: #4ade80; }
592551
.hs-ai-item-status--none::before { background: rgba(255,255,255,0.15); }
552+
.hs-ai-item-status--partial::before { background: rgba(250,204,21,0.7); }
553+
.hs-ai-platform-sub {
554+
display: block;
555+
font-size: 9px;
556+
font-weight: 400;
557+
color: var(--vscode-descriptionForeground);
558+
margin-top: 1px;
559+
}
593560
594561
/* Progress tick */
595562
.hs-ai-item-tick {
@@ -737,13 +704,9 @@ export class HomescreenViewProvider implements vscode.WebviewViewProvider {
737704
<div class="hs-ai-panel-inner hidden" id="hs-ai-state-ready">
738705
<div>
739706
<div class="hs-ai-section-head">Skills</div>
740-
<div class="hs-ai-ide" id="hs-ai-ide" role="group" aria-label="Target IDE">
741-
<div class="hs-ai-ide-pill" id="hs-ai-ide-pill"></div>
742-
<button class="hs-ai-ide-btn active" data-ide="Claude Code">Claude Code</button>
743-
<button class="hs-ai-ide-btn" data-ide="Cursor">Cursor</button>
744-
<button class="hs-ai-ide-btn" data-ide="VS Code (Copilot)">VS Code</button>
745-
</div>
746707
<div id="hs-ai-skills-list"></div>
708+
<div class="hs-ai-section-head" style="margin-top:8px">Install for</div>
709+
<div id="hs-ai-platform-list"></div>
747710
</div>
748711
<div>
749712
<div class="hs-ai-section-head">MCP Servers</div>
@@ -792,41 +755,34 @@ export class HomescreenViewProvider implements vscode.WebviewViewProvider {
792755
const rootUri = workspaceFolders[0].uri;
793756

794757
try {
795-
// Fetch skills once; cache for subsequent opens
796758
if (!this._cachedSkills) {
797759
this._cachedSkills = await fetchSkillList();
798760
}
799761
const skills = this._cachedSkills;
800762

801-
const ideLabels: string[] = ["Claude Code", "Cursor", "VS Code (Copilot)"];
802-
803-
// Pre-compute installed status for all 3 IDEs
804-
const installedByIde: Record<string, string[]> = {};
763+
const platformIds: PlatformId[] = ["universal", "claude-code", "vscode-copilot", "windsurf"];
764+
const installedByPlatform: Record<string, string[]> = {};
805765
await Promise.all(
806-
ideLabels.map(async (label) => {
807-
const installedSet = await readInstalledSkillDirNames(rootUri, label, skills);
808-
installedByIde[label] = [...installedSet];
766+
platformIds.map(async (pid) => {
767+
const set = await readInstalledSkillDirNames(rootUri, pid, skills);
768+
installedByPlatform[pid] = [...set];
809769
})
810770
);
811771

812-
// MCP servers — use detected editor for the config file path
772+
const activePlatforms = await detectActivePlatforms(rootUri);
773+
813774
const editor = detectEditor();
814775
const mcpFilePath = getMcpFilePath(editor);
815776
const rootKey = editor === "vscode" ? "servers" : "mcpServers";
816777
const configuredMcpSet = await readConfiguredMcpServerKeys(rootUri, mcpFilePath, rootKey);
817778

818-
const detectedIde =
819-
editor === "cursor" ? "Cursor" :
820-
editor === "vscode" ? "VS Code (Copilot)" :
821-
"Claude Code";
822-
823779
view.webview.postMessage({
824780
command: "aiToolsData",
825781
skills: skills.map((s) => ({ name: s.name, description: s.description, dirName: s.dirName })),
826-
installedByIde,
782+
installedByPlatform,
783+
activePlatforms,
827784
mcpServers: MCP_SERVERS.map((s) => ({ key: s.key, label: s.label, description: s.description })),
828785
configuredMcpKeys: [...configuredMcpSet],
829-
detectedIde,
830786
});
831787
} catch (err: any) {
832788
view.webview.postMessage({
@@ -838,7 +794,7 @@ export class HomescreenViewProvider implements vscode.WebviewViewProvider {
838794

839795
private async _handleInstallAiTools(
840796
skills: string[],
841-
ideTarget: string,
797+
platforms: string[],
842798
mcpServers: string[]
843799
): Promise<void> {
844800
const view = this._webviewView;
@@ -851,9 +807,8 @@ export class HomescreenViewProvider implements vscode.WebviewViewProvider {
851807
}
852808
const rootUri = workspaceFolders[0].uri;
853809
const errors: string[] = [];
854-
855-
// Install skills
856810
const cachedSkills = this._cachedSkills ?? [];
811+
857812
for (const dirName of skills) {
858813
const skillInfo = cachedSkills.find((s) => s.dirName === dirName);
859814
if (!skillInfo) { continue; }
@@ -868,22 +823,30 @@ export class HomescreenViewProvider implements vscode.WebviewViewProvider {
868823
}
869824

870825
const createdFiles: string[] = [];
871-
try {
872-
if (ideTarget === "Claude Code") {
873-
await installForClaudeCode(rootUri, dirName, content, createdFiles, errors);
874-
} else if (ideTarget === "Cursor") {
875-
await installForCursor(rootUri, dirName, content, createdFiles);
876-
} else {
877-
await installForCopilot(rootUri, skillInfo.name, content, createdFiles);
878-
}
879-
view.webview.postMessage({ command: "aiToolsProgress", item: dirName, status: "done" });
880-
} catch (err: any) {
881-
errors.push(`${dirName}: ${err.message}`);
882-
view.webview.postMessage({ command: "aiToolsProgress", item: dirName, status: "error" });
826+
let anyError = false;
827+
for (const platform of platforms) {
828+
try {
829+
if (platform === "claude-code") {
830+
await installForClaudeCode(rootUri, dirName, content, createdFiles, errors);
831+
} else if (platform === "universal") {
832+
await installForUniversal(rootUri, dirName, content, createdFiles, errors);
833+
} else if (platform === "windsurf") {
834+
await installForWindsurf(rootUri, dirName, content, createdFiles, errors);
835+
} else if (platform === "vscode-copilot") {
836+
await installForCopilot(rootUri, skillInfo.name, content, createdFiles);
837+
}
838+
} catch (err: any) {
839+
errors.push(`${dirName} (${platform}): ${err.message}`);
840+
anyError = true;
841+
}
883842
}
843+
view.webview.postMessage({
844+
command: "aiToolsProgress",
845+
item: dirName,
846+
status: anyError ? "error" : "done",
847+
});
884848
}
885849

886-
// Install MCP servers
887850
if (mcpServers.length > 0) {
888851
const editor = detectEditor();
889852
const createdFiles: string[] = [];
@@ -900,9 +863,7 @@ export class HomescreenViewProvider implements vscode.WebviewViewProvider {
900863
}
901864
}
902865

903-
// Invalidate cached skills so next open re-reads disk
904866
this._cachedSkills = undefined;
905-
906867
view.webview.postMessage({ command: "aiToolsResult", errors });
907868
}
908869
}

0 commit comments

Comments
 (0)