autokap 1.0.7 → 1.0.8

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 (278) hide show
  1. package/assets/cursors/macos.svg +4 -0
  2. package/assets/cursors/windows.svg +15 -0
  3. package/assets/skill/OPCODE-REFERENCE.md +607 -0
  4. package/assets/skill/README.md +39 -0
  5. package/assets/skill/SKILL.md +453 -468
  6. package/assets/skill/STUDIO-SKILL.md +476 -0
  7. package/assets/skill/references/examples.md +104 -0
  8. package/assets/skill/references/interactive-demo.md +225 -0
  9. package/assets/skill/references/mock-data.md +178 -0
  10. package/dist/action-verifier.d.ts +29 -0
  11. package/dist/action-verifier.js +133 -0
  12. package/dist/agent-action-recovery.d.ts +45 -0
  13. package/dist/agent-action-recovery.js +370 -0
  14. package/dist/agent-message-utils.d.ts +21 -0
  15. package/dist/agent-message-utils.js +77 -0
  16. package/dist/agent-url-utils.d.ts +30 -0
  17. package/dist/agent-url-utils.js +138 -0
  18. package/dist/agent.d.ts +92 -8
  19. package/dist/agent.js +2936 -781
  20. package/dist/ak-tree.d.ts +39 -0
  21. package/dist/ak-tree.js +368 -0
  22. package/dist/alt-text.d.ts +26 -0
  23. package/dist/alt-text.js +55 -0
  24. package/dist/auth-capture.d.ts +17 -0
  25. package/dist/auth-capture.js +164 -0
  26. package/dist/benchmark.d.ts +59 -0
  27. package/dist/benchmark.js +135 -0
  28. package/dist/browser-bar.d.ts +14 -6
  29. package/dist/browser-bar.js +145 -8
  30. package/dist/browser-pool.d.ts +7 -0
  31. package/dist/browser-pool.js +15 -5
  32. package/dist/browser-utils.d.ts +31 -0
  33. package/dist/browser-utils.js +97 -0
  34. package/dist/browser.d.ts +51 -1
  35. package/dist/browser.js +1481 -31
  36. package/dist/capture-alt-text.js +2 -1
  37. package/dist/capture-language-preflight.js +14 -0
  38. package/dist/capture-llm-page-identity.js +22 -10
  39. package/dist/capture-page-identity.d.ts +5 -7
  40. package/dist/capture-page-identity.js +211 -78
  41. package/dist/capture-preset-credentials.d.ts +50 -0
  42. package/dist/capture-preset-credentials.js +127 -0
  43. package/dist/capture-request-plan.d.ts +2 -2
  44. package/dist/capture-request-plan.js +64 -16
  45. package/dist/capture-run-optimizer.js +48 -33
  46. package/dist/capture-selector-memory.d.ts +5 -0
  47. package/dist/capture-selector-memory.js +18 -0
  48. package/dist/capture-strategy.d.ts +36 -0
  49. package/dist/capture-strategy.js +95 -0
  50. package/dist/capture-studio-sync.d.ts +1 -0
  51. package/dist/capture-studio-sync.js +9 -3
  52. package/dist/capture-surface-contract.d.ts +36 -0
  53. package/dist/capture-surface-contract.js +299 -0
  54. package/dist/capture-transition-engine.d.ts +28 -0
  55. package/dist/capture-transition-engine.js +292 -0
  56. package/dist/capture-variant-state.d.ts +2 -0
  57. package/dist/capture-variant-state.js +26 -0
  58. package/dist/capture-verification.d.ts +35 -0
  59. package/dist/capture-verification.js +95 -0
  60. package/dist/capture-viewport-lock.d.ts +48 -0
  61. package/dist/capture-viewport-lock.js +74 -0
  62. package/dist/circuit-breaker.d.ts +42 -0
  63. package/dist/circuit-breaker.js +119 -0
  64. package/dist/cli-config.d.ts +8 -1
  65. package/dist/cli-config.js +62 -6
  66. package/dist/cli-contract.d.ts +15 -0
  67. package/dist/cli-contract.js +167 -0
  68. package/dist/cli-runner-local.d.ts +12 -0
  69. package/dist/cli-runner-local.js +102 -0
  70. package/dist/cli-runner.d.ts +34 -0
  71. package/dist/cli-runner.js +433 -0
  72. package/dist/cli-utils.d.ts +0 -1
  73. package/dist/cli-utils.js +2 -5
  74. package/dist/cli.js +1005 -267
  75. package/dist/clip-orchestrator.js +9 -2
  76. package/dist/clip-postprocess.js +25 -16
  77. package/dist/cookie-dismiss.d.ts +2 -0
  78. package/dist/cookie-dismiss.js +48 -13
  79. package/dist/cost-logging.d.ts +8 -0
  80. package/dist/cost-logging.js +160 -46
  81. package/dist/cost-resolution-monitor.d.ts +16 -0
  82. package/dist/cost-resolution-monitor.js +34 -0
  83. package/dist/credential-templates.js +2 -2
  84. package/dist/cursor-overlay-script.d.ts +6 -0
  85. package/dist/cursor-overlay-script.js +169 -0
  86. package/dist/dom-css-purger.d.ts +65 -0
  87. package/dist/dom-css-purger.js +333 -0
  88. package/dist/dom-font-inliner.d.ts +45 -0
  89. package/dist/dom-font-inliner.js +148 -0
  90. package/dist/dom-patch-resolver.d.ts +52 -0
  91. package/dist/dom-patch-resolver.js +242 -0
  92. package/dist/dom-serializer.d.ts +82 -0
  93. package/dist/dom-serializer.js +378 -0
  94. package/dist/element-capture.d.ts +1 -41
  95. package/dist/element-capture.js +202 -446
  96. package/dist/env-validation.d.ts +5 -0
  97. package/dist/env-validation.js +29 -0
  98. package/dist/execution-schema.d.ts +4423 -0
  99. package/dist/execution-schema.js +507 -0
  100. package/dist/execution-types.d.ts +886 -0
  101. package/dist/execution-types.js +65 -0
  102. package/dist/fonts-loader.d.ts +14 -0
  103. package/dist/fonts-loader.js +55 -0
  104. package/dist/hybrid-navigator.js +12 -12
  105. package/dist/index.d.ts +9 -6
  106. package/dist/index.js +10 -4
  107. package/dist/legacy/agent-action-recovery.d.ts +45 -0
  108. package/dist/legacy/agent-action-recovery.js +370 -0
  109. package/dist/legacy/agent-message-utils.d.ts +21 -0
  110. package/dist/legacy/agent-message-utils.js +77 -0
  111. package/dist/legacy/agent-url-utils.d.ts +30 -0
  112. package/dist/legacy/agent-url-utils.js +138 -0
  113. package/dist/legacy/agent.d.ts +226 -0
  114. package/dist/legacy/agent.js +6666 -0
  115. package/dist/legacy/clip-orchestrator.d.ts +148 -0
  116. package/dist/legacy/clip-orchestrator.js +957 -0
  117. package/dist/legacy/credential-templates.d.ts +5 -0
  118. package/dist/legacy/credential-templates.js +60 -0
  119. package/dist/legacy/hybrid-navigator.d.ts +138 -0
  120. package/dist/legacy/hybrid-navigator.js +468 -0
  121. package/dist/legacy/llm-usage.d.ts +17 -0
  122. package/dist/legacy/llm-usage.js +45 -0
  123. package/dist/legacy/prompt-cache.d.ts +10 -0
  124. package/dist/legacy/prompt-cache.js +24 -0
  125. package/dist/legacy/prompts.d.ts +175 -0
  126. package/dist/legacy/prompts.js +1038 -0
  127. package/dist/legacy/tools.d.ts +4 -0
  128. package/dist/legacy/tools.js +216 -0
  129. package/dist/legacy/video-agent.d.ts +143 -0
  130. package/dist/legacy/video-agent.js +4788 -0
  131. package/dist/legacy/video-observation.d.ts +36 -0
  132. package/dist/legacy/video-observation.js +192 -0
  133. package/dist/legacy/video-planner.d.ts +12 -0
  134. package/dist/legacy/video-planner.js +501 -0
  135. package/dist/legacy/video-prompts.d.ts +37 -0
  136. package/dist/legacy/video-prompts.js +569 -0
  137. package/dist/legacy/video-tools.d.ts +3 -0
  138. package/dist/legacy/video-tools.js +59 -0
  139. package/dist/legacy/video-variant-state.d.ts +29 -0
  140. package/dist/legacy/video-variant-state.js +80 -0
  141. package/dist/legacy/vision-model.d.ts +17 -0
  142. package/dist/legacy/vision-model.js +74 -0
  143. package/dist/llm-healer.d.ts +63 -0
  144. package/dist/llm-healer.js +166 -0
  145. package/dist/llm-provider.d.ts +29 -0
  146. package/dist/llm-provider.js +80 -0
  147. package/dist/logger.d.ts +6 -2
  148. package/dist/logger.js +15 -1
  149. package/dist/mockup-html.js +35 -25
  150. package/dist/mockup.d.ts +95 -2
  151. package/dist/mockup.js +427 -166
  152. package/dist/mouse-animation.d.ts +2 -2
  153. package/dist/mouse-animation.js +34 -20
  154. package/dist/opcode-actions.d.ts +42 -0
  155. package/dist/opcode-actions.js +511 -0
  156. package/dist/opcode-runner.d.ts +51 -0
  157. package/dist/opcode-runner.js +770 -0
  158. package/dist/openrouter-client.d.ts +40 -0
  159. package/dist/openrouter-client.js +16 -0
  160. package/dist/overlay-engine.d.ts +24 -0
  161. package/dist/overlay-engine.js +176 -0
  162. package/dist/postcondition.d.ts +16 -0
  163. package/dist/postcondition.js +269 -0
  164. package/dist/program-patcher.d.ts +25 -0
  165. package/dist/program-patcher.js +44 -0
  166. package/dist/prompts.d.ts +13 -5
  167. package/dist/prompts.js +224 -351
  168. package/dist/provider-config.d.ts +12 -0
  169. package/dist/provider-config.js +15 -0
  170. package/dist/recovery-chain.d.ts +37 -0
  171. package/dist/recovery-chain.js +350 -0
  172. package/dist/remote-browser.d.ts +28 -4
  173. package/dist/remote-browser.js +60 -5
  174. package/dist/safari-browser-bar.d.ts +15 -0
  175. package/dist/safari-browser-bar.js +95 -0
  176. package/dist/safari-toolbar-asset.d.ts +15 -0
  177. package/dist/safari-toolbar-asset.js +12 -0
  178. package/dist/security.d.ts +2 -1
  179. package/dist/security.js +49 -10
  180. package/dist/selector-resolver.d.ts +34 -0
  181. package/dist/selector-resolver.js +181 -0
  182. package/dist/semantic-resolver.d.ts +35 -0
  183. package/dist/semantic-resolver.js +161 -0
  184. package/dist/server-capture-runtime.d.ts +5 -3
  185. package/dist/server-capture-runtime.js +42 -95
  186. package/dist/server-credit-usage.d.ts +2 -2
  187. package/dist/server-project-webhooks.d.ts +15 -1
  188. package/dist/server-project-webhooks.js +34 -8
  189. package/dist/server-screenshot-watermark.js +27 -5
  190. package/dist/session-profile.js +164 -1
  191. package/dist/sf-pro-symbols.d.ts +1 -0
  192. package/dist/sf-pro-symbols.js +55 -0
  193. package/dist/skill-packaging.d.ts +28 -0
  194. package/dist/skill-packaging.js +169 -0
  195. package/dist/smart-wait.d.ts +27 -0
  196. package/dist/smart-wait.js +81 -0
  197. package/dist/status-bar-render.d.ts +20 -0
  198. package/dist/status-bar-render.js +410 -0
  199. package/dist/status-bar.d.ts +9 -0
  200. package/dist/status-bar.js +298 -14
  201. package/dist/svg-browser-bar.d.ts +33 -0
  202. package/dist/svg-browser-bar.js +206 -0
  203. package/dist/svg-status-bar.d.ts +36 -0
  204. package/dist/svg-status-bar.js +597 -0
  205. package/dist/svg-text.d.ts +61 -0
  206. package/dist/svg-text.js +118 -0
  207. package/dist/tools.js +89 -451
  208. package/dist/types.d.ts +240 -5
  209. package/dist/types.js +23 -1
  210. package/dist/v2/action-verifier.d.ts +29 -0
  211. package/dist/v2/action-verifier.js +133 -0
  212. package/dist/v2/alt-text.d.ts +26 -0
  213. package/dist/v2/alt-text.js +55 -0
  214. package/dist/v2/benchmark.d.ts +59 -0
  215. package/dist/v2/benchmark.js +135 -0
  216. package/dist/v2/capture-strategy.d.ts +30 -0
  217. package/dist/v2/capture-strategy.js +67 -0
  218. package/dist/v2/capture-verification.d.ts +35 -0
  219. package/dist/v2/capture-verification.js +95 -0
  220. package/dist/v2/circuit-breaker.d.ts +42 -0
  221. package/dist/v2/circuit-breaker.js +119 -0
  222. package/dist/v2/cli-runner-local.d.ts +11 -0
  223. package/dist/v2/cli-runner-local.js +91 -0
  224. package/dist/v2/cli-runner.d.ts +34 -0
  225. package/dist/v2/cli-runner.js +300 -0
  226. package/dist/v2/compiler-prompts.d.ts +27 -0
  227. package/dist/v2/compiler-prompts.js +123 -0
  228. package/dist/v2/compiler.d.ts +37 -0
  229. package/dist/v2/compiler.js +147 -0
  230. package/dist/v2/explorer.d.ts +41 -0
  231. package/dist/v2/explorer.js +56 -0
  232. package/dist/v2/index.d.ts +37 -0
  233. package/dist/v2/index.js +31 -0
  234. package/dist/v2/llm-healer.d.ts +62 -0
  235. package/dist/v2/llm-healer.js +166 -0
  236. package/dist/v2/llm-provider.d.ts +29 -0
  237. package/dist/v2/llm-provider.js +80 -0
  238. package/dist/v2/opcode-runner.d.ts +47 -0
  239. package/dist/v2/opcode-runner.js +634 -0
  240. package/dist/v2/overlay-engine.d.ts +24 -0
  241. package/dist/v2/overlay-engine.js +150 -0
  242. package/dist/v2/postcondition.d.ts +16 -0
  243. package/dist/v2/postcondition.js +249 -0
  244. package/dist/v2/program-patcher.d.ts +25 -0
  245. package/dist/v2/program-patcher.js +44 -0
  246. package/dist/v2/recovery-chain.d.ts +30 -0
  247. package/dist/v2/recovery-chain.js +368 -0
  248. package/dist/v2/schema.d.ts +2580 -0
  249. package/dist/v2/schema.js +295 -0
  250. package/dist/v2/selector-resolver.d.ts +34 -0
  251. package/dist/v2/selector-resolver.js +181 -0
  252. package/dist/v2/semantic-resolver.d.ts +35 -0
  253. package/dist/v2/semantic-resolver.js +161 -0
  254. package/dist/v2/smart-wait.d.ts +27 -0
  255. package/dist/v2/smart-wait.js +81 -0
  256. package/dist/v2/types.d.ts +444 -0
  257. package/dist/v2/types.js +19 -0
  258. package/dist/v2/web-playwright-local.d.ts +69 -0
  259. package/dist/v2/web-playwright-local.js +392 -0
  260. package/dist/version.d.ts +1 -0
  261. package/dist/version.js +5 -0
  262. package/dist/video-agent.js +18 -13
  263. package/dist/video-planner.js +2 -1
  264. package/dist/video-prompts.js +3 -3
  265. package/dist/web-playwright-local.d.ts +126 -0
  266. package/dist/web-playwright-local.js +819 -0
  267. package/dist/ws-auth.js +4 -1
  268. package/dist/ws-broadcast.d.ts +34 -0
  269. package/dist/ws-broadcast.js +85 -0
  270. package/dist/ws-connection-limits.d.ts +12 -0
  271. package/dist/ws-connection-limits.js +44 -0
  272. package/dist/ws-handler-utils.d.ts +32 -0
  273. package/dist/ws-handler-utils.js +139 -0
  274. package/dist/ws-handler.js +294 -164
  275. package/dist/ws-metrics-server.d.ts +9 -0
  276. package/dist/ws-metrics-server.js +31 -0
  277. package/dist/ws-server.js +41 -1
  278. package/package.json +51 -34
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Native SVG status bar generator (no Playwright).
3
+ *
4
+ * Mirrors `generateStatusBarHtml` in src/status-bar.ts pixel for pixel, but
5
+ * emits a self-contained SVG document instead of HTML/CSS. The SVG is meant
6
+ * to be rasterized via Sharp inside applyDeviceFrameSharp.
7
+ *
8
+ * Layout strategy:
9
+ * - Each status bar variant translates its flexbox layout into explicit
10
+ * positioning math: text widths come from svg-text.measureText, icons
11
+ * have known viewBox dimensions.
12
+ * - Icons are inlined as `<path>` elements re-using the exact same path
13
+ * data Apple ships in iOS status bar SVG assets (signal/wifi/battery).
14
+ * - Text is converted to native `<path>` via svg-text.layoutText so the
15
+ * SVG is fully self-contained and font-installation independent.
16
+ */
17
+ import type { StatusBarConfig, StatusBarLayout, StatusBarDeviceType } from './status-bar.js';
18
+ export interface StatusBarSvgOptions {
19
+ config: StatusBarConfig;
20
+ /** Logical width (CSS pixels). Matches the screen rect's logical width. */
21
+ width: number;
22
+ /** Logical height (CSS pixels). Matches the status-bar logical height. */
23
+ height: number;
24
+ scale: number;
25
+ deviceType: StatusBarDeviceType;
26
+ layout?: StatusBarLayout;
27
+ /**
28
+ * Output pixel scale. The SVG's `viewBox` is set to logical coordinates so
29
+ * the layout math is identical to the Playwright HTML pipeline; the SVG's
30
+ * `width`/`height` attributes are set to logical * pixelScale so librsvg
31
+ * rasterizes at the requested output resolution. Defaults to 1.
32
+ */
33
+ pixelScale?: number;
34
+ }
35
+ /** Generate a complete `<svg>` document for the requested status bar variant. */
36
+ export declare function generateStatusBarSvg(opts: StatusBarSvgOptions): string;
@@ -0,0 +1,597 @@
1
+ /**
2
+ * Native SVG status bar generator (no Playwright).
3
+ *
4
+ * Mirrors `generateStatusBarHtml` in src/status-bar.ts pixel for pixel, but
5
+ * emits a self-contained SVG document instead of HTML/CSS. The SVG is meant
6
+ * to be rasterized via Sharp inside applyDeviceFrameSharp.
7
+ *
8
+ * Layout strategy:
9
+ * - Each status bar variant translates its flexbox layout into explicit
10
+ * positioning math: text widths come from svg-text.measureText, icons
11
+ * have known viewBox dimensions.
12
+ * - Icons are inlined as `<path>` elements re-using the exact same path
13
+ * data Apple ships in iOS status bar SVG assets (signal/wifi/battery).
14
+ * - Text is converted to native `<path>` via svg-text.layoutText so the
15
+ * SVG is fully self-contained and font-installation independent.
16
+ */
17
+ import { layoutText, measureText, baselineForCenteredText, } from './svg-text.js';
18
+ const DEFAULT_CONFIG = {
19
+ time: '9:41',
20
+ date: 'Sat 1 Mar',
21
+ menuBarApp: 'Safari',
22
+ menuBarItems: ['File', 'Edit', 'View', 'History', 'Bookmarks', 'Window', 'Help'],
23
+ signalStrength: 4,
24
+ showNetworkType: false,
25
+ networkType: '5G',
26
+ wifiStrength: 3,
27
+ batteryLevel: 100,
28
+ batteryCharging: false,
29
+ showBatteryPercentage: true,
30
+ colorScheme: 'light',
31
+ carrierName: 'Carrier',
32
+ autoLocale: true,
33
+ };
34
+ const DEFAULT_LAYOUTS = {
35
+ 'iphone-dynamic-island': {
36
+ leftInset: 56,
37
+ rightInset: 36,
38
+ centerGap: { left: 128, right: 274 },
39
+ fontSize: { time: 17, networkType: 13 },
40
+ iconSize: { signal: 20, wifi: 18, battery: 28 },
41
+ iconGap: 7,
42
+ },
43
+ 'iphone-notch': {
44
+ leftInset: 56,
45
+ rightInset: 36,
46
+ centerGap: { left: 128, right: 274 },
47
+ fontSize: { time: 17, networkType: 13 },
48
+ iconSize: { signal: 20, wifi: 18, battery: 28 },
49
+ iconGap: 7,
50
+ },
51
+ 'iphone-home-button': {
52
+ leftInset: 6,
53
+ rightInset: 6,
54
+ fontSize: { time: 14, networkType: 14 },
55
+ iconSize: { signal: 16, wifi: 14, battery: 24 },
56
+ iconGap: 4,
57
+ },
58
+ ipad: {
59
+ leftInset: 24,
60
+ rightInset: 24,
61
+ fontSize: { time: 15, networkType: 11 },
62
+ iconSize: { signal: 17, wifi: 15, battery: 25 },
63
+ iconGap: 6,
64
+ },
65
+ 'mac-notch': {
66
+ leftInset: 12,
67
+ rightInset: 12,
68
+ centerGap: { left: 700, right: 936 },
69
+ fontSize: { time: 13, networkType: 11 },
70
+ iconSize: { signal: 0, wifi: 14, battery: 22 },
71
+ iconGap: 8,
72
+ },
73
+ };
74
+ /** Generate a complete `<svg>` document for the requested status bar variant. */
75
+ export function generateStatusBarSvg(opts) {
76
+ const cfg = { ...DEFAULT_CONFIG, ...opts.config };
77
+ const layout = opts.layout ?? DEFAULT_LAYOUTS[opts.deviceType];
78
+ const { width, height, deviceType } = opts;
79
+ const ps = opts.pixelScale ?? 1;
80
+ const color = cfg.colorScheme === 'dark' ? '#FFFFFF' : '#000000';
81
+ let body = '';
82
+ switch (deviceType) {
83
+ case 'iphone-dynamic-island':
84
+ case 'iphone-notch':
85
+ body = buildIPhoneStatusBar(cfg, layout, width, height, color);
86
+ break;
87
+ case 'iphone-home-button':
88
+ body = buildIPhoneHomeButtonStatusBar(cfg, layout, width, height, color);
89
+ break;
90
+ case 'ipad':
91
+ body = buildIPadStatusBar(cfg, layout, width, height, color);
92
+ break;
93
+ case 'mac-notch':
94
+ body = buildMacMenuBar(cfg, layout, width, height, color);
95
+ break;
96
+ }
97
+ const outW = Math.round(width * ps);
98
+ const outH = Math.round(height * ps);
99
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${outW}" height="${outH}" viewBox="0 0 ${width} ${height}" shape-rendering="geometricPrecision" text-rendering="geometricPrecision">${body}</svg>`;
100
+ }
101
+ // ── iPhone Status Bar (notch / dynamic island) ─────────────────────────
102
+ function buildIPhoneStatusBar(cfg, layout, width, height, color) {
103
+ const timeSize = layout.fontSize.time;
104
+ const networkSize = layout.fontSize.networkType;
105
+ const gap = layout.iconGap;
106
+ const { signal: signalW, wifi: wifiW, battery: batteryW } = layout.iconSize;
107
+ const isDark = cfg.colorScheme === 'dark';
108
+ // Time on the left.
109
+ const timeBaseline = baselineForCenteredText(0, height, 'text-semibold', timeSize);
110
+ const timeText = layoutText({
111
+ text: cfg.time,
112
+ fontId: 'text-semibold',
113
+ fontSize: timeSize,
114
+ x: layout.leftInset,
115
+ baselineY: timeBaseline,
116
+ letterSpacing: -0.4,
117
+ });
118
+ const timePath = `<path d="${timeText.pathData}" fill="${color}"/>`;
119
+ // Right side: signal · network? · wifi · battery — laid out from RIGHT.
120
+ // We compute total width and place items left-to-right starting at the
121
+ // calculated left edge. Vertical centering uses the icon viewBox aspect.
122
+ const items = [];
123
+ // Signal
124
+ const signalH = signalW * (13 / 20);
125
+ items.push({
126
+ width: signalW,
127
+ render: (x) => renderSignalSvg(cfg.signalStrength, color, signalW, x, (height - signalH) / 2),
128
+ });
129
+ // Network type label (optional)
130
+ if (cfg.showNetworkType) {
131
+ const m = measureText({
132
+ text: cfg.networkType,
133
+ fontId: 'text-semibold',
134
+ fontSize: networkSize,
135
+ letterSpacing: 0.2,
136
+ });
137
+ items.push({
138
+ width: m.width,
139
+ render: (x) => {
140
+ const baseline = baselineForCenteredText(0, height, 'text-semibold', networkSize);
141
+ const t = layoutText({
142
+ text: cfg.networkType,
143
+ fontId: 'text-semibold',
144
+ fontSize: networkSize,
145
+ x,
146
+ baselineY: baseline,
147
+ letterSpacing: 0.2,
148
+ });
149
+ return `<path d="${t.pathData}" fill="${color}"/>`;
150
+ },
151
+ });
152
+ }
153
+ // WiFi
154
+ const wifiH = wifiW * (13 / 18);
155
+ items.push({
156
+ width: wifiW,
157
+ render: (x) => renderWifiSvg(cfg.wifiStrength, color, wifiW, x, (height - wifiH) / 2),
158
+ });
159
+ // Battery
160
+ const batteryH = batteryW * (16 / 35);
161
+ items.push({
162
+ width: batteryW,
163
+ render: (x) => renderBatterySvg(cfg.batteryLevel, cfg.batteryCharging, color, batteryW, x, (height - batteryH) / 2, isDark),
164
+ });
165
+ const totalRightW = items.reduce((sum, it, i) => sum + it.width + (i > 0 ? gap : 0), 0);
166
+ let cursorX = width - layout.rightInset - totalRightW;
167
+ let rightSvg = '';
168
+ for (let i = 0; i < items.length; i++) {
169
+ if (i > 0)
170
+ cursorX += gap;
171
+ rightSvg += items[i].render(cursorX);
172
+ cursorX += items[i].width;
173
+ }
174
+ return `${timePath}${rightSvg}`;
175
+ }
176
+ // ── iPhone Home Button Status Bar ──────────────────────────────────────
177
+ function buildIPhoneHomeButtonStatusBar(cfg, layout, width, height, color) {
178
+ const timeSize = layout.fontSize.time;
179
+ const labelSize = layout.fontSize.networkType;
180
+ const gap = layout.iconGap;
181
+ const { signal: signalW, wifi: wifiW, battery: batteryW } = layout.iconSize;
182
+ const isDark = cfg.colorScheme === 'dark';
183
+ let svg = '';
184
+ // ── LEFT: signal · carrier · network? · wifi ──
185
+ const signalH = signalW * (13 / 20);
186
+ const wifiH = wifiW * (13 / 18);
187
+ let lx = layout.leftInset;
188
+ svg += renderSignalSvg(cfg.signalStrength, color, signalW, lx, (height - signalH) / 2);
189
+ lx += signalW + gap;
190
+ // Carrier label (max-width:80px, ellipsized if needed)
191
+ const carrierBaseline = baselineForCenteredText(0, height, 'text-regular', labelSize);
192
+ const carrier = ellipsizeText(cfg.carrierName, 'text-regular', labelSize, 80);
193
+ const carrierLayout = layoutText({
194
+ text: carrier,
195
+ fontId: 'text-regular',
196
+ fontSize: labelSize,
197
+ x: lx,
198
+ baselineY: carrierBaseline,
199
+ });
200
+ svg += `<path d="${carrierLayout.pathData}" fill="${color}"/>`;
201
+ lx += carrierLayout.width + gap;
202
+ // Network badge (optional, semibold, 1px smaller)
203
+ if (cfg.showNetworkType) {
204
+ const badgeSize = labelSize - 1;
205
+ const badgeBaseline = baselineForCenteredText(0, height, 'text-semibold', badgeSize);
206
+ const badge = layoutText({
207
+ text: cfg.networkType,
208
+ fontId: 'text-semibold',
209
+ fontSize: badgeSize,
210
+ x: lx,
211
+ baselineY: badgeBaseline,
212
+ });
213
+ svg += `<path d="${badge.pathData}" fill="${color}"/>`;
214
+ lx += badge.width + gap;
215
+ }
216
+ svg += renderWifiSvg(cfg.wifiStrength, color, wifiW, lx, (height - wifiH) / 2);
217
+ // ── CENTER: time (semibold) ──
218
+ const timeMeasure = measureText({
219
+ text: cfg.time,
220
+ fontId: 'text-semibold',
221
+ fontSize: timeSize,
222
+ letterSpacing: -0.4,
223
+ });
224
+ const timeBaseline = baselineForCenteredText(0, height, 'text-semibold', timeSize);
225
+ const timeLayout = layoutText({
226
+ text: cfg.time,
227
+ fontId: 'text-semibold',
228
+ fontSize: timeSize,
229
+ x: (width - timeMeasure.width) / 2,
230
+ baselineY: timeBaseline,
231
+ letterSpacing: -0.4,
232
+ });
233
+ svg += `<path d="${timeLayout.pathData}" fill="${color}"/>`;
234
+ // ── RIGHT: batteryPct · battery icon ──
235
+ const batteryH = batteryW * (16 / 35);
236
+ const items = [];
237
+ if (cfg.showBatteryPercentage) {
238
+ const text = `${cfg.batteryLevel}%`;
239
+ const m = measureText({ text, fontId: 'text-regular', fontSize: labelSize });
240
+ items.push({
241
+ width: m.width,
242
+ render: (x) => {
243
+ const baseline = baselineForCenteredText(0, height, 'text-regular', labelSize);
244
+ const t = layoutText({
245
+ text,
246
+ fontId: 'text-regular',
247
+ fontSize: labelSize,
248
+ x,
249
+ baselineY: baseline,
250
+ });
251
+ return `<path d="${t.pathData}" fill="${color}"/>`;
252
+ },
253
+ });
254
+ }
255
+ items.push({
256
+ width: batteryW,
257
+ render: (x) => renderBatterySvg(cfg.batteryLevel, cfg.batteryCharging, color, batteryW, x, (height - batteryH) / 2, isDark),
258
+ });
259
+ const totalRightW = items.reduce((sum, it, i) => sum + it.width + (i > 0 ? gap : 0), 0);
260
+ let rx = width - layout.rightInset - totalRightW;
261
+ for (let i = 0; i < items.length; i++) {
262
+ if (i > 0)
263
+ rx += gap;
264
+ svg += items[i].render(rx);
265
+ rx += items[i].width;
266
+ }
267
+ return svg;
268
+ }
269
+ // ── iPad Status Bar ────────────────────────────────────────────────────
270
+ function buildIPadStatusBar(cfg, layout, width, height, color) {
271
+ const timeSize = layout.fontSize.time;
272
+ const labelSize = layout.fontSize.networkType;
273
+ const gap = layout.iconGap;
274
+ const { signal: signalW, wifi: wifiW, battery: batteryW } = layout.iconSize;
275
+ const isDark = cfg.colorScheme === 'dark';
276
+ let svg = '';
277
+ // LEFT: time · 16px gap · date
278
+ const timeBaseline = baselineForCenteredText(0, height, 'text-regular', timeSize);
279
+ const timeLayout = layoutText({
280
+ text: cfg.time,
281
+ fontId: 'text-regular',
282
+ fontSize: timeSize,
283
+ x: layout.leftInset,
284
+ baselineY: timeBaseline,
285
+ letterSpacing: -0.4,
286
+ });
287
+ svg += `<path d="${timeLayout.pathData}" fill="${color}"/>`;
288
+ if (cfg.date) {
289
+ const dateLayout = layoutText({
290
+ text: cfg.date,
291
+ fontId: 'text-regular',
292
+ fontSize: timeSize,
293
+ x: layout.leftInset + timeLayout.width + 16,
294
+ baselineY: timeBaseline,
295
+ letterSpacing: -0.4,
296
+ });
297
+ svg += `<path d="${dateLayout.pathData}" fill="${color}"/>`;
298
+ }
299
+ // RIGHT: signal · wifi · battery% · battery
300
+ const signalH = signalW * (13 / 20);
301
+ const wifiH = wifiW * (13 / 18);
302
+ const batteryH = batteryW * (16 / 35);
303
+ const items = [];
304
+ items.push({
305
+ width: signalW,
306
+ render: (x) => renderSignalSvg(cfg.signalStrength, color, signalW, x, (height - signalH) / 2),
307
+ });
308
+ items.push({
309
+ width: wifiW,
310
+ render: (x) => renderWifiSvg(cfg.wifiStrength, color, wifiW, x, (height - wifiH) / 2),
311
+ });
312
+ const pctText = `${cfg.batteryLevel}%`;
313
+ const pctMeasure = measureText({ text: pctText, fontId: 'text-regular', fontSize: labelSize });
314
+ items.push({
315
+ width: pctMeasure.width,
316
+ render: (x) => {
317
+ const baseline = baselineForCenteredText(0, height, 'text-regular', labelSize);
318
+ const t = layoutText({
319
+ text: pctText,
320
+ fontId: 'text-regular',
321
+ fontSize: labelSize,
322
+ x,
323
+ baselineY: baseline,
324
+ });
325
+ return `<path d="${t.pathData}" fill="${color}"/>`;
326
+ },
327
+ });
328
+ items.push({
329
+ width: batteryW,
330
+ render: (x) => renderBatterySvg(cfg.batteryLevel, cfg.batteryCharging, color, batteryW, x, (height - batteryH) / 2, isDark),
331
+ });
332
+ const totalRightW = items.reduce((sum, it, i) => sum + it.width + (i > 0 ? gap : 0), 0);
333
+ let rx = width - layout.rightInset - totalRightW;
334
+ for (let i = 0; i < items.length; i++) {
335
+ if (i > 0)
336
+ rx += gap;
337
+ svg += items[i].render(rx);
338
+ rx += items[i].width;
339
+ }
340
+ return svg;
341
+ }
342
+ // ── macOS Menu Bar ─────────────────────────────────────────────────────
343
+ function buildMacMenuBar(cfg, layout, width, height, color) {
344
+ const fontSize = layout.fontSize.time;
345
+ const gap = layout.iconGap;
346
+ const { wifi: wifiW, battery: batteryW } = layout.iconSize;
347
+ const isDark = cfg.colorScheme === 'dark';
348
+ // Mac uses SF Pro Display, not Text.
349
+ const baseFontId = 'display-regular';
350
+ const boldFontId = 'display-semibold';
351
+ const baseline = baselineForCenteredText(0, height, baseFontId, fontSize);
352
+ const boldBaseline = baselineForCenteredText(0, height, boldFontId, fontSize);
353
+ let svg = '';
354
+ // ── LEFT: 􀣺 (apple) · App · Menu items ──
355
+ // Apple logo: SF Pro Symbols, ~3px larger than text
356
+ const appleSize = fontSize + 3;
357
+ const appleBaseline = baselineForCenteredText(0, height, 'symbols', appleSize);
358
+ let lx = layout.leftInset;
359
+ const appleLayout = layoutText({
360
+ text: '\uF8FF',
361
+ fontId: 'symbols',
362
+ fontSize: appleSize,
363
+ x: lx,
364
+ baselineY: appleBaseline,
365
+ });
366
+ svg += `<path d="${appleLayout.pathData}" fill="${color}"/>`;
367
+ lx += appleLayout.width + (gap + 6);
368
+ // App name (bold)
369
+ const appLayout = layoutText({
370
+ text: cfg.menuBarApp,
371
+ fontId: boldFontId,
372
+ fontSize,
373
+ x: lx,
374
+ baselineY: boldBaseline,
375
+ });
376
+ svg += `<path d="${appLayout.pathData}" fill="${color}"/>`;
377
+ lx += appLayout.width + (gap + 6);
378
+ // Menu items (regular, gap+10 between items per the HTML inner flex)
379
+ const itemGap = gap + 10;
380
+ for (let i = 0; i < cfg.menuBarItems.length; i++) {
381
+ const item = cfg.menuBarItems[i];
382
+ const itemLayout = layoutText({
383
+ text: item,
384
+ fontId: baseFontId,
385
+ fontSize,
386
+ x: lx,
387
+ baselineY: baseline,
388
+ });
389
+ svg += `<path d="${itemLayout.pathData}" fill="${color}"/>`;
390
+ lx += itemLayout.width + (i < cfg.menuBarItems.length - 1 ? itemGap : 0);
391
+ }
392
+ // ── RIGHT: wifi · [batt% batt] · search · cc · avatar · date · time ──
393
+ const wifiH = wifiW * (13 / 18);
394
+ const batteryH = batteryW * (16 / 35);
395
+ // Each item knows its own width and how to render at a given x.
396
+ const rightItems = [];
397
+ rightItems.push({
398
+ width: wifiW,
399
+ render: (x) => renderWifiSvg(cfg.wifiStrength, color, wifiW, x, (height - wifiH) / 2),
400
+ });
401
+ // Battery cluster: pct text + icon, separated by 3px (matches the HTML
402
+ // inner flex `gap:3px`).
403
+ const battPctText = `${cfg.batteryLevel}%`;
404
+ const battSize = fontSize - 1;
405
+ const pctMeasure = measureText({ text: battPctText, fontId: baseFontId, fontSize: battSize });
406
+ const battBaseline = baselineForCenteredText(0, height, baseFontId, battSize);
407
+ const battClusterW = (cfg.showBatteryPercentage ? pctMeasure.width + 3 : 0) + batteryW;
408
+ rightItems.push({
409
+ width: battClusterW,
410
+ render: (x) => {
411
+ let s = '';
412
+ if (cfg.showBatteryPercentage) {
413
+ const t = layoutText({
414
+ text: battPctText,
415
+ fontId: baseFontId,
416
+ fontSize: battSize,
417
+ x,
418
+ baselineY: battBaseline,
419
+ });
420
+ s += `<path d="${t.pathData}" fill="${color}"/>`;
421
+ x += pctMeasure.width + 3;
422
+ }
423
+ s += renderBatterySvg(cfg.batteryLevel, cfg.batteryCharging, color, batteryW, x, (height - batteryH) / 2, isDark);
424
+ return s;
425
+ },
426
+ });
427
+ // SF Symbols: search · cc · avatar
428
+ const symbolBaseline = baselineForCenteredText(0, height, 'symbols', fontSize);
429
+ const symbolList = [
430
+ { text: '\u{1002AB}' }, // search
431
+ { text: '\u{10070A}' }, // control center
432
+ { text: '\u{10026D}' }, // avatar
433
+ ];
434
+ for (const sym of symbolList) {
435
+ const m = measureText({ text: sym.text, fontId: 'symbols', fontSize });
436
+ rightItems.push({
437
+ width: m.width,
438
+ render: (x) => {
439
+ const t = layoutText({
440
+ text: sym.text,
441
+ fontId: 'symbols',
442
+ fontSize,
443
+ x,
444
+ baselineY: symbolBaseline,
445
+ });
446
+ return `<path d="${t.pathData}" fill="${color}"/>`;
447
+ },
448
+ });
449
+ }
450
+ // Date + time (medium weight in the HTML — we approximate with semibold
451
+ // since our font subset only ships regular/semibold). At 13px the visual
452
+ // delta vs. true medium is imperceptible.
453
+ if (cfg.date) {
454
+ const dateBaseline = baselineForCenteredText(0, height, boldFontId, fontSize);
455
+ const dateMeasure = measureText({ text: cfg.date, fontId: boldFontId, fontSize });
456
+ rightItems.push({
457
+ width: dateMeasure.width,
458
+ render: (x) => {
459
+ const t = layoutText({
460
+ text: cfg.date,
461
+ fontId: boldFontId,
462
+ fontSize,
463
+ x,
464
+ baselineY: dateBaseline,
465
+ });
466
+ return `<path d="${t.pathData}" fill="${color}"/>`;
467
+ },
468
+ });
469
+ }
470
+ const timeBaseline = baselineForCenteredText(0, height, boldFontId, fontSize);
471
+ const timeMeasure = measureText({ text: cfg.time, fontId: boldFontId, fontSize });
472
+ rightItems.push({
473
+ width: timeMeasure.width,
474
+ render: (x) => {
475
+ const t = layoutText({
476
+ text: cfg.time,
477
+ fontId: boldFontId,
478
+ fontSize,
479
+ x,
480
+ baselineY: timeBaseline,
481
+ });
482
+ return `<path d="${t.pathData}" fill="${color}"/>`;
483
+ },
484
+ });
485
+ const rightGap = gap + 2;
486
+ const totalRightW = rightItems.reduce((sum, it, i) => sum + it.width + (i > 0 ? rightGap : 0), 0);
487
+ let rx = width - layout.rightInset - totalRightW;
488
+ for (let i = 0; i < rightItems.length; i++) {
489
+ if (i > 0)
490
+ rx += rightGap;
491
+ svg += rightItems[i].render(rx);
492
+ rx += rightItems[i].width;
493
+ }
494
+ return svg;
495
+ }
496
+ // ── Icon SVG generators ────────────────────────────────────────────────
497
+ //
498
+ // These mirror the SVG output of status-bar.ts's renderSignalBars / renderWifi
499
+ // / renderBattery, but emit a `<g transform="...">` so the icon can be placed
500
+ // at any (x, y) inside the parent SVG instead of being a top-level `<svg>`.
501
+ const SIGNAL_BAR_PATHS = [
502
+ 'M2.13333 7.53962H1.06667C0.477563 7.53962 0 8.06421 0 8.71132V11.0547C0 11.7018 0.477563 12.2264 1.06667 12.2264H2.13333C2.72244 12.2264 3.2 11.7018 3.2 11.0547V8.71132C3.2 8.06421 2.72244 7.53962 2.13333 7.53962Z',
503
+ 'M7.43411 5.09433H6.36745C5.77834 5.09433 5.30078 5.62652 5.30078 6.28301V11.0377C5.30078 11.6942 5.77834 12.2264 6.36745 12.2264H7.43411C8.02322 12.2264 8.50078 11.6942 8.50078 11.0377V6.28301C8.50078 5.62652 8.02322 5.09433 7.43411 5.09433Z',
504
+ 'M11.7659 2.44528H12.8326C13.4217 2.44528 13.8992 2.97078 13.8992 3.61902V11.0527C13.8992 11.7009 13.4217 12.2264 12.8326 12.2264H11.7659C11.1768 12.2264 10.6992 11.7009 10.6992 11.0527V3.61902C10.6992 2.97078 11.1768 2.44528 11.7659 2.44528Z',
505
+ 'M19.2 1.14623C19.2 0.513183 18.7224 0 18.1333 0H17.0667C16.4776 0 16 0.513183 16 1.14623V11.0802C16 11.7132 16.4776 12.2264 17.0667 12.2264H18.1333C18.7224 12.2264 19.2 11.7132 19.2 11.0802V1.14623Z',
506
+ ];
507
+ const WIFI_TIER_PATHS = [
508
+ 'M8.5713 2.46628C11.0584 2.46639 13.4504 3.38847 15.2529 5.04195C15.3887 5.1696 15.6056 5.16799 15.7393 5.03834L17.0368 3.77487C17.1045 3.70911 17.1422 3.62004 17.1417 3.52735C17.1411 3.43467 17.1023 3.34603 17.0338 3.28104C12.3028 -1.09368 4.83907 -1.09368 0.108056 3.28104C0.039524 3.34598 0.000639766 3.4346 7.82398e-06 3.52728C-0.000624118 3.61996 0.0370483 3.70906 0.104689 3.77487L1.40255 5.03834C1.53615 5.16819 1.75327 5.1698 1.88893 5.04195C3.69167 3.38836 6.08395 2.46628 8.5713 2.46628Z',
509
+ 'M8.56795 6.68656C9.92527 6.68647 11.2341 7.19821 12.2403 8.12234C12.3763 8.2535 12.5907 8.25065 12.7234 8.11593L14.0106 6.79663C14.0784 6.72742 14.1161 6.63355 14.1151 6.536C14.1141 6.43844 14.0746 6.34536 14.0054 6.27757C10.9416 3.38672 6.19688 3.38672 3.13305 6.27757C3.06384 6.34536 3.02435 6.43849 3.02345 6.53607C3.02254 6.63366 3.06028 6.72752 3.12822 6.79663L4.41513 8.11593C4.54778 8.25065 4.76215 8.2535 4.89823 8.12234C5.90368 7.19882 7.21152 6.68713 8.56795 6.68656Z',
510
+ 'M11.0924 9.48011C11.0943 9.58546 11.0572 9.68703 10.9899 9.76084L8.81327 12.2156C8.74946 12.2877 8.66247 12.3283 8.5717 12.3283C8.48093 12.3283 8.39394 12.2877 8.33013 12.2156L6.1531 9.76084C6.08585 9.68697 6.04886 9.58537 6.05085 9.48002C6.05284 9.37467 6.09365 9.27491 6.16364 9.20429C7.55374 7.8904 9.58966 7.8904 10.9798 9.20429C11.0497 9.27497 11.0904 9.37476 11.0924 9.48011Z',
511
+ ];
512
+ function renderSignalSvg(strength, color, size, x, y) {
513
+ const viewW = 20;
514
+ const viewH = 13;
515
+ const scale = size / viewW;
516
+ const paths = SIGNAL_BAR_PATHS.map((d, i) => {
517
+ const opacity = i < strength ? 1 : 0.25;
518
+ return `<path d="${d}" fill="${color}" opacity="${opacity}"/>`;
519
+ }).join('');
520
+ return `<g transform="translate(${fmt(x)},${fmt(y)}) scale(${fmt(scale)})">${paths}</g>`;
521
+ }
522
+ function renderWifiSvg(strength, color, size, x, y) {
523
+ const viewW = 18;
524
+ const viewH = 13;
525
+ const scale = size / viewW;
526
+ const tiers = WIFI_TIER_PATHS.map((d, i) => {
527
+ const tierLevel = 3 - i;
528
+ const opacity = strength >= tierLevel ? 1 : 0.25;
529
+ return `<path d="${d}" fill="${color}" opacity="${opacity}"/>`;
530
+ }).join('');
531
+ return `<g transform="translate(${fmt(x)},${fmt(y)}) scale(${fmt(scale)})">${tiers}</g>`;
532
+ }
533
+ function renderBatterySvg(level, charging, color, size, x, y, isDark) {
534
+ const viewW = 35;
535
+ const viewH = 16;
536
+ const scale = size / viewW;
537
+ if (charging) {
538
+ const chargingGreen = isDark ? '#0DF07D' : '#32CB58';
539
+ const chargingBorder = '#ADADB1';
540
+ const boltColor = color;
541
+ const inner = `
542
+ <path d="M17.332 13.9694L15.8203 15.8561H24.668C25.668 15.8561 26.582 15.778 27.4102 15.6217C28.2383 15.4576 28.9531 15.0748 29.5547 14.4733C30.1484 13.8795 30.5234 13.1725 30.6797 12.3522C30.8438 11.524 30.9258 10.61 30.9258 9.60998V6.2467C30.9258 5.2467 30.8438 4.33655 30.6797 3.51624C30.5234 2.68811 30.1484 1.97717 29.5547 1.38342C28.9531 0.78186 28.2383 0.402954 27.4102 0.246704C26.582 0.0826416 25.668 0.000610352 24.668 0.000610352H19.6992C19.7695 0.18811 19.8086 0.37561 19.8164 0.56311C19.8242 0.742798 19.8047 0.93811 19.7578 1.14905C19.7188 1.35999 19.6523 1.60608 19.5586 1.88733H24.9961C25.5977 1.88733 26.1875 1.94592 26.7656 2.06311C27.3516 2.17249 27.8203 2.40295 28.1719 2.75452C28.5312 3.11389 28.7656 3.58264 28.875 4.16077C28.9844 4.73108 29.0391 5.31702 29.0391 5.91858V9.92639C29.0391 10.5358 28.9844 11.1295 28.875 11.7076C28.7656 12.278 28.5312 12.7428 28.1719 13.1022C27.8203 13.4537 27.3516 13.6881 26.7656 13.8053C26.1875 13.9147 25.5977 13.9694 24.9961 13.9694H17.332Z" fill="${chargingBorder}"/>
543
+ <path d="M13.5937 1.88672L15.1055 9.53674e-07L6.25781 1.72716e-06C5.25781 1.81458e-06 4.34375 0.0781269 3.51562 0.234377C2.6875 0.39844 1.97265 0.781252 1.37109 1.38281C0.777343 1.97656 0.402343 2.6836 0.246093 3.50391C0.0820302 4.33203 -9.27501e-07 5.2461 -8.40078e-07 6.2461L-5.46051e-07 9.60938C-4.58628e-07 10.6094 0.0820309 11.5195 0.246093 12.3398C0.402344 13.168 0.777344 13.8789 1.37109 14.4727C1.97266 15.0742 2.6875 15.4531 3.51562 15.6094C4.34375 15.7734 5.25781 15.8555 6.25781 15.8555L11.2266 15.8555C11.1562 15.668 11.1172 15.4805 11.1094 15.293C11.1016 15.1133 11.1211 14.918 11.168 14.707C11.207 14.4961 11.2734 14.25 11.3672 13.9688L5.92969 13.9688C5.32812 13.9688 4.73828 13.9102 4.16016 13.793C3.57422 13.6836 3.10547 13.4531 2.75391 13.1016C2.39453 12.7422 2.16016 12.2734 2.05078 11.6953C1.94141 11.125 1.88672 10.5391 1.88672 9.9375L1.88672 5.92969C1.88672 5.32031 1.94141 4.72656 2.05078 4.14844C2.16016 3.57813 2.39453 3.11328 2.75391 2.75391C3.10547 2.40235 3.57422 2.16797 4.16016 2.05078C4.73828 1.94141 5.32812 1.88672 5.92969 1.88672L13.5937 1.88672Z" fill="${chargingBorder}"/>
544
+ <path d="M32.5195 4.89905V10.9576C32.832 10.9342 33.1562 10.7975 33.4922 10.5475C33.8281 10.2897 34.1094 9.93811 34.3359 9.4928C34.5703 9.04749 34.6875 8.52405 34.6875 7.92249C34.6875 7.32874 34.5703 6.8092 34.3359 6.36389C34.1094 5.91858 33.8281 5.56702 33.4922 5.3092C33.1562 5.05139 32.832 4.91467 32.5195 4.89905Z" fill="${chargingBorder}"/>
545
+ <path d="M4.32422 12.5162C4.58203 12.5787 4.91016 12.61 5.30859 12.61H12.1758L12.7969 10.9225H11.1328C10.5469 10.9225 10.043 10.7194 9.62109 10.3131C9.20703 9.90686 9 9.40686 9 8.81311C9 8.29749 9.17578 7.81702 9.52734 7.3717L12.8086 3.2467H5.34375C4.92969 3.2467 4.59375 3.28186 4.33594 3.35217C4.07812 3.41467 3.86719 3.52795 3.70312 3.69202C3.54688 3.85608 3.43359 4.06702 3.36328 4.32483C3.29297 4.57483 3.25781 4.90686 3.25781 5.32092V10.5592C3.25781 10.9655 3.29297 11.2936 3.36328 11.5436C3.43359 11.7936 3.54688 12.0006 3.70312 12.1647C3.86719 12.3287 4.07422 12.4459 4.32422 12.5162Z" fill="${chargingGreen}"/>
546
+ <path d="M21.6914 8.48499L18.4102 12.61H25.6172C26.0234 12.61 26.3516 12.5787 26.6016 12.5162C26.8516 12.4459 27.0586 12.3287 27.2227 12.1647C27.5273 11.86 27.6797 11.3248 27.6797 10.5592V5.29749C27.6797 4.89124 27.6445 4.56311 27.5742 4.31311C27.5039 4.06311 27.3867 3.85608 27.2227 3.69202C27.0586 3.52795 26.8516 3.41467 26.6016 3.35217C26.3516 3.28186 26.0234 3.2467 25.6172 3.2467H19.043L18.4219 4.9342H20.0742C20.668 4.9342 21.1719 5.13733 21.5859 5.54358C22.0078 5.94983 22.2188 6.44983 22.2188 7.04358C22.2188 7.5592 22.043 8.03967 21.6914 8.48499Z" fill="${chargingGreen}"/>
547
+ <path d="M10.7812 8.34432C10.6484 8.52401 10.582 8.68026 10.582 8.81307C10.582 8.96151 10.6328 9.0826 10.7344 9.17635C10.8438 9.2701 10.9727 9.31698 11.1211 9.31698H15.0938L12.9727 15.0123C12.8867 15.2623 12.8945 15.4654 12.9961 15.6217C13.1055 15.7779 13.2578 15.8599 13.4531 15.8678C13.6484 15.8678 13.8242 15.7701 13.9805 15.5748L20.4023 7.50057C20.5352 7.3287 20.6016 7.17635 20.6016 7.04354C20.6016 6.8951 20.5469 6.77401 20.4375 6.68026C20.3359 6.58651 20.2109 6.53963 20.0625 6.53963H16.0898L18.2109 0.844323C18.3047 0.594323 18.2969 0.391198 18.1875 0.234948C18.0781 0.0786978 17.9258 0.000572791 17.7305 0.000572791C17.543 -0.00723971 17.3672 0.0865103 17.2031 0.281823L10.7812 8.34432Z" fill="${boltColor}"/>
548
+ `;
549
+ return `<g transform="translate(${fmt(x)},${fmt(y)}) scale(${fmt(scale)})">${inner}</g>`;
550
+ }
551
+ // Non-charging: outline + nub + level fill.
552
+ const batteryBorder = '#ADADB1';
553
+ // The native asset's "fill" path covers the entire interior; we clip it
554
+ // horizontally based on level by using a `clipPath` on a `<g>` so we don't
555
+ // touch the path data. The native rendering shows the bar reaching the
556
+ // right inner edge at 100%, with the standard apple battery curve.
557
+ // Native left/right inner padding is ~3.26 (matches the path geometry).
558
+ const innerLeft = 3.258;
559
+ const innerWidth = 24.422; // 27.68 - 3.258 ≈ inner cavity
560
+ const fillW = innerWidth * Math.max(0, Math.min(1, level / 100));
561
+ const fillRect = level > 0
562
+ ? `<rect x="${fmt(innerLeft)}" y="3.247" width="${fmt(fillW)}" height="9.363" rx="${fmt(2.05)}" ry="${fmt(2.05)}" fill="${color}"/>`
563
+ : '';
564
+ const inner = `
565
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M24.668 0.000976562C25.668 0.000976562 26.582 0.0830078 27.4102 0.24707C28.2383 0.403328 28.9531 0.782242 29.5547 1.38379C30.1483 1.9775 30.5235 2.68856 30.6797 3.5166C30.8437 4.33682 30.9258 5.24721 30.9258 6.24707V9.61035C30.9258 10.6102 30.8437 11.5245 30.6797 12.3525C30.5234 13.1727 30.1484 13.88 29.5547 14.4736C28.9532 15.0751 28.2382 15.458 27.4102 15.6221C26.582 15.7783 25.6679 15.8564 24.668 15.8564L10.5156 15.8574V15.8555H6.25781C5.25781 15.8555 4.34375 15.7734 3.51562 15.6094C2.6875 15.4531 1.97266 15.0742 1.37109 14.4727C0.777344 13.8789 0.402344 13.168 0.246094 12.3398C0.0820312 11.5195 9.34357e-08 10.6094 0 9.60938V6.24609C-6.73586e-08 5.24609 0.0820313 4.33203 0.246094 3.50391C0.402344 2.68359 0.777344 1.97656 1.37109 1.38281C1.97266 0.78125 2.6875 0.398438 3.51562 0.234375C4.34375 0.078125 5.25781 1.64241e-07 6.25781 0L24.668 0.000976562ZM5.92969 1.88672C5.32813 1.88672 4.73828 1.94141 4.16016 2.05078C3.57422 2.16797 3.10547 2.40234 2.75391 2.75391C2.39453 3.11328 2.16016 3.57813 2.05078 4.14844C1.94141 4.72656 1.88672 5.32031 1.88672 5.92969V9.9375C1.88672 10.5391 1.94141 11.125 2.05078 11.6953C2.16016 12.2734 2.39453 12.7422 2.75391 13.1016C3.10547 13.4531 3.57422 13.6836 4.16016 13.793C4.73828 13.9102 5.32813 13.9687 5.92969 13.9688L24.9961 13.9697C25.5976 13.9697 26.1875 13.915 26.7656 13.8057C27.3515 13.6885 27.8203 13.454 28.1719 13.1025C28.5312 12.7432 28.7656 12.2782 28.875 11.708C28.9844 11.13 29.039 10.536 29.0391 9.92676V5.91895C29.0391 5.3175 28.9843 4.73134 28.875 4.16113C28.7656 3.5831 28.5311 3.11423 28.1719 2.75488C27.8203 2.40333 27.3515 2.17286 26.7656 2.06348C26.1875 1.94629 25.5977 1.8877 24.9961 1.8877L13 1.88867V1.88672H5.92969Z" fill="${batteryBorder}"/>
566
+ <path d="M32.5195 4.89941C32.832 4.91504 33.1563 5.05176 33.4922 5.30957C33.828 5.56737 34.1094 5.91902 34.3359 6.36426C34.5702 6.80951 34.6875 7.32923 34.6875 7.92285C34.6875 8.52426 34.5703 9.04794 34.3359 9.49316C34.1094 9.93834 33.8281 10.2901 33.4922 10.5479C33.1563 10.7978 32.832 10.9346 32.5195 10.958V4.89941Z" fill="${batteryBorder}"/>
567
+ ${fillRect}
568
+ `;
569
+ return `<g transform="translate(${fmt(x)},${fmt(y)}) scale(${fmt(scale)})">${inner}</g>`;
570
+ }
571
+ // ── Helpers ────────────────────────────────────────────────────────────
572
+ /**
573
+ * Crude monospace ellipsis: shrink the text until measured width fits in
574
+ * `maxW`. Used for the iPhone home-button carrier label.
575
+ */
576
+ function ellipsizeText(text, fontId, fontSize, maxW) {
577
+ const measured = measureText({ text, fontId, fontSize });
578
+ if (measured.width <= maxW)
579
+ return text;
580
+ let lo = 0;
581
+ let hi = text.length;
582
+ while (lo < hi) {
583
+ const mid = (lo + hi + 1) >> 1;
584
+ const candidate = `${text.slice(0, mid)}…`;
585
+ if (measureText({ text: candidate, fontId, fontSize }).width <= maxW) {
586
+ lo = mid;
587
+ }
588
+ else {
589
+ hi = mid - 1;
590
+ }
591
+ }
592
+ return lo === 0 ? '…' : `${text.slice(0, lo)}…`;
593
+ }
594
+ function fmt(n) {
595
+ return Number.isInteger(n) ? String(n) : (Math.round(n * 1000) / 1000).toString();
596
+ }
597
+ //# sourceMappingURL=svg-status-bar.js.map