Skip to content

Commit deac9cd

Browse files
chore: bump .claude submodule pointer
1 parent e11e8bd commit deac9cd

5 files changed

Lines changed: 192 additions & 48 deletions

File tree

.claude

src/components/ResourceCard.astro

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
---
22
import { Image } from 'astro:assets';
3-
import { RESOURCE_TYPE_LABEL, RESOURCE_TYPE_ICON, resourceIcon, type Resource } from '@/data/resources';
3+
import {
4+
RESOURCE_TYPE_LABEL,
5+
RESOURCE_TYPE_ICON,
6+
RESOURCE_TAG_LABEL,
7+
resourceIcon,
8+
type Resource,
9+
} from '@/data/resources';
410
import Icon from './Icon.astro';
511
612
interface Props {
713
resource: Resource;
814
}
915
const { resource } = Astro.props;
10-
const { title, type, secondaryTypes, url, description, note, authors, image } = resource;
16+
const { title, type, secondaryTypes, tags, url, description, note, authors, image } = resource;
1117
const typeLabel = RESOURCE_TYPE_LABEL[type];
1218
const iconName = resourceIcon(resource);
1319
// A "#" url is an intentional placeholder (a resource that isn't live yet) — render the title as
@@ -79,28 +85,41 @@ const isPlaceholder = url === '#';
7985
<p class="mt-2 text-sm text-muted">{description}</p>
8086
{note && <p class="mt-2 text-sm text-muted">{note}</p>}
8187

