Skip to content

Commit 3dfb963

Browse files
feat(resources): replace AWS service icons with target-aware icons
The per-service icons in the Resources view were hand-edited derivatives of AWS's Architecture Icons; bundling AWS iconography in the product risks violating AWS's icon terms of use. Remove the 6 derivative SVGs and the per-service icon API, and instead show an icon that reflects the profile's target: the LocalStack mark for LocalStack-targeted profiles and the built-in `cloud` codicon for real AWS. Both are themed ThemeIcons with no AWS IP. - Delete resources/icons/services/*.svg and ServiceProvider.getIconPath (plus the now-dead `context` plumbing through the provider chain) - Add AWSConfig.getEndpointForProfile; resolve isLocalStack per profile in viewProvider and thread it to the service-and-resource-type row - Update the resource-browser spec; add tree-item icon and getEndpointForProfile tests - Archive the remove-aws-service-icons OpenSpec change Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 2b03ee8 commit 3dfb963

25 files changed

Lines changed: 290 additions & 117 deletions

File tree

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
schema: spec-driven
2+
created: 2026-06-23
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
## Context
2+
3+
The Resources view sets a service icon on each combined service-and-resource-type row via `ServiceProvider.getIconPath(serviceId)` ([serviceProvider.ts:104-122](../../../src/platforms/aws/services/serviceProvider.ts)), called from `ResourceServiceTypeTreeItem` ([treeItems.ts:75](../../../src/views/resources/treeItems.ts)). `getIconPath` returns the path to a bundled per-service SVG when one exists, else a `symbol-misc` codicon. Only 6 SVGs exist, and all 6 are hand-edited derivatives of AWS's Architecture Icons. Shipping AWS's iconography in our product is a terms-of-use concern, so they must be removed.
4+
5+
The supporting infrastructure makes a clean replacement easy: the view already resolves per-profile context in `viewProvider.makeResourceProfiles` ([viewProvider.ts:150](../../../src/views/resources/viewProvider.ts)) (STS caller identity + IAM alias), `AWSConfig.getSectionForProfile(profile.id)?.endpoint_url` already exposes a profile's endpoint, the LocalStack-backed instance focus is a synthetic profile named `localstack`, and the LocalStack logo is already a registered, themeable font glyph (`localstack-logo` in `contributes.icons`, used as `$(localstack-logo)` in [status-bar.ts:99](../../../src/plugins/status-bar.ts)). The tree rows form a parent chain (service-type → region → profile), so a profile-level fact can be threaded down to the row.
6+
7+
## Goals / Non-Goals
8+
9+
**Goals:**
10+
- Remove every AWS-derived icon asset and the per-service icon API from the codebase.
11+
- Give each service row a meaningful, AWS-IP-free icon that signals where its resources live (LocalStack vs AWS).
12+
- Reuse existing, already-themed icons (`localstack-logo` glyph, built-in `cloud` codicon) — no new assets, no new dependencies.
13+
14+
**Non-Goals:**
15+
- Restoring distinct per-service iconography (explicitly out — that's what we're removing). Per-service art could only return later as LocalStack's own original work; not in scope here.
16+
- Changing the tree hierarchy, resource listing, or details view.
17+
- Touching LocalStack's own marks (`localstack.svg`, `localstack.woff`, the `localstack-logo` glyph).
18+
19+
## Decisions
20+
21+
### Decision: Icon denotes the profile target, not the service
22+
23+
The icon moves from a per-service property to a per-profile-target property. A row under a LocalStack-targeted profile shows `ThemeIcon("localstack-logo")`; a row under an AWS-targeted profile shows `ThemeIcon("cloud")` (a built-in codicon). Both are `ThemeIcon`s, so they inherit theme foreground color and selection inversion for free, and neither bundles AWS content. Alternative considered: a single neutral codicon for every row (simplest), rejected because the target signal is more useful and costs little given the profile context is already resolved. Alternative considered: no icon at all, rejected because it loses row alignment/visual anchor for no benefit.
24+
25+
### Decision: LocalStack-vs-AWS signal is the profile's custom endpoint
26+
27+
A profile targets LocalStack when it resolves to a custom/local endpoint — `AWSConfig.getSectionForProfile(profile.id)?.endpoint_url` is set — or when it is the synthetic `localstack` instance profile. Otherwise it targets real AWS. Rationale: the configured `localstack` profile is written with `endpoint_url = http://localhost.localstack.cloud:4566` ([configure-aws.ts:79](../../../src/utils/configure-aws.ts)), and real AWS profiles have no custom endpoint. This is a binary, dependency-free check. Trade-off: a user who points a profile at some *non-LocalStack* custom endpoint would also get the LocalStack mark. Accepted as a rare edge case; optionally refine later by matching the endpoint host against a `localstack`/`localhost` pattern rather than mere presence.
28+
29+
### Decision: Resolve once per profile, thread the flag down
30+
31+
Compute `isLocalStack` in `makeResourceProfiles` (where profile context already exists) and store it on `ResourceProfileTreeItem`. `ResourceServiceTypeTreeItem` reads it via its parent chain (`parent.parent`) — or it is passed explicitly through `ResourceRegionTreeItem` — and selects the icon in its constructor, replacing the `provider.getIconPath(...)` call. This keeps endpoint resolution out of the per-row hot path and off `ServiceProvider`.
32+
33+
### Decision: Delete `getIconPath` rather than repurpose it
34+
35+
`getIconPath` is removed entirely (with its `fs`/`path` imports and `symbol-misc` fallback) because the icon is no longer derivable from a service id. Keeping a stubbed method would invite re-introducing AWS assets. `ServiceProvider` returns to being purely about resource data.
36+
37+
## Risks / Trade-offs
38+
39+
- **Misclassifying a custom non-LocalStack endpoint as LocalStack** → Accepted edge case; mitigated optionally by host-pattern matching on the endpoint URL.
40+
- **Profiles whose endpoint can't be resolved** (error rows already handled by `ResourceErrorTreeItem`) → target resolution must default safely (treat unknown as AWS/cloud) and never throw in the row constructor.
41+
- **Visual regression for users who valued per-service icons** → Intentional; communicated by the proposal's rationale. The target icon is arguably more informative for a tool that spans local and cloud.
42+
- **Residual AWS-derived assets elsewhere** (READMEs, marketplace art, appinspector webview) → Out of scope for this change but worth a follow-up sweep; this change covers the resource-browser service icons only.
43+
44+
## Migration Plan
45+
46+
1. Delete the 6 SVGs in `resources/icons/services/`.
47+
2. Add target resolution in `viewProvider.makeResourceProfiles`; store `isLocalStack` on `ResourceProfileTreeItem`.
48+
3. Thread the flag to `ResourceServiceTypeTreeItem`; set `iconPath` to `localstack-logo` or `cloud` accordingly.
49+
4. Remove `ServiceProvider.getIconPath` and its now-unused imports.
50+
5. Update/remove icon-related tests; add coverage that a LocalStack-targeted row and an AWS-targeted row select the expected ThemeIcon ids.
51+
52+
Rollback: restore `getIconPath` and the SVGs from git history; the change is self-contained.
53+
54+
## Open Questions
55+
56+
- Refine the LocalStack signal to host-pattern matching (`localstack`/`localhost`) now, or keep the simpler "any custom endpoint ⇒ LocalStack" until a real misclassification is reported?
57+
- Is `cloud` the best AWS-side codicon, or a more neutral one (e.g. `cloud-upload` is wrong; `cloud` reads as generic cloud)? Confirm against the codicon set during apply.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
## Why
2+
3+
The 6 service icons in the Resources view are hand-edited derivatives of AWS's Architecture Icons. Bundling AWS's iconography in our own commercial product risks violating AWS's icon terms of use, so we want them out of the codebase. The remaining 110 services already show a generic codicon, so removing the 6 derivatives also removes an inconsistency. Rather than leave service rows bare, we replace the per-service icon with a **target-aware** icon that carries no AWS intellectual property: it tells the user whether a row's resources live in a LocalStack emulator or in real AWS.
4+
5+
## What Changes
6+
7+
- **Remove all AWS-derived assets:** delete the 6 hand-edited SVGs in `resources/icons/services/` (`cloudformation`, `dynamodb`, `lambda`, `sns`, `sqs`, `states`).
8+
- **Drop the per-service icon API:** remove `ServiceProvider.getIconPath(serviceId)` along with its now-unused `fs`/`path` imports and the `symbol-misc` fallback. The icon is no longer a property of the service.
9+
- **Add a target-aware row icon:** the service-and-resource-type row shows `ThemeIcon("localstack-logo")` when its profile targets a LocalStack emulator and the built-in `ThemeIcon("cloud")` codicon when it targets real AWS. Both are themed icons (theme- and selection-aware) with no AWS-derived content.
10+
- **Determine the target at the profile level:** in `viewProvider.makeResourceProfiles`, resolve whether a profile points at LocalStack via its `endpoint_url` (a custom/local endpoint, or the synthetic `localstack` instance profile, means LocalStack; no custom endpoint means AWS), set an `isLocalStack` flag on `ResourceProfileTreeItem`, and thread it down through `ResourceRegionTreeItem` to `ResourceServiceTypeTreeItem`.
11+
- **Keep LocalStack's own marks untouched:** `resources/icons/localstack.svg`, `resources/fonts/localstack.woff`, and the `localstack-logo` `contributes.icons` glyph are LocalStack's own branding and remain.
12+
13+
## Capabilities
14+
15+
### New Capabilities
16+
<!-- none -->
17+
18+
### Modified Capabilities
19+
- `resource-browser`: the "Resources view renders the active focus" requirement changes so the combined service-and-resource-type row's icon denotes the **target** (LocalStack vs AWS) rather than the service, and explicitly forbids bundling AWS-derived service icons.
20+
21+
## Impact
22+
23+
- **Code:** `src/platforms/aws/services/serviceProvider.ts` (remove `getIconPath`); `src/views/resources/treeItems.ts` (`ResourceServiceTypeTreeItem` icon, thread `isLocalStack`); `src/views/resources/viewProvider.ts` (resolve target per profile); existing icon tests, if any.
24+
- **Assets:** `resources/icons/services/*.svg` deleted (6 → 0). No new assets added.
25+
- **No new dependencies.** `cloud` is a built-in codicon; `localstack-logo` is an already-registered font glyph (used in `status-bar.ts`).
26+
- **Supersedes** the abandoned `service-icon-font` direction (do-not-bundle-AWS-icons reverses it).
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
## MODIFIED Requirements
2+
3+
### Requirement: Resources view renders the active focus
4+
5+
The system SHALL provide a "Resources" tree view that renders the active focus as a hierarchy of profile → region → **service-and-resource-type** → resource (ARN). The service and resource type SHALL be combined into a single row rather than two nested levels: the row's label is the service name and its description (dimmed) is the resource type's plural name (e.g. label `SQS` / description `Queues`). A service with multiple resource types SHALL render one row per resource type, each sharing the service name (e.g. `Lambda — Functions` and `Lambda — Event Source Mappings`). When no focus is active, the view SHALL show a placeholder prompting the user to select a focus.
6+
7+
The combined service-and-resource-type row SHALL carry an icon that denotes the **target** of its profile, not the service: a LocalStack mark when the profile points at a LocalStack emulator, and a generic cloud icon when it points at real AWS. This icon SHALL appear on the combined row and SHALL NOT appear on the individual resource (ARN) leaves. A profile SHALL be treated as targeting LocalStack when it resolves to a custom/local endpoint (or is the synthetic `localstack` instance profile) and as targeting AWS otherwise. The system SHALL NOT bundle or display AWS-derived service icons.
8+
9+
#### Scenario: No focus shows a placeholder
10+
11+
- **WHEN** no focus selector has been activated
12+
- **THEN** the Resources view shows a placeholder prompting the user to select a focus
13+
14+
#### Scenario: Active focus is rendered hierarchically
15+
16+
- **WHEN** a focus is active
17+
- **THEN** the Resources view shows its profiles, and each profile expands to regions, then to combined service-and-resource-type rows, then to resources
18+
19+
#### Scenario: Service and resource type share one row
20+
21+
- **WHEN** a region contains the SQS service with its single `Queues` resource type
22+
- **THEN** a single row labeled `SQS` with the dimmed description `Queues` is shown, and expanding it lists the queue ARNs (which carry no row icon)
23+
24+
#### Scenario: A multi-resource-type service renders one row per type
25+
26+
- **WHEN** a region contains Lambda (which has both Functions and Event Source Mappings)
27+
- **THEN** two sibling rows appear — `Lambda` / `Functions` and `Lambda` / `Event Source Mappings` — each carrying the same target icon
28+
29+
#### Scenario: Empty resource types still appear
30+
31+
- **WHEN** a combined service-and-resource-type row has no resources
32+
- **THEN** the row is still shown and expands to a `[ No Resources ]` placeholder
33+
34+
#### Scenario: LocalStack-targeted rows show the LocalStack mark
35+
36+
- **WHEN** a service-and-resource-type row belongs to a profile that targets a LocalStack emulator (a custom/local endpoint or the synthetic `localstack` instance profile)
37+
- **THEN** the row's icon is the LocalStack mark, not an AWS-derived service icon
38+
39+
#### Scenario: AWS-targeted rows show a generic cloud icon
40+
41+
- **WHEN** a service-and-resource-type row belongs to a profile that targets real AWS (no custom endpoint)
42+
- **THEN** the row's icon is a generic cloud icon, not an AWS-derived service icon
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
## 1. Remove AWS-derived assets and API
2+
3+
- [x] 1.1 Delete the 6 AWS-derived SVGs in resources/icons/services/ (cloudformation, dynamodb, lambda, sns, sqs, states); remove the now-empty services/ directory if nothing else uses it
4+
- [x] 1.2 Remove ServiceProvider.getIconPath(serviceId) from src/platforms/aws/services/serviceProvider.ts, along with the now-unused fs and path imports and the symbol-misc fallback (also removed the orphaned `context` plumbing through ServiceProvider/DeclarativeServiceProvider/ProviderFactory and its callers)
5+
- [x] 1.3 Confirm no other references to getIconPath or resources/icons/services remain (grep)
6+
7+
## 2. Resolve the profile target
8+
9+
- [x] 2.1 In viewProvider.makeResourceProfiles, determine isLocalStack per profile from AWSConfig.getEndpointForProfile(profile.id) (custom/local endpoint, or synthetic `localstack` instance profile ⇒ LocalStack; otherwise AWS) via the isLocalStackProfile helper; getEndpointForProfile returns undefined on unresolvable config so it defaults to AWS and never throws
10+
- [x] 2.2 Add an isLocalStack flag to ResourceProfileTreeItem; the service-type row reads it via its existing parent chain (parent.parent.isLocalStack), so no ResourceRegionTreeItem change was needed
11+
12+
## 3. Apply the target-aware icon
13+
14+
- [x] 3.1 In ResourceServiceTypeTreeItem, set iconPath to new ThemeIcon("localstack-logo") when the target is LocalStack and new ThemeIcon("cloud") when it is AWS, replacing the provider.getIconPath(...) call
15+
- [x] 3.2 Verify the ARN leaf rows still carry no icon (ResourceArnTreeItem sets no iconPath)
16+
17+
## 4. Tests & verification
18+
19+
- [x] 4.1 Remove or update any existing tests that asserted per-service icon paths / getIconPath behavior (none existed — no-op)
20+
- [x] 4.2 Add a test asserting a LocalStack-targeted row resolves to ThemeIcon id "localstack-logo" and an AWS-targeted row resolves to ThemeIcon id "cloud" (src/test/resources/treeItems.test.ts; plus getEndpointForProfile tests in awsConfig.test.ts)
21+
- [x] 4.3 Run the full test suite (vscode-test) and confirm all pass (109 passing, 1 pre-existing pending)
22+
- [x] 4.4 Launch the extension and visually confirm: LocalStack-profile rows show the LocalStack mark and AWS-profile rows show the cloud icon, under both a light and a dark theme — confirmed OK by user
23+
- [x] 4.5 Grep the repo to confirm no AWS-derived icon assets remain referenced by the resource browser (no code refs; only LocalStack's own localstack.svg / localstack-icon PNG remain)

openspec/specs/resource-browser/spec.md

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ TBD - created by archiving change integrate-resource-browsers. Update Purpose af
55
## Requirements
66
### Requirement: Resources view renders the active focus
77

8-
The system SHALL provide a "Resources" tree view that renders the active focus as a hierarchy of profile → region → **service-and-resource-type** → resource (ARN). The service and resource type SHALL be combined into a single row rather than two nested levels: the row's label is the service name and its description (dimmed) is the resource type's plural name (e.g. label `SQS` / description `Queues`). A service with multiple resource types SHALL render one row per resource type, each sharing the service name and icon (e.g. `Lambda — Functions` and `Lambda — Event Source Mappings`). The service icon SHALL appear on this combined row and SHALL NOT appear on the individual resource (ARN) leaves. When no focus is active, the view SHALL show a placeholder prompting the user to select a focus.
8+
The system SHALL provide a "Resources" tree view that renders the active focus as a hierarchy of profile → region → **service-and-resource-type** → resource (ARN). The service and resource type SHALL be combined into a single row rather than two nested levels: the row's label is the service name and its description (dimmed) is the resource type's plural name (e.g. label `SQS` / description `Queues`). A service with multiple resource types SHALL render one row per resource type, each sharing the service name (e.g. `Lambda — Functions` and `Lambda — Event Source Mappings`). When no focus is active, the view SHALL show a placeholder prompting the user to select a focus.
9+
10+
The combined service-and-resource-type row SHALL carry an icon that denotes the **target** of its profile, not the service: a LocalStack mark when the profile points at a LocalStack emulator, and a generic cloud icon when it points at real AWS. This icon SHALL appear on the combined row and SHALL NOT appear on the individual resource (ARN) leaves. A profile SHALL be treated as targeting LocalStack when it resolves to a custom/local endpoint (or is the synthetic `localstack` instance profile) and as targeting AWS otherwise. The system SHALL NOT bundle or display AWS-derived service icons.
911

1012
#### Scenario: No focus shows a placeholder
1113

@@ -20,18 +22,28 @@ The system SHALL provide a "Resources" tree view that renders the active focus a
2022
#### Scenario: Service and resource type share one row
2123

2224
- **WHEN** a region contains the SQS service with its single `Queues` resource type
23-
- **THEN** a single row labeled `SQS` with the dimmed description `Queues` is shown (carrying the SQS icon), and expanding it lists the queue ARNs (which carry no service icon)
25+
- **THEN** a single row labeled `SQS` with the dimmed description `Queues` is shown, and expanding it lists the queue ARNs (which carry no row icon)
2426

2527
#### Scenario: A multi-resource-type service renders one row per type
2628

2729
- **WHEN** a region contains Lambda (which has both Functions and Event Source Mappings)
28-
- **THEN** two sibling rows appear — `Lambda` / `Functions` and `Lambda` / `Event Source Mappings` — each carrying the Lambda icon
30+
- **THEN** two sibling rows appear — `Lambda` / `Functions` and `Lambda` / `Event Source Mappings` — each carrying the same target icon
2931

3032
#### Scenario: Empty resource types still appear
3133

3234
- **WHEN** a combined service-and-resource-type row has no resources
3335
- **THEN** the row is still shown and expands to a `[ No Resources ]` placeholder
3436

37+
#### Scenario: LocalStack-targeted rows show the LocalStack mark
38+
39+
- **WHEN** a service-and-resource-type row belongs to a profile that targets a LocalStack emulator (a custom/local endpoint or the synthetic `localstack` instance profile)
40+
- **THEN** the row's icon is the LocalStack mark, not an AWS-derived service icon
41+
42+
#### Scenario: AWS-targeted rows show a generic cloud icon
43+
44+
- **WHEN** a service-and-resource-type row belongs to a profile that targets real AWS (no custom endpoint)
45+
- **THEN** the row's icon is a generic cloud icon, not an AWS-derived service icon
46+
3547
### Requirement: Dynamic expansion of wildcard selectors
3648

3749
The system SHALL expand wildcard and default selectors in the active focus when rendering: wildcard profiles to all configured profiles, wildcard/default regions to the appropriate region set, wildcard services to all supported service providers, and wildcard ARNs to the resources actually present (listed via the platform).

0 commit comments

Comments
 (0)