@harmonyos-arkts/d2h 0.0.0-stage → 0.1.0

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.
Files changed (74) hide show
  1. package/.claude-plugin/marketplace.json +13 -0
  2. package/.claude-plugin/plugin.json +7 -0
  3. package/README.md +168 -2
  4. package/agents/android-to-hmos-00-orchestrator.md +312 -0
  5. package/agents/d2h.md +168 -0
  6. package/bin/install-opencode.mjs +51 -0
  7. package/install-opencode.sh +160 -0
  8. package/opencode/agents.json +16 -0
  9. package/package.json +16 -4
  10. package/schemas/android-source-manifest.schema.json +25 -0
  11. package/schemas/checkpoint-provenance.schema.json +67 -0
  12. package/schemas/final-acceptance.schema.json +29 -0
  13. package/schemas/managed-evidence-index.schema.json +15 -0
  14. package/schemas/managed-evidence.schema.json +114 -0
  15. package/schemas/migration-config.schema.json +51 -0
  16. package/schemas/migration-report-index.schema.json +39 -0
  17. package/schemas/migration-report-item.schema.json +51 -0
  18. package/schemas/migration-report-summary.schema.json +25 -0
  19. package/schemas/migration-status.schema.json +202 -0
  20. package/schemas/preflight.schema.json +60 -0
  21. package/schemas/source-order-audit.schema.json +16 -0
  22. package/schemas/source-provenance-event.schema.json +19 -0
  23. package/schemas/spec-app-shard.schema.json +45 -0
  24. package/schemas/spec-fact-corrections-shard.schema.json +13 -0
  25. package/schemas/spec-features-shard.schema.json +43 -0
  26. package/schemas/spec-index.schema.json +26 -0
  27. package/schemas/spec-interactions-shard.schema.json +40 -0
  28. package/schemas/spec-page.schema.json +98 -0
  29. package/schemas/spec-pages-index.schema.json +1 -0
  30. package/schemas/spec-unresolved-shard.schema.json +1 -0
  31. package/schemas/task-envelope.schema.json +55 -0
  32. package/schemas/task-plan.schema.json +123 -0
  33. package/skills/android2hmos_resources_convert/SKILL.md +162 -0
  34. package/skills/android2hmos_resources_convert/references/image-conversion-rules.md +230 -0
  35. package/skills/android2hmos_resources_convert/references/svg-fix-patterns.md +175 -0
  36. package/skills/android2hmos_resources_convert/references/xml-drawable-to-svg-rules.md +513 -0
  37. package/skills/appgraph-rule-audit/SKILL.md +58 -0
  38. package/skills/appgraph-rule-audit/references/audit-contract.md +47 -0
  39. package/skills/appgraph-rule-audit/references/recommendation-schema.md +41 -0
  40. package/skills/appgraph-rule-audit/schemas/rule-opportunities.schema.json +125 -0
  41. package/skills/arkts-app-identity/SKILL.md +238 -0
  42. package/skills/arkts-i18n/SKILL.md +496 -0
  43. package/skills/arkts-i18n/evals/evals.json +84 -0
  44. package/skills/arkts-i18n/references/code-examples.md +302 -0
  45. package/skills/arkts-i18n/references/common-pitfalls.md +391 -0
  46. package/skills/arkts-i18n/references/dynamic-language-switch.md +604 -0
  47. package/skills/arkts-i18n/references/hardcoded-string-scanner.md +348 -0
  48. package/skills/arkts-i18n/references/language-codes.md +104 -0
  49. package/skills/arkts-i18n/references/resource-file-structure.md +775 -0
  50. package/skills/arkts-i18n/references/static-vs-dynamic.md +242 -0
  51. package/skills/arkts-i18n/references/v1-compat.md +244 -0
  52. package/skills/arkts-i18n/scripts/audit_i18n_completeness.sh +174 -0
  53. package/skills/arkts-icon-sizing/SKILL.md +211 -0
  54. package/skills/arkts-icon-sizing/scripts/icon_audit.py +131 -0
  55. package/skills/arkts-icon-sizing/scripts/icon_autofix.py +88 -0
  56. package/skills/arkts-icon-sizing/scripts/icon_dims.py +179 -0
  57. package/skills/arkts-icon-sizing/scripts/icon_fix.py +119 -0
  58. package/skills/arkts-mvvm-architecture/SKILL.md +613 -0
  59. package/skills/harmonyos-migration-playbook/SKILL.md +56 -0
  60. package/skills/harmonyos-migration-playbook/agents/openai.yaml +7 -0
  61. package/skills/harmonyos-migration-playbook/references/arkts-compile.md +24 -0
  62. package/skills/harmonyos-migration-playbook/references/harmony-runtime.md +53 -0
  63. package/skills/harmonyos-migration-playbook/references/lesson-lifecycle.md +45 -0
  64. package/skills/harmonyos-migration-playbook/references/protocol-e2e.md +23 -0
  65. package/skills/harmonyos-migration-playbook/references/ui-automation.md +52 -0
  66. package/skills/harmonyos-migration-playbook/references/windows-environment.md +38 -0
  67. package/skills/maintaining-migration-report/SKILL.md +155 -0
  68. package/skills/native-library-substitution/SKILL.md +385 -0
  69. package/skills/native-library-substitution/references/native-library-substitution.json +56906 -0
  70. package/skills/native-library-substitution/references/native-library-substitution.md +163 -0
  71. package/skills/preparing-migration-workspace/SKILL.md +124 -0
  72. package/skills/preparing-migration-workspace/toolchain.json +43 -0
  73. package/skills/reviewing-migration-process/SKILL.md +62 -0
  74. package/src/install-opencode.mjs +108 -0
