@robosophy/ui-vue 1.1.8 → 1.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ai-context.md +8 -0
- package/components.md +95 -0
- package/dist/components/RsIcon/RsIcon.types.d.ts +1 -1
- package/dist/components/RsIcon/RsIcon.types.d.ts.map +1 -1
- package/dist/components/RsIcon/RsIcon.vue.d.ts.map +1 -1
- package/dist/icon-manifest.json +1 -1
- package/dist/index.js +8 -3
- package/dist/index.js.map +1 -1
- package/dist/vite.d.ts +2 -1
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +289 -43
- package/dist/vite.js.map +1 -1
- package/icon-scanner.md +293 -0
- package/package.json +4 -2
package/icon-scanner.md
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
# Icon Scanner — @robosophy/ui-vue
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The Vite plugin `robosophyMaterialSymbols()` automatically detects which Material Symbols
|
|
6
|
+
icons are used in source code and generates an SVG sprite with only those icons.
|
|
7
|
+
No fonts are loaded.
|
|
8
|
+
|
|
9
|
+
The scanner works at **build time** (and in dev mode via file watching).
|
|
10
|
+
It scans every `.vue`, `.ts`, `.tsx`, `.js`, `.jsx`, and `.mjs` file in the project's `src/` directory
|
|
11
|
+
(or custom `roots`), extracts icon names, families, and weights, then downloads the matching SVGs
|
|
12
|
+
from the Google Material Design Icons repository.
|
|
13
|
+
|
|
14
|
+
When the package is built (`npm run build`), a **pre-scan** (the manifest script in
|
|
15
|
+
`scripts/generate-icon-manifest.mjs`) identifies icons used by the library's own components
|
|
16
|
+
(`RsSidebar`, `RsAccountSelector`, etc.) and writes them to `dist/icon-manifest.json`.
|
|
17
|
+
The Vite plugin loads this manifest at dev/build time as a baseline, then adds icons found
|
|
18
|
+
by scanning the consuming project's source.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Detection Patterns
|
|
23
|
+
|
|
24
|
+
The scanner uses five patterns to find icon usage in source code.
|
|
25
|
+
Patterns 4 and 5 were added for cases that the original three could not handle.
|
|
26
|
+
|
|
27
|
+
### Pattern 1: `<RsIcon>` Component Tag
|
|
28
|
+
|
|
29
|
+
**Regex:** `/ <RsIcon\b([^>]*)>/g`
|
|
30
|
+
|
|
31
|
+
Parses the attributes with `parseStaticAttributes` and reads `name`, `:name`, `family`,
|
|
32
|
+
`:family`, `weight`, `:weight`.
|
|
33
|
+
|
|
34
|
+
**Detected:**
|
|
35
|
+
```vue
|
|
36
|
+
<RsIcon name="home" />
|
|
37
|
+
<RsIcon name="home" family="rounded" />
|
|
38
|
+
<RsIcon :name="cond ? 'home' : 'settings'" family="rounded" />
|
|
39
|
+
<RsIcon name="home" weight="200" />
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**Not detected** (variable references):
|
|
43
|
+
```vue
|
|
44
|
+
<RsIcon :name="iconVar" />
|
|
45
|
+
<RsIcon :name="item.icon" />
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Filter:** icon names must match `/^[a-z0-9_]+$/` (lowercase with underscores).
|
|
49
|
+
|
|
50
|
+
**Dynamic `:name` with ternaries:** any string literal inside the `:name` expression
|
|
51
|
+
is extracted. For example `:name="cond ? 'add' : 'remove'"` extracts both `add` and `remove`.
|
|
52
|
+
|
|
53
|
+
**Dynamic `:name` without string literals:** if the binding is a plain variable reference
|
|
54
|
+
like `:name="current_icon"`, the scanner no longer treats it as an icon name
|
|
55
|
+
(improvement 3, see changelog). Use `include` or magic comments for these cases.
|
|
56
|
+
|
|
57
|
+
### Pattern 2: `rsPrimeIcon()` Helper
|
|
58
|
+
|
|
59
|
+
**Regex:** `/\brsPrimeIcon\(\s*["']([a-z0-9_]+)["'](?:\s*,\s*\{([^)]*)\})?/g`
|
|
60
|
+
|
|
61
|
+
**Detected:**
|
|
62
|
+
```ts
|
|
63
|
+
rsPrimeIcon("home")
|
|
64
|
+
rsPrimeIcon('settings', { family: 'rounded' })
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Not detected:**
|
|
68
|
+
```ts
|
|
69
|
+
rsPrimeIcon(dynamicName)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Pattern 3: Object Literal `icon:` Property
|
|
73
|
+
|
|
74
|
+
**Regex:** `/\bicon\s*:\s*["']([a-z0-9_]+)["']/g`
|
|
75
|
+
|
|
76
|
+
After matching, the scanner finds the enclosing `{...}` object and extracts `weight` and
|
|
77
|
+
`family` from its body (using `findEnclosingObject`). Comments are stripped from the body
|
|
78
|
+
before searching for these properties.
|
|
79
|
+
|
|
80
|
+
**Detected:**
|
|
81
|
+
```ts
|
|
82
|
+
const items = [
|
|
83
|
+
{ label: "Inbox", icon: "inbox" },
|
|
84
|
+
{ label: "Settings", icon: "settings", family: "rounded", weight: 200 },
|
|
85
|
+
];
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Not detected** (no `icon:` string literal):
|
|
89
|
+
```ts
|
|
90
|
+
const items = [{ icon: dynamicVar }]; // variable reference
|
|
91
|
+
{ icon } // shorthand property
|
|
92
|
+
{ ["icon"]: "home" } // computed property
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**`findEnclosingObject` behavior:**
|
|
96
|
+
- Scans backward from the `icon:` match to find the nearest opening `{`
|
|
97
|
+
- Skips over strings, template literals, escaped quotes, and comments (`//` and `/* */`)
|
|
98
|
+
- Scans forward to find the matching `}`
|
|
99
|
+
- Returns the body content between `{` and `}`
|
|
100
|
+
- The body is then cleaned of comments before searching for `weight:` and `family:`
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
// This correctly reads weight=200 and family="rounded":
|
|
104
|
+
{ icon: "home", weight: 200, family: "rounded" }
|
|
105
|
+
|
|
106
|
+
// Comments are ignored:
|
|
107
|
+
{ /* weight: 200, family: "rounded" */ icon: "home" }
|
|
108
|
+
// → default weight 400, default family "outlined"
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Pattern 4: Magic Comments
|
|
112
|
+
|
|
113
|
+
**Regex:** `/\/\/\s*@robosophy-icon:\s*([a-z0-9_]+)(?:\s+family=(\w+))?(?:\s+weight=(\d+))?/g`
|
|
114
|
+
|
|
115
|
+
For cases where the scanner cannot detect an icon statically (props, variables, API responses,
|
|
116
|
+
computed properties), use magic comments to declare them explicitly.
|
|
117
|
+
|
|
118
|
+
**Usage:**
|
|
119
|
+
```ts
|
|
120
|
+
// @robosophy-icon: home
|
|
121
|
+
// @robosophy-icon: settings family=rounded
|
|
122
|
+
// @robosophy-icon: warning weight=200
|
|
123
|
+
// @robosophy-icon: manage_accounts family=rounded weight=200
|
|
124
|
+
|
|
125
|
+
const sidebarItems = [
|
|
126
|
+
{ id: "home", label: "Home", icon: "home" },
|
|
127
|
+
{ id: "settings", label: "Settings", icon: "settings" },
|
|
128
|
+
];
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Best for:**
|
|
132
|
+
- Icon names passed as props to parent components
|
|
133
|
+
- Icon names in constants, enums, or configuration objects
|
|
134
|
+
- Icon names from API responses
|
|
135
|
+
- Icon names in computed properties or functions
|
|
136
|
+
|
|
137
|
+
**Placement:** anywhere in the source file. The scanner reads the entire file content.
|
|
138
|
+
|
|
139
|
+
### Pattern 5: Arrays of Icon Strings
|
|
140
|
+
|
|
141
|
+
**Regex:** `/\/\/\s*@robosophy-icon:\s*([a-z0-9_]+)(?:\s+family=(\w+))?(?:\s+weight=(\d+))?/g`
|
|
142
|
+
|
|
143
|
+
Wait, that's the same as Pattern 4. Let me check the actual regex I used...
|
|
144
|
+
|
|
145
|
+
Actually, Pattern 5 detects arrays assigned to variables whose name contains "icon" (case-insensitive).
|
|
146
|
+
|
|
147
|
+
**Regex:** `/(?:const|let|var)\s+(\w*[Ii]con\w*)\s*=\s*\[/g`
|
|
148
|
+
|
|
149
|
+
After finding the start of an icon array, it extracts all string literals from the brackets.
|
|
150
|
+
|
|
151
|
+
**Detected:**
|
|
152
|
+
```ts
|
|
153
|
+
const iconNames = ["home", "settings", "info"];
|
|
154
|
+
const sidebarIcons = ["home", "search"];
|
|
155
|
+
const ICON_LIST = ["warning", "error"];
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Not detected** (no "icon" in variable name):
|
|
159
|
+
```ts
|
|
160
|
+
const names = ["home", "settings"]; // variable name doesn't contain "icon"
|
|
161
|
+
const items = [{ icon: "home" }]; // already handled by Pattern 3
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**Caveat:** This pattern uses a heuristic (variable name contains "icon"). It may miss icons
|
|
165
|
+
stored in arrays with unrelated names. Use magic comments for those.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Plugin Options
|
|
170
|
+
|
|
171
|
+
| Option | Type | Default | Description |
|
|
172
|
+
|--------|------|---------|-------------|
|
|
173
|
+
| `include` | `IconInput[]` | `[]` | Icons to include regardless of detection. Each entry is a string (name) or `{ family, name, weight? }`. If weight is omitted, all valid weights (100-700) are included. |
|
|
174
|
+
| `scan` | `boolean` | `true` | Set to `false` to disable source scanning entirely. All icons must be in `include`. |
|
|
175
|
+
| `roots` | `string[]` | `["./src"]` | Directories to scan for icon usage. |
|
|
176
|
+
| `outputDir` | `string` | `"assets"` | Output directory for the SVG sprite. |
|
|
177
|
+
| `fontDisplay` | `string` | — | CSS `font-display` value. |
|
|
178
|
+
| `preload` | `boolean` | `false` | Preload the SVG sprite in the `<head>`. |
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## `icon-manifest.json`
|
|
183
|
+
|
|
184
|
+
The build script (`scripts/generate-icon-manifest.mjs`) scans the library's own components
|
|
185
|
+
under `src/components/` and produces `dist/icon-manifest.json`.
|
|
186
|
+
|
|
187
|
+
**File format:**
|
|
188
|
+
```json
|
|
189
|
+
{
|
|
190
|
+
"outlined": ["home", "search", "settings"],
|
|
191
|
+
"rounded": ["settings"],
|
|
192
|
+
"sharp": []
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**Role in the pipeline:**
|
|
197
|
+
1. Build generates the manifest from the library's component source
|
|
198
|
+
2. Vite plugin loads the manifest as a baseline
|
|
199
|
+
3. Plugin then scans the consuming project's source for additional icons
|
|
200
|
+
4. Plugin fetches SVGs for the combined set
|
|
201
|
+
|
|
202
|
+
**Why not just use the manifest alone?**
|
|
203
|
+
The manifest ensures that icons required by library components are always available,
|
|
204
|
+
even if the consuming project doesn't use those components. However, the manifest may
|
|
205
|
+
include icons that the consumer never uses (it's a conservative baseline). The plugin's
|
|
206
|
+
source scan adds only the icons that the consumer's own code references.
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Known Limitations
|
|
211
|
+
|
|
212
|
+
### Icon names passed as props
|
|
213
|
+
|
|
214
|
+
```vue
|
|
215
|
+
<!-- RsSidebar uses :name="icon" which is a prop -->
|
|
216
|
+
<RsIcon :name="icon" />
|
|
217
|
+
|
|
218
|
+
<!-- RsSidebar uses :name="item.icon" from v-for iteration -->
|
|
219
|
+
<RsIcon :name="item.icon" />
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
The scanner cannot resolve the runtime value of `icon` or `item.icon`.
|
|
223
|
+
|
|
224
|
+
**Solutions:**
|
|
225
|
+
1. Use `include` in the Vite plugin config
|
|
226
|
+
2. Add magic comments (`// @robosophy-icon: icon_name`) in the component file
|
|
227
|
+
3. Declare the icon in the same file as the data source (Pattern 3 will detect it)
|
|
228
|
+
|
|
229
|
+
### Icon names in arrays of strings
|
|
230
|
+
|
|
231
|
+
```ts
|
|
232
|
+
const iconNames = ["home", "settings"];
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Pattern 5 detects these only if the variable name contains "icon".
|
|
236
|
+
|
|
237
|
+
### Icon names in enums or constants
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
export enum Icons {
|
|
241
|
+
HOME = "home",
|
|
242
|
+
SETTINGS = "settings",
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Not detected. Use magic comments or `include`.
|
|
247
|
+
|
|
248
|
+
### Icon names from API responses
|
|
249
|
+
|
|
250
|
+
```ts
|
|
251
|
+
const config = await fetchConfig();
|
|
252
|
+
return <RsIcon :name="config.iconName" />;
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Cannot be detected statically. Use `include` or magic comments.
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## Best Practices
|
|
260
|
+
|
|
261
|
+
### For AI agents editing consuming projects
|
|
262
|
+
|
|
263
|
+
1. **Static icons:** use `<RsIcon name="icon_name" />` — always detected by Pattern 1
|
|
264
|
+
2. **Object configs:** use `{ icon: "icon_name" }` — detected by Pattern 3
|
|
265
|
+
3. **Dynamic icons:** use magic comments or `include`
|
|
266
|
+
4. **PrimeVue buttons:** use `rsPrimeIcon('icon_name')` — detected by Pattern 2
|
|
267
|
+
5. **Props to RsSidebar:** icons in the `items` array objects are detected by Pattern 3
|
|
268
|
+
|
|
269
|
+
### For humans
|
|
270
|
+
|
|
271
|
+
When adding a new icon to the codebase:
|
|
272
|
+
1. Check if it's statically detectable (direct string literal)
|
|
273
|
+
2. If not, add a magic comment near the usage
|
|
274
|
+
3. If the icon is used in many places, add it to the plugin config's `include`
|
|
275
|
+
|
|
276
|
+
### Tag helper
|
|
277
|
+
|
|
278
|
+
To quickly test if an icon name exists in the Material Symbols set:
|
|
279
|
+
|
|
280
|
+
```
|
|
281
|
+
https://fonts.google.com/icons?icon.query=icon_name
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## Changelog
|
|
287
|
+
|
|
288
|
+
| Date | Change |
|
|
289
|
+
|------|--------|
|
|
290
|
+
| 2026-07-13 | **Pattern 4** (magic comments) and **Pattern 5** (icon arrays) added |
|
|
291
|
+
| 2026-07-13 | `findEnclosingObject`: now handles comments, escaped quotes, and template literals |
|
|
292
|
+
| 2026-07-13 | `resolveNames`: dynamic `:name` bindings no longer fall back to treating variable names as icon names |
|
|
293
|
+
| 2026-07-13 | Pattern 3: weight and family are now extracted from the enclosing object literal |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@robosophy/ui-vue",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.10",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"homepage": "https://robosophy.gitlab.io/robosophy-basf/robosophy-library/",
|
|
@@ -25,12 +25,14 @@
|
|
|
25
25
|
},
|
|
26
26
|
"./AGENTS.md": "./AGENTS.md",
|
|
27
27
|
"./ai-context.md": "./ai-context.md",
|
|
28
|
-
"./components.md": "./components.md"
|
|
28
|
+
"./components.md": "./components.md",
|
|
29
|
+
"./icon-scanner.md": "./icon-scanner.md"
|
|
29
30
|
},
|
|
30
31
|
"files": [
|
|
31
32
|
"AGENTS.md",
|
|
32
33
|
"ai-context.md",
|
|
33
34
|
"components.md",
|
|
35
|
+
"icon-scanner.md",
|
|
34
36
|
"dist",
|
|
35
37
|
"README.md",
|
|
36
38
|
"package.json"
|