@canonical/code-standards 0.1.0 → 0.1.1
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/.github/PULL_REQUEST_TEMPLATE.md +13 -0
- package/.github/workflows/ci.yml +40 -0
- package/biome.json +6 -0
- package/bun.lock +200 -0
- package/data/code.ttl +208 -167
- package/data/css.ttl +110 -91
- package/data/icons.ttl +186 -150
- package/data/packaging.ttl +428 -170
- package/data/react.ttl +306 -244
- package/data/rust.ttl +563 -467
- package/data/storybook.ttl +108 -90
- package/data/styling.ttl +40 -40
- package/data/tsdoc.ttl +111 -86
- package/data/turtle.ttl +89 -68
- package/definitions/CodeStandard.ttl +28 -20
- package/docs/code.md +37 -327
- package/docs/css.md +20 -19
- package/docs/icons.md +37 -41
- package/docs/index.md +2 -1
- package/docs/packaging.md +643 -0
- package/docs/react.md +54 -58
- package/docs/rust.md +92 -158
- package/docs/storybook.md +18 -20
- package/docs/styling.md +8 -8
- package/docs/tsdoc.md +16 -16
- package/docs/turtle.md +15 -15
- package/package.json +16 -2
- package/skills/add-standard/SKILL.md +83 -47
- package/src/scripts/generate-docs.ts +95 -13
- package/src/scripts/index.ts +4 -2
- package/tsconfig.json +8 -0
package/data/icons.ttl
CHANGED
|
@@ -15,32 +15,33 @@ cs:IconFileStorage a cs:CodeStandard ;
|
|
|
15
15
|
cs:name "icons/file/storage" ;
|
|
16
16
|
cs:hasCategory cs:IconsCategory ;
|
|
17
17
|
cs:description "Each icon shall be stored as a single SVG file in the `icons/` directory." ;
|
|
18
|
-
cs:
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
cs:do [
|
|
19
|
+
cs:description "Store each icon as an individual SVG file in the designated `icons/` directory." ;
|
|
20
|
+
cs:code """
|
|
21
21
|
icons/
|
|
22
22
|
├── warning.svg
|
|
23
23
|
├── search.svg
|
|
24
24
|
└── github.svg
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
cs:
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
"""
|
|
26
|
+
] ;
|
|
27
|
+
cs:dont [
|
|
28
|
+
cs:description "Store icons in nested directories or use incorrect file extensions." ;
|
|
29
|
+
cs:code """
|
|
30
30
|
icons/
|
|
31
31
|
├── variants/
|
|
32
32
|
│ └── warning-dark.svg
|
|
33
33
|
├── warning/
|
|
34
34
|
│ └── index.svg
|
|
35
35
|
└── warning.svg.txt
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
"""
|
|
37
|
+
] ;
|
|
38
|
+
cs:dont [
|
|
39
|
+
cs:description "Combine multiple icons into a single SVG file." ;
|
|
40
|
+
cs:code """
|
|
40
41
|
icons/
|
|
41
42
|
└── all-icons.svg
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
"""
|
|
44
|
+
] .
|
|
44
45
|
|
|
45
46
|
# Icon File Naming Standard
|
|
46
47
|
cs:IconFileNaming a cs:CodeStandard ;
|
|
@@ -49,49 +50,52 @@ cs:IconFileNaming a cs:CodeStandard ;
|
|
|
49
50
|
cs:description """Icon files must:
|
|
50
51
|
- Use kebab-case naming
|
|
51
52
|
- Use semantic identifiers""" ;
|
|
52
|
-
cs:
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
cs:do [
|
|
54
|
+
cs:description "Use kebab-case and a semantic identifier for the icon file name." ;
|
|
55
|
+
cs:code """
|
|
55
56
|
warning.svg
|
|
56
57
|
user-profile.svg
|
|
57
58
|
arrow-down.svg
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
cs:
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
"""
|
|
60
|
+
] ;
|
|
61
|
+
cs:dont [
|
|
62
|
+
cs:description "Include size, theme, or redundant suffixes in the file name." ;
|
|
63
|
+
cs:code """
|
|
63
64
|
warning-16.svg
|
|
64
65
|
warning-dark.svg
|
|
65
66
|
warning_icon.svg
|
|
66
67
|
WARNING.svg
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
"""
|
|
69
|
+
] ;
|
|
70
|
+
cs:dont [
|
|
71
|
+
cs:description "Use non-semantic or ambiguous names that do not describe the icon." ;
|
|
72
|
+
cs:code """
|
|
71
73
|
icon1.svg
|
|
72
74
|
shape.svg
|
|
73
|
-
|
|
74
|
-
|
|
75
|
+
"""
|
|
76
|
+
] .
|
|
75
77
|
|
|
76
78
|
# Icon Group Element Standard
|
|
77
79
|
cs:IconGroupElement a cs:CodeStandard ;
|
|
78
80
|
cs:name "icons/svg/group-element" ;
|
|
79
81
|
cs:hasCategory cs:IconsCategory ;
|
|
80
82
|
cs:description "Each icon's SVG markup must contain a single `<g>` element with an `id` matching the filename (without `.svg`)." ;
|
|
81
|
-
cs:
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
cs:do [
|
|
84
|
+
cs:description "Include a single `<g>` element with an `id` that matches the filename." ;
|
|
85
|
+
cs:language "svg" ;
|
|
86
|
+
cs:code """
|
|
84
87
|
<!-- warning.svg -->
|
|
85
88
|
<svg>
|
|
86
89
|
<g id="warning">
|
|
87
90
|
<!-- icon paths -->
|
|
88
91
|
</g>
|
|
89
92
|
</svg>
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
cs:
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
"""
|
|
94
|
+
] ;
|
|
95
|
+
cs:dont [
|
|
96
|
+
cs:description "Use multiple `<g>` elements or an `id` that does not match the filename." ;
|
|
97
|
+
cs:language "svg" ;
|
|
98
|
+
cs:code """
|
|
95
99
|
<!-- warning.svg -->
|
|
96
100
|
<svg>
|
|
97
101
|
<g id="icon-group">
|
|
@@ -101,115 +105,126 @@ cs:IconGroupElement a cs:CodeStandard ;
|
|
|
101
105
|
<!-- alternate paths -->
|
|
102
106
|
</g>
|
|
103
107
|
</svg>
|
|
104
|
-
|
|
105
|
-
|
|
108
|
+
"""
|
|
109
|
+
] .
|
|
106
110
|
|
|
107
111
|
# Icon ViewBox Standard
|
|
108
112
|
cs:IconViewBox a cs:CodeStandard ;
|
|
109
113
|
cs:name "icons/svg/viewbox" ;
|
|
110
114
|
cs:hasCategory cs:IconsCategory ;
|
|
111
115
|
cs:description "All icon SVGs must use a `viewBox` of `0 0 16 16`." ;
|
|
112
|
-
cs:
|
|
113
|
-
|
|
114
|
-
|
|
116
|
+
cs:do [
|
|
117
|
+
cs:description "Set the `viewBox` attribute to `0 0 16 16` in the `<svg>` element." ;
|
|
118
|
+
cs:language "svg" ;
|
|
119
|
+
cs:code """
|
|
115
120
|
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'>
|
|
116
121
|
<g id="icon">
|
|
117
122
|
<!-- Icon content scaled to 16x16 -->
|
|
118
123
|
</g>
|
|
119
124
|
</svg>
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
cs:
|
|
123
|
-
|
|
124
|
-
|
|
125
|
+
"""
|
|
126
|
+
] ;
|
|
127
|
+
cs:dont [
|
|
128
|
+
cs:description "Use a `viewBox` with dimensions other than `16 16`." ;
|
|
129
|
+
cs:language "svg" ;
|
|
130
|
+
cs:code """
|
|
125
131
|
<!-- Bad: Different viewBox size -->
|
|
126
132
|
<svg viewBox="0 0 24 24">
|
|
127
133
|
<g id="icon">...</g>
|
|
128
134
|
</svg>
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
135
|
+
"""
|
|
136
|
+
] ;
|
|
137
|
+
cs:dont [
|
|
138
|
+
cs:description "Use a non-square `viewBox`." ;
|
|
139
|
+
cs:language "svg" ;
|
|
140
|
+
cs:code """
|
|
133
141
|
<!-- Bad: Non-square viewBox -->
|
|
134
142
|
<svg viewBox="0 0 16 24">
|
|
135
143
|
<g id="icon">...</g>
|
|
136
144
|
</svg>
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
145
|
+
"""
|
|
146
|
+
] ;
|
|
147
|
+
cs:dont [
|
|
148
|
+
cs:description "Omit the `viewBox` attribute." ;
|
|
149
|
+
cs:language "svg" ;
|
|
150
|
+
cs:code """
|
|
141
151
|
<!-- Bad: Missing viewBox -->
|
|
142
152
|
<svg width="16" height="16">
|
|
143
153
|
<g id="icon">...</g>
|
|
144
154
|
</svg>
|
|
145
|
-
|
|
146
|
-
|
|
155
|
+
"""
|
|
156
|
+
] .
|
|
147
157
|
|
|
148
158
|
# Icon Color Usage Standard
|
|
149
159
|
cs:IconColorUsage a cs:CodeStandard ;
|
|
150
160
|
cs:name "icons/svg/color-usage" ;
|
|
151
161
|
cs:hasCategory cs:IconsCategory ;
|
|
152
162
|
cs:description "Icon must use `fill` with `currentColor` for their paths and shapes." ;
|
|
153
|
-
cs:
|
|
154
|
-
|
|
155
|
-
|
|
163
|
+
cs:do [
|
|
164
|
+
cs:description "Use `currentColor` for the `fill` of paths in non-branded icons." ;
|
|
165
|
+
cs:language "svg" ;
|
|
166
|
+
cs:code """
|
|
156
167
|
<!-- Non-branded icon -->
|
|
157
168
|
<svg viewBox="0 0 16 16">
|
|
158
169
|
<g id="search">
|
|
159
170
|
<path fill="currentColor" d="..." />
|
|
160
171
|
</g>
|
|
161
172
|
</svg>
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
cs:
|
|
165
|
-
|
|
166
|
-
|
|
173
|
+
"""
|
|
174
|
+
] ;
|
|
175
|
+
cs:dont [
|
|
176
|
+
cs:description "Use hard-coded colors in icons." ;
|
|
177
|
+
cs:language "svg" ;
|
|
178
|
+
cs:code """
|
|
167
179
|
<path fill="#000000" d="..." />
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
180
|
+
"""
|
|
181
|
+
] ;
|
|
182
|
+
cs:dont [
|
|
183
|
+
cs:description "Use `opacity` to create different shades of a color." ;
|
|
184
|
+
cs:language "svg" ;
|
|
185
|
+
cs:code """
|
|
172
186
|
<path fill="currentColor" opacity="0.5" d="..." />
|
|
173
|
-
|
|
174
|
-
|
|
187
|
+
"""
|
|
188
|
+
] .
|
|
175
189
|
|
|
176
190
|
# Icon Variant Standard
|
|
177
191
|
cs:IconVariant a cs:CodeStandard ;
|
|
178
192
|
cs:name "icons/variant/single-file" ;
|
|
179
193
|
cs:hasCategory cs:IconsCategory ;
|
|
180
194
|
cs:description "Each icon concept must have exactly one SVG file. Size and theme variants are not allowed." ;
|
|
181
|
-
cs:
|
|
182
|
-
|
|
183
|
-
|
|
195
|
+
cs:do [
|
|
196
|
+
cs:description "Use a single SVG file for each icon and control its size and color with CSS." ;
|
|
197
|
+
cs:language "svg" ;
|
|
198
|
+
cs:code """
|
|
184
199
|
<!-- Single icon file -->
|
|
185
200
|
<svg viewBox="0 0 16 16">
|
|
186
201
|
<g id="warning">
|
|
187
202
|
<path fill="currentColor" d="..." />
|
|
188
203
|
</g>
|
|
189
204
|
</svg>
|
|
190
|
-
|
|
191
|
-
```css
|
|
205
|
+
|
|
192
206
|
/* CSS usage */
|
|
193
207
|
.small-icon { font-size: 1em; } /* 16px */
|
|
194
208
|
.large-icon { font-size: 2em; } /* 32px */
|
|
195
209
|
.warning-icon { color: var(--color-warning); }
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
cs:
|
|
199
|
-
|
|
200
|
-
|
|
210
|
+
"""
|
|
211
|
+
] ;
|
|
212
|
+
cs:dont [
|
|
213
|
+
cs:description "Create multiple files for different icon sizes." ;
|
|
214
|
+
cs:code """
|
|
201
215
|
warning.svg
|
|
202
216
|
warning-small.svg
|
|
203
217
|
warning-large.svg
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
218
|
+
"""
|
|
219
|
+
] ;
|
|
220
|
+
cs:dont [
|
|
221
|
+
cs:description "Create multiple files for different themes." ;
|
|
222
|
+
cs:code """
|
|
208
223
|
warning.svg
|
|
209
224
|
warning-dark.svg
|
|
210
225
|
warning-light.svg
|
|
211
|
-
|
|
212
|
-
|
|
226
|
+
"""
|
|
227
|
+
] .
|
|
213
228
|
|
|
214
229
|
# Icon Type Safety Standard
|
|
215
230
|
cs:IconTypeSafety a cs:CodeStandard ;
|
|
@@ -219,9 +234,10 @@ cs:IconTypeSafety a cs:CodeStandard ;
|
|
|
219
234
|
- Maintained in a type-safe constant array
|
|
220
235
|
- Used to generate the `IconName` type
|
|
221
236
|
- Defined in the icon exporter package""" ;
|
|
222
|
-
cs:
|
|
223
|
-
|
|
224
|
-
|
|
237
|
+
cs:do [
|
|
238
|
+
cs:description "Define a constant array of icon names and derive the `IconName` type from it in the icon exporter package." ;
|
|
239
|
+
cs:language "typescript" ;
|
|
240
|
+
cs:code """
|
|
225
241
|
// In the icon exporter package
|
|
226
242
|
export const ICON_NAMES = [
|
|
227
243
|
"warning",
|
|
@@ -230,65 +246,74 @@ export const ICON_NAMES = [
|
|
|
230
246
|
] as const;
|
|
231
247
|
|
|
232
248
|
export type IconName = typeof ICON_NAMES[number];
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
249
|
+
"""
|
|
250
|
+
] ;
|
|
251
|
+
cs:do [
|
|
252
|
+
cs:description "Import the `IconName` type in consumer code to ensure type safety." ;
|
|
253
|
+
cs:language "typescript" ;
|
|
254
|
+
cs:code """
|
|
237
255
|
// In consumer code
|
|
238
256
|
import type { IconName } from '@canonical/ds-assets';
|
|
239
257
|
|
|
240
258
|
export interface ComponentProps {
|
|
241
259
|
icon: IconName;
|
|
242
260
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
261
|
+
"""
|
|
262
|
+
] ;
|
|
263
|
+
cs:do [
|
|
264
|
+
cs:description "Use `Pick` to restrict choices from `IconName` when necessary." ;
|
|
265
|
+
cs:language "typescript" ;
|
|
266
|
+
cs:code """
|
|
248
267
|
// In consumer code
|
|
249
268
|
import type { IconName } from '@canonical/ds-assets';
|
|
250
269
|
|
|
251
270
|
export interface ComponentProps {
|
|
252
271
|
icon: Pick<IconName, "warning" | "search">;
|
|
253
272
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
cs:
|
|
257
|
-
|
|
258
|
-
|
|
273
|
+
"""
|
|
274
|
+
] ;
|
|
275
|
+
cs:dont [
|
|
276
|
+
cs:description "Define icon name types or values on the consumer side." ;
|
|
277
|
+
cs:language "typescript" ;
|
|
278
|
+
cs:code """
|
|
259
279
|
// Bad: String literal type defined in consumer code
|
|
260
280
|
type ComponentIconName = "warning" | "search";
|
|
261
281
|
|
|
262
282
|
export interface ComponentProps {
|
|
263
283
|
icon: ComponentIconName;
|
|
264
284
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
285
|
+
"""
|
|
286
|
+
] ;
|
|
287
|
+
cs:dont [
|
|
288
|
+
cs:description "Use a generic `string` type for icon names." ;
|
|
289
|
+
cs:language "typescript" ;
|
|
290
|
+
cs:code """
|
|
269
291
|
// Bad: No type safety for icon names
|
|
270
292
|
export interface ComponentProps {
|
|
271
293
|
icon: string;
|
|
272
294
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
295
|
+
"""
|
|
296
|
+
] ;
|
|
297
|
+
cs:dont [
|
|
298
|
+
cs:description "Maintain separate type and value definitions." ;
|
|
299
|
+
cs:language "typescript" ;
|
|
300
|
+
cs:code """
|
|
277
301
|
// Bad: Duplication of icon names
|
|
278
302
|
const ICONS = ["warning", "search"];
|
|
279
303
|
type IconName = "warning" | "search";
|
|
280
|
-
|
|
281
|
-
|
|
304
|
+
"""
|
|
305
|
+
] .
|
|
282
306
|
|
|
283
307
|
# Icon Mask Usage Standard
|
|
284
308
|
cs:IconMaskUsage a cs:CodeStandard ;
|
|
285
309
|
cs:name "icons/svg/mask-usage" ;
|
|
286
310
|
cs:hasCategory cs:IconsCategory ;
|
|
287
311
|
cs:description "Icons may use SVG masks to negate or partially negate paths, enabling advanced visual effects such as cutouts and overlays." ;
|
|
288
|
-
cs:
|
|
289
|
-
|
|
290
|
-
Example
|
|
291
|
-
|
|
312
|
+
cs:do [
|
|
313
|
+
cs:description "Use SVG <mask> elements to create negative space or overlays in icons.
|
|
314
|
+
Example" ;
|
|
315
|
+
cs:language "svg" ;
|
|
316
|
+
cs:code """
|
|
292
317
|
<svg width='16' height='16' xmlns='http://www.w3.org/2000/svg'>
|
|
293
318
|
<defs>
|
|
294
319
|
<mask id="error-mask">
|
|
@@ -300,60 +325,71 @@ Example:
|
|
|
300
325
|
<circle fill='currentColor' cx='8' cy='8' r='7' mask="url(#error-mask)"/>
|
|
301
326
|
</g>
|
|
302
327
|
</svg>
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
328
|
+
"""
|
|
329
|
+
] ;
|
|
330
|
+
cs:do [
|
|
331
|
+
cs:description "Use <rect> with fill="white" to define the mask area, and <path> with fill="black" to subtract shapes.
|
|
332
|
+
Example" ;
|
|
333
|
+
cs:language "svg" ;
|
|
334
|
+
cs:code """
|
|
308
335
|
<mask id="mask">
|
|
309
336
|
<rect width="16" height="16" fill="white"/>
|
|
310
337
|
<path d="M4 4h8v8H4z" fill="black"/>
|
|
311
338
|
</mask>
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
339
|
+
"""
|
|
340
|
+
] ;
|
|
341
|
+
cs:do [
|
|
342
|
+
cs:description "Use opacity on mask paths to achieve partial negation (e.g., faded or semi-transparent cutouts).
|
|
343
|
+
Example" ;
|
|
344
|
+
cs:language "svg" ;
|
|
345
|
+
cs:code """
|
|
317
346
|
<mask id="progress-mask">
|
|
318
347
|
<rect width="16" height="16" fill="white"/>
|
|
319
348
|
<path d='...' fill="black" opacity=".5"/>
|
|
320
349
|
</mask>
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
350
|
+
"""
|
|
351
|
+
] ;
|
|
352
|
+
cs:do [
|
|
353
|
+
cs:description "Reference the mask in the icon's main shape using mask="url(#mask-id)".
|
|
354
|
+
Example" ;
|
|
355
|
+
cs:language "svg" ;
|
|
356
|
+
cs:code """
|
|
326
357
|
<circle fill='currentColor' cx='8' cy='8' r='7' mask="url(#mask-id)"/>
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
cs:
|
|
330
|
-
|
|
331
|
-
Example
|
|
332
|
-
|
|
358
|
+
"""
|
|
359
|
+
] ;
|
|
360
|
+
cs:dont [
|
|
361
|
+
cs:description "Use masks without clear purpose or visual benefit.
|
|
362
|
+
Example" ;
|
|
363
|
+
cs:language "svg" ;
|
|
364
|
+
cs:code """
|
|
333
365
|
<!-- Bad: Mask used but has no effect -->
|
|
334
366
|
<mask id="empty-mask">
|
|
335
367
|
<rect width="16" height="16" fill="white"/>
|
|
336
368
|
</mask>
|
|
337
369
|
<circle fill='currentColor' cx='8' cy='8' r='7' mask="url(#empty-mask)"/>
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
370
|
+
"""
|
|
371
|
+
] ;
|
|
372
|
+
cs:dont [
|
|
373
|
+
cs:description "use non-semantic mask ids or omit mask references in the main shape.
|
|
374
|
+
Example" ;
|
|
375
|
+
cs:language "svg" ;
|
|
376
|
+
cs:code """
|
|
343
377
|
<!-- Bad: Non-semantic mask id -->
|
|
344
378
|
<mask id="123">
|
|
345
379
|
<rect width="16" height="16" fill="white"/>
|
|
346
380
|
<path d="..." fill="black"/>
|
|
347
381
|
</mask>
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
382
|
+
"""
|
|
383
|
+
] ;
|
|
384
|
+
cs:dont [
|
|
385
|
+
cs:description "rely on masks for simple icons that do not require negative space or overlays.
|
|
386
|
+
Example" ;
|
|
387
|
+
cs:language "svg" ;
|
|
388
|
+
cs:code """
|
|
353
389
|
<!-- Bad: Mask used for a simple shape -->
|
|
354
390
|
<mask id="simple-mask">
|
|
355
391
|
<rect width="16" height="16" fill="white"/>
|
|
356
392
|
</mask>
|
|
357
393
|
<rect fill='currentColor' x='2' y='2' width='12' height='12' mask="url(#simple-mask)"/>
|
|
358
|
-
|
|
359
|
-
|
|
394
|
+
"""
|
|
395
|
+
] .
|