Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 66 additions & 17 deletions src/workspace/workspacesProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ export class WorkspaceProvider
workspace,
this.getWorkspacesQuery === WorkspaceQuery.All,
showMetadata,
this.client.getAxiosInstance().defaults.baseURL,
);

return workspaceTreeItem;
Expand Down Expand Up @@ -258,12 +259,16 @@ export class WorkspaceProvider
// We need to do this for now because the reporting isn't super accurate
// yet.
appStatuses.push(
new AppStatusTreeItem({
id: status.id,
name: status.message,
command: app.command,
workspace_name: element.workspace.name,
}),
new AppStatusTreeItem(
{
id: status.id,
name: status.message,
command: app.command,
workspace_name: element.workspace.name,
icon: app.icon,
},
this.client.getAxiosInstance().defaults.baseURL,
Comment on lines +268 to +270
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should pass the resolved icon instead of the baseUrl here (esp. since it should always be defined). That way we do not leak abstraction into the AppStatusTreeItem which shouldn't be aware of those details

),
);
}
}
Expand Down Expand Up @@ -358,6 +363,45 @@ class AgentMetadataTreeItem extends vscode.TreeItem {
}
}

function resolveTreeItemIconPath(
icon: string | undefined,
baseUrl?: string,
): vscode.Uri | undefined {
if (!icon?.trim()) {
return undefined;
}

const trimmed = icon.trim();
if (/^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(trimmed)) {
return vscode.Uri.parse(trimmed);
}

if (!baseUrl) {
return undefined;
}
Comment on lines +374 to +381
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not needed, the URL implements WHATWG resolution semantics, so:

new URL("https://x/y.svg", "https://base/").toString() // "https://x/y.svg"
new URL("/icon.svg",      "https://base/").toString() // "https://base/icon.svg"                                                      
new URL("data:image/png;base64,...", "https://base/").toString() // unchanged


try {
const fullIconUrl = new URL(trimmed, baseUrl).toString();
return vscode.Uri.parse(fullIconUrl);
} catch {
return undefined;
}
}

function defaultTreeItemIconPath(): {
light: vscode.Uri;
dark: vscode.Uri;
} {
return {
light: vscode.Uri.file(
path.join(__dirname, "..", "..", "media", "logo-black.svg"),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems wrong now, it used to be __filename then traversing up by two levels but now it's __dirname but by two levels as well

),
dark: vscode.Uri.file(
path.join(__dirname, "..", "..", "media", "logo-white.svg"),
),
};
}

class AppStatusTreeItem extends vscode.TreeItem {
constructor(
public readonly app: {
Expand All @@ -366,12 +410,16 @@ class AppStatusTreeItem extends vscode.TreeItem {
url?: string;
command?: string;
workspace_name?: string;
icon?: string;
},
baseUrl?: string,
) {
super("", vscode.TreeItemCollapsibleState.None);
super(app.name, vscode.TreeItemCollapsibleState.None);
this.id = app.id;
this.description = app.name;
this.description = app.workspace_name;
Comment on lines +417 to +419
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly name here is confusing, this is status.message which is why it was rendered in the description (to make it less prominent). How would this currently look like actually?

If we want the status to be more prominent here then maybe we just show app.display_name ?? app.slug in the description? Since the workspace name is already the parent (or grandparent) so no need to repeat it

this.contextValue = "coderAppStatus";
const resolvedAppIcon = resolveTreeItemIconPath(app.icon, baseUrl);
this.iconPath = resolvedAppIcon ?? defaultTreeItemIconPath();

// Add command to handle clicking on the app
this.command = {
Expand Down Expand Up @@ -409,16 +457,8 @@ export class OpenableTreeItem extends vscode.TreeItem {
tags.push("running");
}
this.contextValue = tags.join("+");
this.iconPath ??= defaultTreeItemIconPath();
}

override iconPath = {
light: vscode.Uri.file(
path.join(__filename, "..", "..", "media", "logo-black.svg"),
),
dark: vscode.Uri.file(
path.join(__filename, "..", "..", "media", "logo-white.svg"),
),
};
}

export class AgentTreeItem extends OpenableTreeItem {
Expand Down Expand Up @@ -446,6 +486,7 @@ export class WorkspaceTreeItem extends OpenableTreeItem {
workspace: Workspace,
public readonly showOwner: boolean,
public readonly watchMetadata = false,
baseUrl?: string,
) {
const status = workspaceStatusLabel(workspace.latest_build.status);

Expand All @@ -467,5 +508,13 @@ export class WorkspaceTreeItem extends OpenableTreeItem {
? "coderWorkspaceMultipleAgents"
: "coderWorkspaceSingleAgent",
);

const resolvedWorkspaceIcon = resolveTreeItemIconPath(
workspace.template_icon,
baseUrl,
);
if (resolvedWorkspaceIcon) {
this.iconPath = resolvedWorkspaceIcon;
}
}
}