82-
{authors && authors.length > 0 && (
83-
<p class="mt-auto pt-4 text-xs text-muted">
84-
by{' '}
85-
{authors.map((a, i) => (
86-
<>
87-
{i > 0 && (i === authors.length - 1 ? ' and ' : ', ')}
88-
{a.url ? (
89-
<a
90-
href={a.url}
91-
target="_blank"
92-
rel="noopener"
93-
aria-label={`${a.name}'s profile (opens in a new tab)`}
94-
class="font-medium text-[var(--accent-strong)] hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--bg)]"
95-
>
96-
{a.name}
97-
</a>
98-
) : (
99-
<span class="font-medium text-default">{a.name}</span>
100-
)}
101-
</>
102-
))}
103-
</p>
104-
)}
88+
<!-- Tags + author pinned to the bottom as one group (matches PostCard's mt-auto meta block) -->
89+
<div class="mt-auto">
90+
{tags && tags.length > 0 && (
91+
<ul class="mt-4 flex flex-wrap gap-1.5" aria-label="Tags">
92+
{tags.map((t) => (
93+
<li class="rounded-md border border-default px-2 py-0.5 text-xs font-medium text-muted">
94+
{RESOURCE_TAG_LABEL[t]}
95+
</li>
96+
))}
97+
</ul>
98+
)}
99+
100+
{authors && authors.length > 0 && (
101+
<p class="mt-3 text-xs text-muted">
102+
by{' '}
103+
{authors.map((a, i) => (
104+
<>
105+
{i > 0 && (i === authors.length - 1 ? ' and ' : ', ')}
106+
{a.url ? (
107+
<a
108+
href={a.url}
109+
target="_blank"
110+
rel="noopener"
111+
aria-label={`${a.name}'s profile (opens in a new tab)`}
112+
class="font-medium text-[var(--accent-strong)] hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--bg)]"
113+
>
114+
{a.name}
115+
</a>
116+
) : (
117+
<span class="font-medium text-default">{a.name}</span>
118+
)}
119+
</>
120+
))}
121+
</p>
122+
)}
123+
</div>
105124
</div>
106125
</article>

src/data/resources.ts

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
* NOT repeat a resource's own marketing claims as fact ("runs client-side / no data leaves
1616
* your browser", "fastest", "secure") unless verified; on our site they read as our endorsement.
1717
* 2. If the `type` is new, add it to ResourceType + RESOURCE_TYPE_LABEL + RESOURCE_TYPE_ICON.
18+
* 2b. Add subject `tags` (a CLOSED enum, like the blog's) — the /resources page renders them as a
19+
* second filter row (Type × Tag, AND-combined; buttons only, NO archive route). Assign only
20+
* tags the resource OBSERVABLY fits. To add a new tag, extend ResourceTag + RESOURCE_TAG_LABEL.
1821
* 3. (Optional) Give it a real logo: drop the file in `src/assets/resources/`, `import` it at the
1922
* top of this file, and set it as the entry's `image`. Without one, the card shows a generated
2023
* icon tile (brand gradient + the type's Icon).
@@ -41,7 +44,7 @@ import muleySolutionsToolsCover from '@/assets/resources/muley-solutions-tools.p
4144
import mulesoftCommunitySlackCover from '@/assets/resources/mulesoft-community-slack.png';
4245
import mclsMuleSecurePropertiesCover from '@/assets/resources/mcls-mule-secure-properties.jpeg';
4346
import muleflowBizviewCover from '@/assets/resources/muleflow-bizview.jpg';
44-
import muleflowVisualizerCover from '@/assets/resources/muleflow-visualizer.jpg';
47+
import muleflowVisualizerForBitbucketCover from '@/assets/resources/muleflow-visualizer-for-bitbucket.jpg';
4548
import muleflowVisualizerForGithubCover from '@/assets/resources/muleflow-visualizer-for-github.jpg';
4649

4750
export type ResourceType =
@@ -54,6 +57,33 @@ export type ResourceType =
5457
| 'slack'
5558
| 'tool';
5659

60+
/**
61+
* Subject tags — a CLOSED enum (mirrors the blog's `TAGS`). These cross-cut `type`: several tools
62+
* of different types can share a tag (e.g. MuleFD and the MuleFlow extensions are all `visualizer`s).
63+
* Drives the second filter row on /resources. To add one, extend this + RESOURCE_TAG_LABEL.
64+
*/
65+
export type ResourceTag =
66+
| 'ai'
67+
| 'cli'
68+
| 'dataweave'
69+
| 'formatter'
70+
| 'learning'
71+
| 'monitoring'
72+
| 'security'
73+
| 'visualizer';
74+
75+
/** Human-readable label per tag — drives the filter chips + card chips. Alphabetical by label. */
76+
export const RESOURCE_TAG_LABEL: Record<ResourceTag, string> = {
77+
ai: 'AI',
78+
cli: 'CLI',
79+
dataweave: 'DataWeave',
80+
formatter: 'Formatter',
81+
learning: 'Learning',
82+
monitoring: 'Monitoring',
83+
security: 'Security',
84+
visualizer: 'Visualizer',
85+
};
86+
5787
export interface Resource {
5888
/** Stable kebab-case slug — used for list keys and filter data attributes. */
5989
id: string;
@@ -66,6 +96,11 @@ export interface Resource {
6696
* its primary `type`. Keep off `type` to avoid duplicates.
6797
*/
6898
secondaryTypes?: ResourceType[];
99+
/**
100+
* Subject tags (closed `ResourceTag` enum) — cross-cutting the `type`. Drives the second filter
101+
* row and the card's tag chips. Assign only tags the resource observably fits; omit if none apply.
102+
*/
103+
tags?: ResourceTag[];
69104
/** Off-site link (Chrome Web Store, VSCode Marketplace, GitHub, etc.). */
70105
url: string;
71106
/** One-to-three-sentence blurb shown on the card. */
@@ -124,6 +159,7 @@ export const RESOURCES: Resource[] = [
124159
id: 'anypoint-monitor',
125160
title: 'Anypoint Monitor',
126161
type: 'vscode-extension',
162+
tags: ['monitoring'],
127163
url: 'https://marketplace.visualstudio.com/items?itemName=EdgarMoran.anypoint-monitor',
128164
description:
129165
'A VSCode extension that surfaces Anypoint Platform monitoring data without leaving your editor.',
@@ -145,6 +181,7 @@ export const RESOURCES: Resource[] = [
145181
title: 'DataWeave Studio',
146182
type: 'ide',
147183
secondaryTypes: ['vscode-extension'],
184+
tags: ['dataweave'],
148185
url: 'https://github.com/Ashutosh-Vijay/DataWeave-Studio',
149186
description:
150187
'A local IDE for DataWeave 2.0 — run, test, and debug transforms without Anypoint Studio. Available as a desktop app or a VSCode extension, with the engine and runtime bundled in for offline use.',
@@ -155,6 +192,7 @@ export const RESOURCES: Resource[] = [
155192
id: 'dwcode',
156193
title: 'DWCode',
157194
type: 'challenges',
195+
tags: ['dataweave', 'learning'],
158196
url: 'https://dwcode.vercel.app/',
159197
description:
160198
'A browser-based DataWeave practice platform — coding problems, a playground, contests, and a leaderboard to sharpen your DataWeave skills.',
@@ -168,6 +206,7 @@ export const RESOURCES: Resource[] = [
168206
id: 'fluxmule',
169207
title: 'FluxMule',
170208
type: 'chrome-extension',
209+
secondaryTypes: ['ide'],
171210
url: 'https://chromewebstore.google.com/detail/fluxmule/imnkaohplcoblbkccemmdbbpbnenhjec',
172211
description:
173212
'A Chrome extension that streamlines working inside the Anypoint Platform right from your browser.',
@@ -183,6 +222,7 @@ export const RESOURCES: Resource[] = [
183222
id: 'integration-trails',
184223
title: 'Integration Trails',
185224
type: 'challenges',
225+
tags: ['dataweave', 'learning'],
186226
url: 'https://app.integrationtrails.io/challenges',
187227
description:
188228
'A hands-on learning platform for integration developers — practice DataWeave and MuleSoft skills through graded challenges, structured trails, and a gamified XP leaderboard.',
@@ -193,6 +233,7 @@ export const RESOURCES: Resource[] = [
193233
id: 'matt-pocock-skills',
194234
title: 'Matt Pocock Skills',
195235
type: 'agent-skills',
236+
tags: ['ai'],
196237
url: 'https://github.com/mattpocock/skills',
197238
description:
198239
'A collection of AI agent skills I reach for a lot — TDD, debugging, code review, domain modeling, and more.',
@@ -204,6 +245,7 @@ export const RESOURCES: Resource[] = [
204245
id: 'mcls-mule-secure-properties',
205246
title: 'MCLS Mule Secure Properties',
206247
type: 'vscode-extension',
248+
tags: ['security'],
207249
url: 'https://marketplace.visualstudio.com/items?itemName=MasterCompcouk.mcls-mule-secure-properties',
208250
description:
209251
'A VSCode extension that previews, encrypts, and decrypts MuleSoft secure properties in YAML and .properties files.',
@@ -214,6 +256,7 @@ export const RESOURCES: Resource[] = [
214256
id: 'mule-xml-formatter',
215257
title: 'Mule XML Formatter',
216258
type: 'vscode-extension',
259+
tags: ['formatter'],
217260
url: 'https://marketplace.visualstudio.com/items?itemName=SravanNerella.mule-xml-formatter',
218261
description:
219262
'A VSCode extension that formats Mule XML config files consistently — tidying indentation and structure so your flow XML stays clean and readable.',
@@ -223,6 +266,7 @@ export const RESOURCES: Resource[] = [
223266
id: 'mulefd',
224267
title: 'MuleFD',
225268
type: 'tool',
269+
tags: ['cli', 'visualizer'],
226270
url: 'https://github.com/manikmagar/mulefd',
227271
description:
228272
'A CLI tool that reads Mule 3/4 app config and generates visual flow diagrams — mapping how flows connect, spotting unused or recursive flows, and untangling flow spaghetti.',
@@ -233,26 +277,29 @@ export const RESOURCES: Resource[] = [
233277
id: 'muleflow-bizview',
234278
title: 'MuleFlow BizView',
235279
type: 'chrome-extension',
280+
tags: ['visualizer'],
236281
url: 'https://chromewebstore.google.com/detail/muleflow-bizview/jjglahhpajacblleceggnmgjdoaaehpn',
237282
description:
238283
'A Chrome extension that turns a MuleSoft project into interactive diagrams — an overview map of how flows connect and left-to-right flowcharts for each flow — and searches across the business logic (DataWeave, SQL, HTTP parameters). Exports diagrams to Draw.io, LucidChart, or PNG.',
239284
image: muleflowBizviewCover,
240285
authors: [{ name: 'Ronald Vega', url: 'https://www.linkedin.com/in/ronald-vega/' }],
241286
},
242287
{
243-
id: 'muleflow-visualizer',
244-
title: 'MuleFlow Visualizer',
288+
id: 'muleflow-visualizer-for-bitbucket',
289+
title: 'MuleFlow Visualizer for Bitbucket',
245290
type: 'chrome-extension',
291+
tags: ['visualizer'],
246292
url: 'https://chromewebstore.google.com/detail/muleflow-visualizer/kfnggnohaknfjipdnkkfibdgkhecbfkg',
247293
description:
248294
'A Chrome extension that renders MuleSoft XML files as flow diagrams directly in Bitbucket Cloud — showing flows, subflows, and MUnit tests, with pull-request diff support and a component search.',
249-
image: muleflowVisualizerCover,
295+
image: muleflowVisualizerForBitbucketCover,
250296
authors: [{ name: 'Ronald Vega', url: 'https://www.linkedin.com/in/ronald-vega/' }],
251297
},
252298
{
253299
id: 'muleflow-visualizer-for-github',
254300
title: 'MuleFlow Visualizer for GitHub',
255301
type: 'chrome-extension',
302+
tags: ['visualizer'],
256303
url: 'https://chromewebstore.google.com/detail/muleflow-visualizer-for-g/miooblfebdbnpnliffkpmbdgnbhomgjf',
257304
description:
258305
'A Chrome extension that renders MuleSoft XML files as flow diagrams directly in GitHub — on both repository files and pull-request diffs — with a component search and a property inspector.',
@@ -276,6 +323,7 @@ export const RESOURCES: Resource[] = [
276323
id: 'muley-solutions-tools',
277324
title: 'Muley Solutions Tools',
278325
type: 'tool',
326+
tags: ['ai'],
279327
url: 'https://muley.solutions/tools',
280328
description:
281329
'A curated directory of working MuleSoft Agent Fabric and Anypoint Platform tools and interactive demos — agentic asset designers, an Omni Gateway policy marketplace, agent/MCP testing toolkits, network tracers, and connectors — filterable by category and each linking out to the live tool or demo.',
@@ -289,6 +337,7 @@ export const RESOURCES: Resource[] = [
289337
id: 'dataweave-slack',
290338
title: 'Official DataWeave Language Slack Workspace',
291339
type: 'slack',
340+
tags: ['dataweave'],
292341
url: 'https://join.slack.com/t/dataweavelanguage/shared_invite/zt-1ewv2igp0-3ZiqQqaMdO_utwaEjxBpTw',
293342
description:
294343
'A community Slack workspace focused on the DataWeave language — ask questions, share transforms, and connect with other DataWeave developers. The link is an open invite to join.',
@@ -308,6 +357,7 @@ export const RESOURCES: Resource[] = [
308357
id: 'prostdev-skills',
309358
title: 'ProstDev Skills',
310359
type: 'agent-skills',
360+
tags: ['ai'],
311361
url: 'https://github.com/ProstDev/skills',
312362
description:
313363
'The AI agent skills I use for MuleSoft content and development, packaged so you can install and run them yourself.',
@@ -318,6 +368,7 @@ export const RESOURCES: Resource[] = [
318368
id: 'upendra-mulesoft-tools',
319369
title: "Upendra's MuleSoft Tools",
320370
type: 'tool',
371+
tags: ['formatter', 'security'],
321372
url: 'https://upendra-thunuguntla.github.io/#tools',
322373
description:
323374
'A growing collection of free, browser-based MuleSoft dev tools — a Secure Properties generator, JSON↔RAML and RAML↔OAS converters, YAML↔Properties converters, a MuleSoft log to cURL converter, a Mule XML SDK helper, a cron expression builder, and more.',

src/pages/llms.txt.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { APIRoute } from 'astro';
22
import { getPosts, getTags, getCategories, tagUrl, categoryUrl } from '@/lib/content';
33
import { PLAYLISTS, videosInPlaylist } from '@/data/videos';
4-
import { RESOURCES, RESOURCE_TYPE_LABEL } from '@/data/resources';
4+
import { RESOURCES, RESOURCE_TYPE_LABEL, RESOURCE_TAG_LABEL } from '@/data/resources';
55
import { SITE } from '@/config';
66

77
/**
@@ -57,7 +57,12 @@ export const GET: APIRoute = async () => {
5757
for (const r of RESOURCES) {
5858
if (r.url === '#') continue; // skip placeholders that aren't live yet
5959
const by = r.authors?.length ? ` (by ${r.authors.map((a) => a.name).join(' and ')})` : '';
60-
lines.push(`- [${r.title}](${r.url}) — ${RESOURCE_TYPE_LABEL[r.type]}${by}: ${r.description}`);
60+
const tags = r.tags?.length
61+
? ` [${r.tags.map((t) => RESOURCE_TAG_LABEL[t]).join(', ')}]`
62+
: '';
63+
lines.push(
64+
`- [${r.title}](${r.url}) — ${RESOURCE_TYPE_LABEL[r.type]}${tags}${by}: ${r.description}`
65+
);
6166
}
6267
lines.push('');
6368
}

0 commit comments

Comments
 (0)