@@ -0,0 +1,230 @@
1
+ # Android to HarmonyOS Image Resource Conversion Rules
2
+
3
+ Complete rules for converting Android **image resources** — bitmap files, nine-patch images, launcher icons (mipmap), adaptive icons — into HarmonyOS `media/` resources. XML drawables → SVG are covered separately in `xml-drawable-to-svg-rules.md`; this document defines what routes to SVG, plus the qualifier directory algorithm and reference resolution needed by image conversion.
4
+
5
+ ## Table of Contents
6
+ 1. [Target Directory Structure](#target-directory-structure)
7
+ 2. [Bitmap Images → media](#bitmap-images--media)
8
+ 3. [Nine-Patch Images](#nine-patch-images)
9
+ 4. [Mipmap → media](#mipmap--media)
10
+ 5. [Adaptive Icon → Layered-Image JSON](#adaptive-icon--layered-image-json)
11
+ 6. [Identifying XML vs Image Drawables](#identifying-xml-vs-image-drawables)
12
+ 7. [Qualifier Mapping](#qualifier-mapping)
13
+ 8. [Reference Resolution for Images](#reference-resolution-for-images)
14
+ 9. [Special Cases and Edge Cases](#special-cases-and-edge-cases)
15
+
16
+ ---
17
+
18
+ ## Target Directory Structure
19
+
20
+ HarmonyOS image resources live under the `media/` resource group:
21
+
22
+ ```
23
+ entry/src/main/resources/
24
+ ├── base/media/ # default images (no qualifier)
25
+ ├── ldpi/media/ # density-qualified
26
+ ├── dark/media/ # night mode
27
+ ├── zh_CN/media/ # locale-qualified
28
+ └── zh_CN-dark-ldpi/media/ # combined qualifiers
29
+ ```
30
+
31
+ HarmonyOS supported image formats: PNG, JPG, GIF, SVG, WEBP, BMP — all common Android bitmap formats copy directly.
32
+
33
+ ---
34
+
35
+ ## Bitmap Images → media
36
+
37
+ Copy image files from Android `drawable*/` (and `mipmap*/`) to the qualifier-mapped `media/` directory:
38
+
39
+ - `.png`, `.jpg`, `.jpeg`, `.gif`, `.webp`, `.bmp` → direct copy, filename unchanged
40
+ - Resource names are preserved exactly as Android spells them — they are identifiers
41
+ - Do NOT copy XML files in this step — classify them first (see [Identifying XML vs Image Drawables](#identifying-xml-vs-image-drawables))
42
+
43
+ ---
44
+
45
+ ## Nine-Patch Images
46
+
47
+ Android nine-patch files (`.9.png`) have no native HarmonyOS equivalent:
48
+
49
+ 1. **Rename**: `.9.png` → `<name>_9.png`. HarmonyOS forbids `.` in resource filenames except the final extension separator, so `btn_bg.9.png` would be invalid — it becomes `btn_bg_9.png`.
50
+ 2. **Semantics lost**: the 1-px stretch/padding border markers are rendered as ordinary pixels; nine-patch stretching behavior does not carry over. The developer must implement stretching at the ArkUI component level (`.borderRadius` + `.resizable` or 9-slice via `ResizableOptions` where supported).
51
+ 3. Always log nine-patch conversions in the report with the rename and the lost-semantics note.
52
+
53
+ ---
54
+
55
+ ## Mipmap → media
56
+
57
+ Launcher icons from `mipmap*/` directories go to the qualifier-mapped `media/` directory:
58
+
59
+ - PNG/WEBP launcher icons (`ic_launcher.png`, `ic_launcher_round.png`, `ic_launcher_foreground.png`, `ic_launcher_background.png`, …) → direct copy
60
+ - **Adaptive icon XML** (root `<adaptive-icon>`, commonly in `mipmap-anydpi-v26/`) → layered-image JSON (see below)
61
+ - **VectorDrawable XML in mipmap** (root `<vector>`) → convert to SVG exactly like drawable vectors
62
+
63
+ **Common pattern**: `mipmap-anydpi-v26/` is extremely common. Parse the qualifier as `anydpi` (→ `base`) + `v26` (→ strip) → target `base/media/`. Classify the files inside by their XML root element, never by the directory name.
64
+
65
+ ---
66
+
67
+ ## Adaptive Icon → Layered-Image JSON
68
+
69
+ Convert `<adaptive-icon>` XML to `<original_name>_layered_image.json` in the target `media/` directory.
70
+
71
+ **Conversion rules:**
72
+
73
+ 1. Parse the XML and extract the `<background>` and `<foreground>` elements.
74
+ 2. Only `background` and `foreground` are valid keys in the output JSON. Skip `monochrome` (and any other elements) **from the JSON output** — HarmonyOS layered-image has no equivalent. However, the resource *referenced* by `monochrome` is a real asset that must still be converted as a normal image.
75
+ 3. Convert references:
76
+ - `@drawable/xxx` → `$media:xxx`
77
+ - `@mipmap/xxx` → `$media:xxx`
78
+ - `@color/xxx` → **generate a solid-color PNG** (below) — layered-image only accepts `$media:` references, never `$color:`
79
+ 4. **Color background handling (critical)**:
80
+ a. Resolve `@color/xxx` to its hex value from `values*/colors.xml`, following reference chains to a concrete value.
81
+ b. Generate a solid-color PNG filled entirely with that hex (any small size, e.g. 1×1 or 108×108 px).
82
+ c. Save as `<color_name>_bg.png` in `base/media/`.
83
+ d. Reference it as `$media:<color_name>_bg` in the JSON.
84
+ e. If the color cannot be resolved (library resource, source `res/` fallback), log it as an unresolved dependency in the report.
85
+ 5. A layer may carry artwork as a **child element** instead of an `android:drawable` attribute (`<foreground><vector …/></foreground>`). Convert that inline drawable to its own media file named `<icon_name>_<layer>.svg` and reference it as `$media:<icon_name>_<layer>`. Never emit the JSON with the layer simply absent. A `<bitmap android:src="@drawable/x"/>` wrapper is just a reference — point at `$media:x`.
86
+
87
+ **Example:**
88
+
89
+ ```xml
90
+ <!-- mipmap-anydpi-v26/ic_launcher.xml -->
91
+ <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
92
+ <background android:drawable="@color/md_orange_700" />
93
+ <foreground android:drawable="@mipmap/ic_launcher_foreground" />
94
+ <monochrome android:drawable="@drawable/ic_launcher_monochrome" />
95
+ </adaptive-icon>
96
+ ```
97
+
98
+ Suppose `@color/md_orange_700` resolves to `#F57C00`. Generate `base/media/md_orange_700_bg.png` (solid `#F57C00`), then write `base/media/ic_launcher_layered_image.json`:
99
+
100
+ ```json
101
+ {
102
+ "layered-image": {
103
+ "background": "$media:md_orange_700_bg",
104
+ "foreground": "$media:ic_launcher_foreground"
105
+ }
106
+ }
107
+ ```
108
+
109
+ `@drawable/ic_launcher_monochrome` is additionally converted as a normal drawable (SVG or bitmap per its type) even though it never appears in the JSON.
110
+
111
+ ---
112
+
113
+ ## Identifying XML vs Image Drawables
114
+
115
+ In `drawable*/` and `mipmap*/` directories, classify every `.xml` file by its **root element** (never by filename or directory):
116
+
117
+ | Root element | Type | Route |
118
+ |---|---|---|
119
+ | `<vector>` | VectorDrawable | → SVG (`xml-drawable-to-svg-rules.md`) |
120
+ | `<shape>` | ShapeDrawable | → SVG |
121
+ | `<layer-list>` | LayerListDrawable | → SVG |
122
+ | `<selector>` | StateListDrawable | Extract default state → SVG; log dropped state variants |
123
+ | `<adaptive-icon>` | Adaptive icon | → layered-image JSON (above) |
124
+ | `<ripple>`, `<animated-vector>`, `<animated-selector>`, `<transition>`, `<level-list>`, `<scale>`, `<rotate>`, `<inset>` | Runtime behavior | Unmappable — log. Exception: `<inset>`/`<rotate>` with fixed values may be applied statically and the inner drawable converted; log either way |
125
+
126
+ When several drawable directories provide the same resource name (`drawable/x.xml` and `drawable-anydpi/x.xml` both landing in `base/media/`), the source with **fewer qualifiers** wins; record the loser in the report as a collision — never silently drop it.
127
+
128
+ ---
129
+
130
+ ## Qualifier Mapping
131
+
132
+ Android uses qualifier suffixes on resource directories; HarmonyOS uses a different qualifier naming scheme. Images are always placed under the `media/` group of the mapped qualifier directory.
133
+
134
+ ### Density Qualifiers
135
+
136
+ The DPI range labels differ between platforms:
137
+
138
+ | Android Qualifier | Approx DPI | HarmonyOS Qualifier | HarmonyOS DPI Range |
139
+ |---|---|---|---|
140
+ | `ldpi` | 120 | `sdpi` | (0, 120] |
141
+ | `mdpi` | 160 | `mdpi` | (120, 160] |
142
+ | `tvdpi` | 213 | `ldpi` | (160, 240] |
143
+ | `hdpi` | 240 | `ldpi` | (160, 240] |
144
+ | `xhdpi` | 320 | `xldpi` | (240, 320] |
145
+ | `xxhdpi` | 480 | `xxldpi` | (320, 480] |
146
+ | `xxxhdpi` | 640 | `xxxldpi` | (480, 640] |
147
+ | `nodpi` | N/A | `base` | No density qualifier |
148
+ | `anydpi` | N/A | `base` | No density qualifier |
149
+
150
+ Note the deliberate mismatch: Android `hdpi` (~240) maps to HarmonyOS **`ldpi`** because HarmonyOS labels the (160, 240] range `ldpi`.
151
+
152
+ ### Language/Region Qualifiers
153
+
154
+ | Android | HarmonyOS |
155
+ |---|---|
156
+ | `drawable-ar` | `ar/media/` |
157
+ | `drawable-zh` | `zh/media/` |
158
+ | `drawable-zh-rCN` | `zh_CN/media/` |
159
+ | `drawable-zh-rTW` | `zh_TW/media/` |
160
+ | `drawable-en-rUS` | `en_US/media/` |
161
+ | `drawable-fr-rFR` | `fr_FR/media/` |
162
+
163
+ Parsing: language = 2–3 lowercase letters (ISO 639); region = `r` + 2 uppercase letters, strip the `r`; script = `b+sr+Latn` → `sr_Latn`. Language and region join with `_`.
164
+
165
+ ### Orientation Qualifiers
166
+
167
+ | Android | HarmonyOS |
168
+ |---|---|
169
+ | `land` | `horizontal` |
170
+ | `port` | `vertical` |
171
+
172
+ ### Night Mode Qualifiers
173
+
174
+ | Android | HarmonyOS |
175
+ |---|---|
176
+ | `night` | `dark` |
177
+ | `notnight` | `light` |
178
+
179
+ ### Combining Multiple Qualifiers
180
+
181
+ **Parsing algorithm for any Android drawable/mipmap directory name:**
182
+
183
+ 1. Split the directory name by `-`; the first segment is the resource type
184
+ 2. Parse remaining qualifiers left-to-right: language/region, density, orientation, night mode, API level (`v<N>`), screen-size constraints
185
+ 3. Map each recognized qualifier to its HarmonyOS equivalent
186
+ 4. **Strip silently** (do not reject): API level `v<N>`, layout direction (`ldrtl`/`ldltr`), screen shape (`round`/`notround`), HDR (`highdr`/`lowdr`), aspect (`long`/`notlong`)
187
+ 5. **Skip the entire directory** (mark all resources unmapped) when screen-size qualifiers remain: `sw<N>dp`, `w<N>dp`, `h<N>dp`, `small`, `normal`, `large`, `xlarge` — HarmonyOS has no equivalent; do NOT convert them to `base/`
188
+ 6. If no qualifiers remain after stripping, use `base/`
189
+ 7. Reassemble in HarmonyOS order: MCC_MNC-language_script_country/region-orientation-device-colormode-density
190
+
191
+ **Examples:**
192
+
193
+ | Android Directory | Qualifiers | HarmonyOS Target |
194
+ |---|---|---|
195
+ | `drawable-hdpi` | hdpi→ldpi | `ldpi/media/` |
196
+ | `mipmap-anydpi-v26` | anydpi→base, v26 stripped | `base/media/` |
197
+ | `drawable-zh-rCN-night-hdpi` | zh_CN, dark, ldpi | `zh_CN-dark-ldpi/media/` |
198
+ | `drawable-night-v21` | dark, v21 stripped | `dark/media/` |
199
+ | `drawable-hdpi-v4` | ldpi, v4 stripped | `ldpi/media/` |
200
+ | `drawable-sw600dp` | **skip — unmapped** | N/A |
201
+
202
+ Never reject a directory just because one qualifier is unsupported — strip it and continue.
203
+
204
+ ---
205
+
206
+ ## Reference Resolution for Images
207
+
208
+ Image conversion encounters value references in three places. Build a lookup table from `values*/colors.xml` and `values*/dimens.xml` before converting drawables.
209
+
210
+ **In converted SVG files:**
211
+ - `@color/name` used as `fillColor`/`strokeColor` → resolve to the hex value during SVG generation (follow chains; fallback `#000000` + log)
212
+ - `@dimen/name` used as a dimension → resolve to the numeric value (fallback: sensible default + log)
213
+ - `@android:color/*` system colors → map known ones (`white`→`#FFFFFF`, `black`→`#000000`, `transparent`→`#00000000`); unknown → `#000000` + log
214
+
215
+ **In layered-image JSON files:**
216
+ - `@drawable/xxx` / `@mipmap/xxx` → keep as `$media:xxx` (file references — the files themselves are converted)
217
+ - `@color/xxx` → resolve to hex and generate the solid-color PNG (see adaptive icon rules)
218
+
219
+ **Reference chain depth**: follow at most 5 levels (`A → B → C → concrete value`). Unresolved references are expected — library resources are absent from source `res/`; record them in the report with likely library provenance (e.g. `@drawable/abc_*` → AndroidX AppCompat, `@drawable/mtrl_*`/`design_*` → Material Components) so the developer can supply them manually.
220
+
221
+ ---
222
+
223
+ ## Special Cases and Edge Cases
224
+
225
+ - **Same name in multiple density directories** (`drawable-hdpi/x.png`, `drawable-xxhdpi/x.png`) → each copies to its own qualifier directory (`ldpi/media/x.png`, `xxldpi/media/x.png`) — no conflict.
226
+ - **Same name from qualified and unqualified dirs** (`drawable/x.png` and `drawable-anydpi/x.xml` both → `base/media/`) → the source with fewer qualifiers wins; the loser is recorded as a collision.
227
+ - **Existing files in the HarmonyOS project**: merge, do not overwrite — an existing media file with the same name keeps its position; the attempted copy is logged as a collision for the developer to review.
228
+ - **Empty/corrupt image files**: skip and log; do not copy zero-byte files into `media/`.
229
+ - **Filenames illegal in HarmonyOS** (`.` inside the name, leading digits are fine but uppercase is kept as-is): only the `.9.png` rename is automatic; any other illegal name is copied with a logged `unmappable` entry explaining the manual rename needed.
230
+ - **Large image sets**: first inventory, then group by (classification × qualifier), then convert group by group, then verify — never convert while still scanning.
@@ -0,0 +1,175 @@
1
+ # SVG Display Issue Fix Patterns
2
+
3
+ Common display abnormalities and fixes when migrating SVG/Vector Drawables from Android projects or manually creating SVGs.
4
+
5
+ ---
6
+
7
+ ## Issue 1: viewBox Does Not Match Path Coordinates
8
+
9
+ ### Symptoms
10
+ Icon does not display at all, or only shows a blank area.
11
+
12
+ ### Cause
13
+ The coordinate range defined by `viewBox` does not match the coordinate data in the `d` attribute of `<path>`.
14
+
15
+ For example: path data is based on a 512×512 coordinate system (coordinate values range from 85 to 448), but `viewBox` is set to `0 0 24 24`, causing all paths to exceed the visible area.
16
+
17
+ ### Fix
18
+
19
+ **Option A: Adjust viewBox to Match Path Coordinates (Recommended for Simple Cases)**
20
+ ```xml
21
+ <!-- Wrong: viewBox too small -->
22
+ <svg viewBox="0 0 24 24">
23
+ <path d="M101.51,411.23 Q85.21,395.05 ..." /> <!-- coordinates far exceed 24 -->
24
+ </svg>
25
+
26
+ <!-- Fixed: viewBox matches coordinate system, use width/height to control render size -->
27
+ <svg width="24" height="24" viewBox="0 0 512 512">
28
+ <path d="M101.51,411.23 Q85.21,395.05 ..." />
29
+ </svg>
30
+ ```
31
+
32
+ > Note: `viewBox` defines the path coordinate space, while `width`/`height` define the render size. They are independent.
33
+
34
+ **Option B: Scale Path Coordinates Proportionally (Recommended for Standardization)**
35
+
36
+ Scale all values in the path proportionally to the target coordinate system. For example, scaling from 512 to 24:
37
+
38
+ ```python
39
+ import re
40
+
41
+ scale = 24.0 / 512.0 # 0.046875
42
+
43
+ def scale_number(match):
44
+ num = float(match.group(0))
45
+ scaled = round(num * scale, 2)
46
+ return '{:.2f}'.format(scaled).rstrip('0').rstrip('.')
47
+
48
+ path_data = "M101.51,411.23Q85.21,395.05 85.22,371.75..."
49
+ result = re.sub(r'[0-9]+\.?[0-9]*', scale_number, path_data)
50
+ ```
51
+
52
+ After scaling, you can use the standard `viewBox="0 0 24 24"`, consistent with other Material Design icons.
53
+
54
+ ---
55
+
56
+ ## Issue 2: Android Vector Drawable Syntax Remnants
57
+
58
+ ### Symptoms
59
+ The SVG file contains Android-specific XML attributes that standard SVG renderers cannot parse.
60
+
61
+ ### Common Remnant Syntax
62
+
63
+ | Android Syntax | Standard SVG Equivalent |
64
+ |---------------|----------------------|
65
+ | `<group android:translateX="24" android:rotation="90">` | `<g transform="translate(24,0) rotate(90)">` |
66
+ | `android:fillColor="#FF000000"` | `fill="#000000"` |
67
+ | `android:pathData="..."` | `d="..."` |
68
+ | `android:viewportWidth="24"` | Part of `viewBox="0 0 24 24"` |
69
+ | `<vector>` root element | `<svg>` root element |
70
+ | `<clip-path android:pathData="..."/>` | `<defs><clipPath id="..."><path d="..."/></clipPath></defs>` |
71
+
72
+ ### Fix
73
+
74
+ Fully convert Android `<group>` transform attributes to SVG `transform`:
75
+
76
+ ```xml
77
+ <!-- Android Vector Drawable -->
78
+ <group
79
+ android:translateX="24"
80
+ android:translateY="0"
81
+ android:rotation="90"
82
+ android:pivotX="12"
83
+ android:pivotY="12"
84
+ android:scaleX="1.5"
85
+ android:scaleY="1.5">
86
+ <path android:pathData="M18,12l4,-4..." android:fillColor="#FFFFFF"/>
87
+ </group>
88
+
89
+ <!-- Converted to Standard SVG -->
90
+ <g transform="translate(24,0) rotate(90,12,12) scale(1.5,1.5)">
91
+ <path d="M18,12l4,-4..." fill="#FFFFFF"/>
92
+ </g>
93
+ ```
94
+
95
+ Transform order: `translate` → `rotate` (with pivot point) → `scale`
96
+
97
+ ---
98
+
99
+ ## Issue 3: Fill Color is White Causing Invisible Icon
100
+
101
+ ### Symptoms
102
+ SVG file exists and path is correct, but icon does not display (invisible on white background).
103
+
104
+ ### Cause
105
+ The original Android drawable's `fillColor` is `#FFFFFF` (white), which is invisible on a white background. Common for icons designed to appear on dark ActionBars.
106
+
107
+ ### Fix
108
+
109
+ Change `fill="#FFFFFF"` to `fill="#000000"` (black), or choose a suitable color based on UI needs:
110
+
111
+ ```xml
112
+ <!-- Before fix: white fill invisible on white background -->
113
+ <path fill="#FFFFFF" d="M18,12l4,-4..."/>
114
+
115
+ <!-- After fix -->
116
+ <path fill="#000000" d="M18,12l4,-4..."/>
117
+ ```
118
+
119
+ ---
120
+
121
+ ## Issue 4: Google Material Symbols Coordinate System
122
+
123
+ ### Description
124
+ Material Symbols SVGs downloaded from Google Fonts use a special coordinate system:
125
+
126
+ ```xml
127
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#000000">
128
+ <path d="M200-120q-33 0-56.5-23.5T120-200v-560..."/>
129
+ </svg>
130
+ ```
131
+
132
+ - `viewBox="0 -960 960 960"` — Y-axis starts at -960 (positive is upward)
133
+ - Many negative values in path coordinates are normal
134
+ - This format renders correctly in HarmonyOS, **no additional conversion needed**
135
+
136
+ ---
137
+
138
+ ## Issue 5: HarmonyOS media Directory Does Not Support Subdirectories
139
+
140
+ ### Symptoms
141
+ Compilation error: `CompileResource` error "invalid path, not a file"
142
+
143
+ ### Cause
144
+ Created subdirectories under `resources/base/media/` (e.g., `media/icons/`, `media/md/`).
145
+
146
+ ### Fix
147
+
148
+ Move all files from subdirectories to the `media/` root directory, using file name prefixes to distinguish sources:
149
+
150
+ ```bash
151
+ # Wrong: using subdirectories
152
+ media/
153
+ md/
154
+ share.svg
155
+ home.svg
156
+
157
+ # Correct: flat storage with prefixes
158
+ media/
159
+ md_share.svg
160
+ md_home.svg
161
+ ic_custom_icon.svg
162
+ ```
163
+
164
+ ---
165
+
166
+ ## Quick Diagnosis Checklist
167
+
168
+ When SVG icon does not display, check in order:
169
+
170
+ 1. **File exists?** — Confirm the file exists under `entry/src/main/resources/base/media/`
171
+ 2. **Reference correct?** — `$r('app.media.filename_without_extension')`, filename is case-sensitive
172
+ 3. **viewBox matches?** — `viewBox` coordinate range must cover path data coordinate values
173
+ 4. **Syntax standard?** — No `android:` namespace attributes, root element is `<svg>` not `<vector>`
174
+ 5. **Fill color visible?** — `fill` is not `#FFFFFF` (invisible on white background)
175
+ 6. **No subdirectories?** — SVG files are directly under `media/` root directory