android-midscene-automation 0.2.4 → 1.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.
Files changed (525) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +159 -29
  3. package/artemis-main/.dockerignore +15 -0
  4. package/artemis-main/.env.example +34 -0
  5. package/artemis-main/CONTRIBUTING.md +35 -0
  6. package/artemis-main/Dockerfile +68 -0
  7. package/artemis-main/LICENSE +202 -0
  8. package/artemis-main/Makefile +115 -0
  9. package/artemis-main/README.md +322 -0
  10. package/artemis-main/README_CN.md +320 -0
  11. package/artemis-main/apps/__init__.py +15 -0
  12. package/artemis-main/apps/admin_console/__init__.py +19 -0
  13. package/artemis-main/apps/admin_console/core/__init__.py +13 -0
  14. package/artemis-main/apps/admin_console/core/config.py +43 -0
  15. package/artemis-main/apps/admin_console/core/security.py +178 -0
  16. package/artemis-main/apps/admin_console/core/state.py +243 -0
  17. package/artemis-main/apps/admin_console/database/__init__.py +13 -0
  18. package/artemis-main/apps/admin_console/database/connection.py +60 -0
  19. package/artemis-main/apps/admin_console/database/repositories/__init__.py +13 -0
  20. package/artemis-main/apps/admin_console/database/repositories/session_repository.py +564 -0
  21. package/artemis-main/apps/admin_console/database/repositories/step_repository.py +512 -0
  22. package/artemis-main/apps/admin_console/database/repositories/trace_repository.py +133 -0
  23. package/artemis-main/apps/admin_console/index.html +3787 -0
  24. package/artemis-main/apps/admin_console/replay_manager.py +2585 -0
  25. package/artemis-main/apps/admin_console/routers/__init__.py +13 -0
  26. package/artemis-main/apps/admin_console/routers/media.py +198 -0
  27. package/artemis-main/apps/admin_console/routers/replay.py +91 -0
  28. package/artemis-main/apps/admin_console/routers/sessions.py +239 -0
  29. package/artemis-main/apps/admin_console/routers/steps.py +109 -0
  30. package/artemis-main/apps/admin_console/routers/stream.py +56 -0
  31. package/artemis-main/apps/admin_console/routers/system.py +761 -0
  32. package/artemis-main/apps/admin_console/routers/tasks.py +517 -0
  33. package/artemis-main/apps/admin_console/schemas/__init__.py +13 -0
  34. package/artemis-main/apps/admin_console/schemas/task_schema.py +47 -0
  35. package/artemis-main/apps/admin_console/server.py +508 -0
  36. package/artemis-main/apps/admin_console/services/__init__.py +13 -0
  37. package/artemis-main/apps/admin_console/services/device_stream_service.py +137 -0
  38. package/artemis-main/apps/admin_console/services/ipc_service.py +262 -0
  39. package/artemis-main/apps/admin_console/services/media_service.py +623 -0
  40. package/artemis-main/apps/admin_console/services/model_service.py +148 -0
  41. package/artemis-main/apps/admin_console/services/task_preset_catalog.py +514 -0
  42. package/artemis-main/apps/admin_console/services/task_queue_service.py +1719 -0
  43. package/artemis-main/apps/admin_console/services/worker_process_io.py +144 -0
  44. package/artemis-main/apps/showcase_ui/README.md +31 -0
  45. package/artemis-main/apps/showcase_ui/angular.json +132 -0
  46. package/artemis-main/apps/showcase_ui/package-lock.json +9472 -0
  47. package/artemis-main/apps/showcase_ui/package.json +39 -0
  48. package/artemis-main/apps/showcase_ui/proxy.conf.json +23 -0
  49. package/artemis-main/apps/showcase_ui/public/favicon.ico +0 -0
  50. package/artemis-main/apps/showcase_ui/public/logo.png +0 -0
  51. package/artemis-main/apps/showcase_ui/public/logo.svg +56 -0
  52. package/artemis-main/apps/showcase_ui/src/app/app.component.html +24 -0
  53. package/artemis-main/apps/showcase_ui/src/app/app.component.scss +49 -0
  54. package/artemis-main/apps/showcase_ui/src/app/app.component.spec.ts +42 -0
  55. package/artemis-main/apps/showcase_ui/src/app/app.component.ts +32 -0
  56. package/artemis-main/apps/showcase_ui/src/app/app.config.ts +29 -0
  57. package/artemis-main/apps/showcase_ui/src/app/app.routes.ts +27 -0
  58. package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream-startup.spec.ts +108 -0
  59. package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.html +1436 -0
  60. package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.scss +3973 -0
  61. package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.ts +2235 -0
  62. package/artemis-main/apps/showcase_ui/src/app/components/chat-interface/chat-interface.component.html +380 -0
  63. package/artemis-main/apps/showcase_ui/src/app/components/chat-interface/chat-interface.component.scss +1171 -0
  64. package/artemis-main/apps/showcase_ui/src/app/components/chat-interface/chat-interface.component.ts +270 -0
  65. package/artemis-main/apps/showcase_ui/src/app/components/floating-video-player/floating-video-player.component.html +433 -0
  66. package/artemis-main/apps/showcase_ui/src/app/components/floating-video-player/floating-video-player.component.scss +987 -0
  67. package/artemis-main/apps/showcase_ui/src/app/components/floating-video-player/floating-video-player.component.ts +632 -0
  68. package/artemis-main/apps/showcase_ui/src/app/components/legacy-agent-stream/legacy-agent-stream.component.html +130 -0
  69. package/artemis-main/apps/showcase_ui/src/app/components/legacy-agent-stream/legacy-agent-stream.component.scss +406 -0
  70. package/artemis-main/apps/showcase_ui/src/app/components/legacy-agent-stream/legacy-agent-stream.component.ts +74 -0
  71. package/artemis-main/apps/showcase_ui/src/app/components/nav-switcher/nav-switcher.component.scss +115 -0
  72. package/artemis-main/apps/showcase_ui/src/app/components/nav-switcher/nav-switcher.component.ts +52 -0
  73. package/artemis-main/apps/showcase_ui/src/app/core/data/smart-tasks.data.ts +334 -0
  74. package/artemis-main/apps/showcase_ui/src/app/core/i18n/language.service.spec.ts +55 -0
  75. package/artemis-main/apps/showcase_ui/src/app/core/i18n/language.service.ts +61 -0
  76. package/artemis-main/apps/showcase_ui/src/app/core/i18n/translate.pipe.ts +11 -0
  77. package/artemis-main/apps/showcase_ui/src/app/core/i18n/zh-cn.ts +651 -0
  78. package/artemis-main/apps/showcase_ui/src/app/core/models/markdown.model.ts +43 -0
  79. package/artemis-main/apps/showcase_ui/src/app/core/models/pro-tuning.model.spec.ts +47 -0
  80. package/artemis-main/apps/showcase_ui/src/app/core/models/pro-tuning.model.ts +165 -0
  81. package/artemis-main/apps/showcase_ui/src/app/core/models/session.model.ts +85 -0
  82. package/artemis-main/apps/showcase_ui/src/app/core/models/stream.model.ts +246 -0
  83. package/artemis-main/apps/showcase_ui/src/app/core/models/system.model.ts +118 -0
  84. package/artemis-main/apps/showcase_ui/src/app/core/services/task-recommendation.service.ts +111 -0
  85. package/artemis-main/apps/showcase_ui/src/app/pages/home/home.component.html +2130 -0
  86. package/artemis-main/apps/showcase_ui/src/app/pages/home/home.component.scss +5204 -0
  87. package/artemis-main/apps/showcase_ui/src/app/pages/home/home.component.ts +1189 -0
  88. package/artemis-main/apps/showcase_ui/src/app/pages/legacy-workspace/legacy-workspace.component.html +39 -0
  89. package/artemis-main/apps/showcase_ui/src/app/pages/legacy-workspace/legacy-workspace.component.scss +151 -0
  90. package/artemis-main/apps/showcase_ui/src/app/pages/legacy-workspace/legacy-workspace.component.ts +58 -0
  91. package/artemis-main/apps/showcase_ui/src/app/pages/workspace/workspace.component.html +201 -0
  92. package/artemis-main/apps/showcase_ui/src/app/pages/workspace/workspace.component.scss +886 -0
  93. package/artemis-main/apps/showcase_ui/src/app/pages/workspace/workspace.component.ts +324 -0
  94. package/artemis-main/apps/showcase_ui/src/app/services/agent.service.spec.ts +418 -0
  95. package/artemis-main/apps/showcase_ui/src/app/services/agent.service.ts +2012 -0
  96. package/artemis-main/apps/showcase_ui/src/app/services/system.service.spec.ts +173 -0
  97. package/artemis-main/apps/showcase_ui/src/app/services/system.service.ts +620 -0
  98. package/artemis-main/apps/showcase_ui/src/app/utils/action-formatter.util.spec.ts +513 -0
  99. package/artemis-main/apps/showcase_ui/src/app/utils/action-formatter.util.ts +1015 -0
  100. package/artemis-main/apps/showcase_ui/src/app/utils/image-overlay.util.spec.ts +85 -0
  101. package/artemis-main/apps/showcase_ui/src/app/utils/image-overlay.util.ts +662 -0
  102. package/artemis-main/apps/showcase_ui/src/app/utils/markdown-parser.util.spec.ts +70 -0
  103. package/artemis-main/apps/showcase_ui/src/app/utils/markdown-parser.util.ts +383 -0
  104. package/artemis-main/apps/showcase_ui/src/app/utils/recording-timeline.util.spec.ts +107 -0
  105. package/artemis-main/apps/showcase_ui/src/app/utils/recording-timeline.util.ts +123 -0
  106. package/artemis-main/apps/showcase_ui/src/app/utils/run-info.util.spec.ts +92 -0
  107. package/artemis-main/apps/showcase_ui/src/app/utils/run-info.util.ts +116 -0
  108. package/artemis-main/apps/showcase_ui/src/app/utils/stream-aggregator.util.spec.ts +640 -0
  109. package/artemis-main/apps/showcase_ui/src/app/utils/stream-aggregator.util.ts +1021 -0
  110. package/artemis-main/apps/showcase_ui/src/app/utils/tool-formatter.util.spec.ts +375 -0
  111. package/artemis-main/apps/showcase_ui/src/app/utils/tool-formatter.util.ts +1419 -0
  112. package/artemis-main/apps/showcase_ui/src/index.html +37 -0
  113. package/artemis-main/apps/showcase_ui/src/main.ts +22 -0
  114. package/artemis-main/apps/showcase_ui/src/styles.scss +60 -0
  115. package/artemis-main/apps/showcase_ui/tsconfig.app.json +23 -0
  116. package/artemis-main/apps/showcase_ui/tsconfig.json +27 -0
  117. package/artemis-main/apps/showcase_ui/tsconfig.spec.json +23 -0
  118. package/artemis-main/artemis/__init__.py +44 -0
  119. package/artemis-main/artemis/__main__.py +18 -0
  120. package/artemis-main/artemis/_legacy_imports.py +94 -0
  121. package/artemis-main/artemis/_version.py +30 -0
  122. package/artemis-main/artemis/agents/__init__.py +17 -0
  123. package/artemis-main/artemis/agents/checker/checker.json +9 -0
  124. package/artemis-main/artemis/agents/checker/checker.py +859 -0
  125. package/artemis-main/artemis/agents/diagnoser/diagnoser.json +23 -0
  126. package/artemis-main/artemis/agents/diagnoser/diagnoser.py +608 -0
  127. package/artemis-main/artemis/agents/explorer/constants.py +52 -0
  128. package/artemis-main/artemis/agents/explorer/explorer.json +57 -0
  129. package/artemis-main/artemis/agents/explorer/explorer.py +473 -0
  130. package/artemis-main/artemis/agents/explorer/geometry.py +73 -0
  131. package/artemis-main/artemis/agents/explorer/native_runner.py +878 -0
  132. package/artemis-main/artemis/agents/explorer/perception_tools.py +632 -0
  133. package/artemis-main/artemis/agents/explorer/run_setup.py +403 -0
  134. package/artemis-main/artemis/agents/explorer/screen_index.py +266 -0
  135. package/artemis-main/artemis/agents/explorer/tiers.py +110 -0
  136. package/artemis-main/artemis/agents/explorer/tool_declarations.py +394 -0
  137. package/artemis-main/artemis/agents/explorer/universal_runner.py +317 -0
  138. package/artemis-main/artemis/agents/flash/context_compressor.py +452 -0
  139. package/artemis-main/artemis/agents/flash/flash_runner.md +51 -0
  140. package/artemis-main/artemis/agents/flash/flash_summarizer.md +53 -0
  141. package/artemis-main/artemis/agents/flash/flash_summarizer_single.md +54 -0
  142. package/artemis-main/artemis/agents/flash/runner.py +1167 -0
  143. package/artemis-main/artemis/agents/flash/summarizer.py +511 -0
  144. package/artemis-main/artemis/agents/history_analyzer/history_analyzer.md +8 -0
  145. package/artemis-main/artemis/agents/history_analyzer/history_analyzer.py +182 -0
  146. package/artemis-main/artemis/agents/image_processor/image_processor.md +74 -0
  147. package/artemis-main/artemis/agents/image_processor/image_processor.py +322 -0
  148. package/artemis-main/artemis/agents/log_analyzer/log_analyzer.md +15 -0
  149. package/artemis-main/artemis/agents/log_analyzer/log_analyzer.py +403 -0
  150. package/artemis-main/artemis/agents/log_analyzer/log_reader_sub_agent.md +8 -0
  151. package/artemis-main/artemis/agents/log_analyzer/output_analyzer.md +11 -0
  152. package/artemis-main/artemis/agents/log_analyzer/output_analyzer.py +163 -0
  153. package/artemis-main/artemis/agents/object_detector/object_detector.json +9 -0
  154. package/artemis-main/artemis/agents/object_detector/object_detector.py +211 -0
  155. package/artemis-main/artemis/agents/operator/operator.json +3 -0
  156. package/artemis-main/artemis/agents/operator/operator.py +1394 -0
  157. package/artemis-main/artemis/agents/operator/prompts.py +1064 -0
  158. package/artemis-main/artemis/agents/outputter/outputter.md +36 -0
  159. package/artemis-main/artemis/agents/outputter/outputter.py +552 -0
  160. package/artemis-main/artemis/agents/planner/planner.json +32 -0
  161. package/artemis-main/artemis/agents/planner/planner.py +436 -0
  162. package/artemis-main/artemis/agents/prompt_assembly.py +79 -0
  163. package/artemis-main/artemis/agents/summarizer/summarizer.py +190 -0
  164. package/artemis-main/artemis/agents/validator/action_execution.py +98 -0
  165. package/artemis-main/artemis/agents/validator/categories.py +33 -0
  166. package/artemis-main/artemis/agents/validator/execution_loop.py +488 -0
  167. package/artemis-main/artemis/agents/validator/incidents.py +120 -0
  168. package/artemis-main/artemis/agents/validator/pixel_safety_net.md +15 -0
  169. package/artemis-main/artemis/agents/validator/precondition_pixel.py +320 -0
  170. package/artemis-main/artemis/agents/validator/precondition_xml.py +672 -0
  171. package/artemis-main/artemis/agents/validator/tool_declarations.py +332 -0
  172. package/artemis-main/artemis/agents/validator/validator.py +188 -0
  173. package/artemis-main/artemis/agents/video_analyzer/__init__.py +15 -0
  174. package/artemis-main/artemis/agents/video_analyzer/audio_native.py +543 -0
  175. package/artemis-main/artemis/agents/video_analyzer/blackboard.py +821 -0
  176. package/artemis-main/artemis/agents/video_analyzer/chunk_agentic.py +455 -0
  177. package/artemis-main/artemis/agents/video_analyzer/chunk_conversation.py +491 -0
  178. package/artemis-main/artemis/agents/video_analyzer/chunk_native.py +437 -0
  179. package/artemis-main/artemis/agents/video_analyzer/conflict_resolution.py +137 -0
  180. package/artemis-main/artemis/agents/video_analyzer/gemini_files.py +162 -0
  181. package/artemis-main/artemis/agents/video_analyzer/native_coordinator.py +541 -0
  182. package/artemis-main/artemis/agents/video_analyzer/reliability.py +128 -0
  183. package/artemis-main/artemis/agents/video_analyzer/universal_engine.py +487 -0
  184. package/artemis-main/artemis/agents/video_analyzer/universal_tools.py +323 -0
  185. package/artemis-main/artemis/agents/video_analyzer/video_analyzer.md +51 -0
  186. package/artemis-main/artemis/agents/video_analyzer/video_analyzer.py +875 -0
  187. package/artemis-main/artemis/agents/video_analyzer/video_sub_agent.md +15 -0
  188. package/artemis-main/artemis/agents/video_analyzer/video_sub_agent_audio.md +12 -0
  189. package/artemis-main/artemis/clients/accessibility_client.py +438 -0
  190. package/artemis-main/artemis/clients/screen_client_factory.py +403 -0
  191. package/artemis-main/artemis/config/__init__.py +309 -0
  192. package/artemis-main/artemis/config/agent.py +1098 -0
  193. package/artemis-main/artemis/config/constants.py +151 -0
  194. package/artemis-main/artemis/config/core/__init__.py +24 -0
  195. package/artemis-main/artemis/config/core/engine.py +118 -0
  196. package/artemis-main/artemis/config/core/interpolator.py +78 -0
  197. package/artemis-main/artemis/config/llm.py +267 -0
  198. package/artemis-main/artemis/config/paths.py +244 -0
  199. package/artemis-main/artemis/config/runtime.py +197 -0
  200. package/artemis-main/artemis/config/settings.py +315 -0
  201. package/artemis-main/artemis/constants.py +47 -0
  202. package/artemis-main/artemis/context.py +289 -0
  203. package/artemis-main/artemis/controllers/__init__.py +41 -0
  204. package/artemis-main/artemis/controllers/controller_factory.py +41 -0
  205. package/artemis-main/artemis/controllers/device_controller.py +29 -0
  206. package/artemis-main/artemis/controllers/types.py +37 -0
  207. package/artemis-main/artemis/controllers/unified_controller.py +649 -0
  208. package/artemis-main/artemis/core/__init__.py +21 -0
  209. package/artemis-main/artemis/core/diagnostics/__init__.py +90 -0
  210. package/artemis-main/artemis/core/diagnostics/adb_keys.py +270 -0
  211. package/artemis-main/artemis/core/diagnostics/adb_server_connection.py +330 -0
  212. package/artemis-main/artemis/core/diagnostics/device_smoke.py +364 -0
  213. package/artemis-main/artemis/core/diagnostics/emulator_manager.py +501 -0
  214. package/artemis-main/artemis/core/diagnostics/engine.py +438 -0
  215. package/artemis-main/artemis/core/diagnostics/hierarchy_parity.py +216 -0
  216. package/artemis-main/artemis/core/diagnostics/probes/adb_probe.py +887 -0
  217. package/artemis-main/artemis/core/diagnostics/probes/base.py +45 -0
  218. package/artemis-main/artemis/core/diagnostics/probes/credentials_probe.py +296 -0
  219. package/artemis-main/artemis/core/diagnostics/probes/host_probe.py +427 -0
  220. package/artemis-main/artemis/core/diagnostics/probes/runtime_probe.py +169 -0
  221. package/artemis-main/artemis/core/diagnostics/probes/toolchain_probe.py +139 -0
  222. package/artemis-main/artemis/core/diagnostics/readiness.py +83 -0
  223. package/artemis-main/artemis/core/diagnostics/schema.py +118 -0
  224. package/artemis-main/artemis/core/tool_declaration.py +43 -0
  225. package/artemis-main/artemis/core/tool_failure.py +56 -0
  226. package/artemis-main/artemis/data_engine/context_vars.py +24 -0
  227. package/artemis-main/artemis/data_engine/engine.py +1615 -0
  228. package/artemis-main/artemis/data_engine/history_reader.py +142 -0
  229. package/artemis-main/artemis/data_engine/models.py +131 -0
  230. package/artemis-main/artemis/data_engine/storage.py +1245 -0
  231. package/artemis-main/artemis/data_engine/trace.py +727 -0
  232. package/artemis-main/artemis/drivers/__init__.py +48 -0
  233. package/artemis-main/artemis/drivers/android/adb_driver.py +507 -0
  234. package/artemis-main/artemis/drivers/android/hierarchy.py +64 -0
  235. package/artemis-main/artemis/drivers/android/input_ime.py +120 -0
  236. package/artemis-main/artemis/drivers/android/recorder.py +57 -0
  237. package/artemis-main/artemis/drivers/base.py +290 -0
  238. package/artemis-main/artemis/drivers/cloud/cloud_driver.py +117 -0
  239. package/artemis-main/artemis/drivers/factory.py +80 -0
  240. package/artemis-main/artemis/drivers/mock/mock_driver.py +198 -0
  241. package/artemis-main/artemis/drivers/types.py +67 -0
  242. package/artemis-main/artemis/graph/checkpoints.py +1243 -0
  243. package/artemis-main/artemis/graph/graph.py +1055 -0
  244. package/artemis-main/artemis/graph/perception.py +234 -0
  245. package/artemis-main/artemis/graph/state.py +151 -0
  246. package/artemis-main/artemis/graph/visibility.py +238 -0
  247. package/artemis-main/artemis/interfaces/cli/__init__.py +19 -0
  248. package/artemis-main/artemis/interfaces/cli/commands/__init__.py +15 -0
  249. package/artemis-main/artemis/interfaces/cli/commands/batch.py +278 -0
  250. package/artemis-main/artemis/interfaces/cli/commands/doctor.py +468 -0
  251. package/artemis-main/artemis/interfaces/cli/commands/helper.py +226 -0
  252. package/artemis-main/artemis/interfaces/cli/commands/init.py +193 -0
  253. package/artemis-main/artemis/interfaces/cli/commands/mcp.py +862 -0
  254. package/artemis-main/artemis/interfaces/cli/commands/run.py +463 -0
  255. package/artemis-main/artemis/interfaces/cli/commands/server.py +151 -0
  256. package/artemis-main/artemis/interfaces/cli/commands/server_lifecycle.py +328 -0
  257. package/artemis-main/artemis/interfaces/cli/commands/trace.py +111 -0
  258. package/artemis-main/artemis/interfaces/cli/commands/ui.py +404 -0
  259. package/artemis-main/artemis/interfaces/cli/main.py +109 -0
  260. package/artemis-main/artemis/interfaces/sdk/__init__.py +31 -0
  261. package/artemis-main/artemis/interfaces/sdk/client.py +19 -0
  262. package/artemis-main/artemis/interfaces/sdk/task.py +72 -0
  263. package/artemis-main/artemis/llm/__init__.py +25 -0
  264. package/artemis-main/artemis/llm/fake_model.py +179 -0
  265. package/artemis-main/artemis/llm/google/__init__.py +57 -0
  266. package/artemis-main/artemis/llm/google/provider.py +185 -0
  267. package/artemis-main/artemis/llm/google/usage.py +148 -0
  268. package/artemis-main/artemis/llm/parser.py +32 -0
  269. package/artemis-main/artemis/llm/reliability.py +267 -0
  270. package/artemis-main/artemis/llm/router.py +389 -0
  271. package/artemis-main/artemis/llm/schemas.py +36 -0
  272. package/artemis-main/artemis/llm/structured.py +196 -0
  273. package/artemis-main/artemis/main.py +58 -0
  274. package/artemis-main/artemis/mcp/__init__.py +20 -0
  275. package/artemis-main/artemis/mcp/action_executor.py +760 -0
  276. package/artemis-main/artemis/mcp/action_manifest.py +320 -0
  277. package/artemis-main/artemis/mcp/action_names.py +172 -0
  278. package/artemis-main/artemis/mcp/action_server.py +202 -0
  279. package/artemis-main/artemis/mcp/action_session.py +245 -0
  280. package/artemis-main/artemis/mcp/action_specs.py +868 -0
  281. package/artemis-main/artemis/mcp/action_types.py +86 -0
  282. package/artemis-main/artemis/mcp/actuators/__init__.py +21 -0
  283. package/artemis-main/artemis/mcp/actuators/adb.py +414 -0
  284. package/artemis-main/artemis/mcp/actuators/base.py +113 -0
  285. package/artemis-main/artemis/mcp/actuators/mock.py +70 -0
  286. package/artemis-main/artemis/mcp/adb_server.py +490 -0
  287. package/artemis-main/artemis/mcp/observation.py +133 -0
  288. package/artemis-main/artemis/mcp/xml_search_server.py +297 -0
  289. package/artemis-main/artemis/memory/__init__.py +90 -0
  290. package/artemis-main/artemis/memory/chunking.py +1843 -0
  291. package/artemis-main/artemis/memory/context_policy.py +226 -0
  292. package/artemis-main/artemis/memory/step_capsule.md +73 -0
  293. package/artemis-main/artemis/memory/step_memory.py +281 -0
  294. package/artemis-main/artemis/memory/transcript.py +798 -0
  295. package/artemis-main/artemis/platform/__init__.py +61 -0
  296. package/artemis-main/artemis/platform/base.py +104 -0
  297. package/artemis-main/artemis/platform/darwin.py +184 -0
  298. package/artemis-main/artemis/platform/linux.py +207 -0
  299. package/artemis-main/artemis/platform/windows.py +193 -0
  300. package/artemis-main/artemis/resources/__init__.py +46 -0
  301. package/artemis-main/artemis/runtime/__init__.py +108 -0
  302. package/artemis-main/artemis/runtime/adb_endpoint.py +173 -0
  303. package/artemis-main/artemis/runtime/awake_lease.py +283 -0
  304. package/artemis-main/artemis/runtime/awake_service.py +343 -0
  305. package/artemis-main/artemis/runtime/cancel_requests.py +254 -0
  306. package/artemis-main/artemis/runtime/daemon_client.py +422 -0
  307. package/artemis-main/artemis/runtime/device_lock.py +1013 -0
  308. package/artemis-main/artemis/runtime/device_pool.py +478 -0
  309. package/artemis-main/artemis/runtime/helper_manager.py +1030 -0
  310. package/artemis-main/artemis/runtime/process_probe.py +98 -0
  311. package/artemis-main/artemis/runtime/server_lifecycle.py +435 -0
  312. package/artemis-main/artemis/runtime/supervisor.py +121 -0
  313. package/artemis-main/artemis/runtime/trace_store.py +375 -0
  314. package/artemis-main/artemis/sdk/__init__.py +26 -0
  315. package/artemis-main/artemis/sdk/agent.py +485 -0
  316. package/artemis-main/artemis/sdk/builders/__init__.py +29 -0
  317. package/artemis-main/artemis/sdk/builders/agent_config_builder.py +474 -0
  318. package/artemis-main/artemis/sdk/constants.py +15 -0
  319. package/artemis-main/artemis/sdk/run_outcome.py +58 -0
  320. package/artemis-main/artemis/sdk/types/__init__.py +61 -0
  321. package/artemis-main/artemis/sdk/types/agent.py +89 -0
  322. package/artemis-main/artemis/sdk/utils.py +28 -0
  323. package/artemis-main/artemis/services/llm.py +1040 -0
  324. package/artemis-main/artemis/services/token_meter.py +267 -0
  325. package/artemis-main/artemis/telemetry/__init__.py +35 -0
  326. package/artemis-main/artemis/telemetry/models.py +64 -0
  327. package/artemis-main/artemis/telemetry/replay.py +34 -0
  328. package/artemis-main/artemis/telemetry/storage/json_storage.py +47 -0
  329. package/artemis-main/artemis/telemetry/tracer.py +85 -0
  330. package/artemis-main/artemis/toolchain/__init__.py +52 -0
  331. package/artemis-main/artemis/toolchain/descriptors.py +92 -0
  332. package/artemis-main/artemis/toolchain/resolver.py +185 -0
  333. package/artemis-main/artemis/tools/base.py +232 -0
  334. package/artemis-main/artemis/tools/command_tool.py +1240 -0
  335. package/artemis-main/artemis/tools/committee_prompts.json +5 -0
  336. package/artemis-main/artemis/tools/committee_tool.py +422 -0
  337. package/artemis-main/artemis/tools/diagnoser_submit_answer_tool.py +87 -0
  338. package/artemis-main/artemis/tools/diagnostic_tool.py +120 -0
  339. package/artemis-main/artemis/tools/explorer_tool.py +581 -0
  340. package/artemis-main/artemis/tools/history/__init__.py +398 -0
  341. package/artemis-main/artemis/tools/history/replay.py +120 -0
  342. package/artemis-main/artemis/tools/history/screenshot.py +139 -0
  343. package/artemis-main/artemis/tools/history/search.py +563 -0
  344. package/artemis-main/artemis/tools/image_processor_tool.py +124 -0
  345. package/artemis-main/artemis/tools/index.py +47 -0
  346. package/artemis-main/artemis/tools/log_tool.py +108 -0
  347. package/artemis-main/artemis/tools/mobile/launch_app.py +132 -0
  348. package/artemis-main/artemis/tools/mobile/log_utils.py +138 -0
  349. package/artemis-main/artemis/tools/mobile/ocr.py +202 -0
  350. package/artemis-main/artemis/tools/mobile/read_hierarchy.py +108 -0
  351. package/artemis-main/artemis/tools/mobile/read_logs.py +116 -0
  352. package/artemis-main/artemis/tools/mobile/search_logs.py +260 -0
  353. package/artemis-main/artemis/tools/object_detection_tool.py +244 -0
  354. package/artemis-main/artemis/tools/scratchpad.py +819 -0
  355. package/artemis-main/artemis/tools/system/adb_shell.py +31 -0
  356. package/artemis-main/artemis/tools/tool_wrapper.py +270 -0
  357. package/artemis-main/artemis/tools/video_tool.py +261 -0
  358. package/artemis-main/artemis/tools/wait_tool.py +97 -0
  359. package/artemis-main/artemis/utils/app_launch_utils.py +207 -0
  360. package/artemis-main/artemis/utils/coordinates.py +606 -0
  361. package/artemis-main/artemis/utils/credentials_validator.py +201 -0
  362. package/artemis-main/artemis/utils/cv_canvas.py +189 -0
  363. package/artemis-main/artemis/utils/cython_compat.py +45 -0
  364. package/artemis-main/artemis/utils/element_hit_test.py +80 -0
  365. package/artemis-main/artemis/utils/image_diff.py +335 -0
  366. package/artemis-main/artemis/utils/image_hash.py +80 -0
  367. package/artemis-main/artemis/utils/notes.py +470 -0
  368. package/artemis-main/artemis/utils/ocr_api.py +105 -0
  369. package/artemis-main/artemis/utils/ocr_xml_fusion.py +318 -0
  370. package/artemis-main/artemis/utils/plan_grammar.py +513 -0
  371. package/artemis-main/artemis/utils/python_executor.py +213 -0
  372. package/artemis-main/artemis/utils/startup_progress.py +60 -0
  373. package/artemis-main/artemis/utils/task_tree.py +1174 -0
  374. package/artemis-main/artemis/utils/text.py +37 -0
  375. package/artemis-main/artemis/utils/ui_filter.py +651 -0
  376. package/artemis-main/artemis/utils/video.py +685 -0
  377. package/artemis-main/artemis/utils/visualization.py +802 -0
  378. package/artemis-main/config/artemis.example.jsonc +12 -0
  379. package/artemis-main/mcp_server/README.md +111 -0
  380. package/artemis-main/mcp_server/__init__.py +21 -0
  381. package/artemis-main/mcp_server/__main__.py +20 -0
  382. package/artemis-main/mcp_server/background/__init__.py +15 -0
  383. package/artemis-main/mcp_server/background/task_runner.py +453 -0
  384. package/artemis-main/mcp_server/base.py +49 -0
  385. package/artemis-main/mcp_server/notifiers/__init__.py +74 -0
  386. package/artemis-main/mcp_server/notifiers/agentapi.py +289 -0
  387. package/artemis-main/mcp_server/notifiers/base.py +45 -0
  388. package/artemis-main/mcp_server/notifiers/composite.py +87 -0
  389. package/artemis-main/mcp_server/notifiers/desktop.py +108 -0
  390. package/artemis-main/mcp_server/notifiers/file.py +69 -0
  391. package/artemis-main/mcp_server/notifiers/script.py +90 -0
  392. package/artemis-main/mcp_server/notifiers/webhook.py +87 -0
  393. package/artemis-main/mcp_server/rules.md +68 -0
  394. package/artemis-main/mcp_server/server.py +106 -0
  395. package/artemis-main/mcp_server/tools/__init__.py +29 -0
  396. package/artemis-main/mcp_server/tools/device_state.py +107 -0
  397. package/artemis-main/mcp_server/tools/diagnose.py +1249 -0
  398. package/artemis-main/mcp_server/tools/inspect_trace.py +394 -0
  399. package/artemis-main/mcp_server/tools/task_manager.py +627 -0
  400. package/artemis-main/mcp_server/tools/task_runner.py +548 -0
  401. package/artemis-main/mcp_server/utils/__init__.py +15 -0
  402. package/artemis-main/mcp_server/utils/device_utils.py +180 -0
  403. package/artemis-main/mcp_server/utils/env_utils.py +65 -0
  404. package/artemis-main/packages/artemis-accessibility-helper/ArtemisAccessibilityHelper.apk +0 -0
  405. package/artemis-main/packages/artemis-accessibility-helper/app/build.gradle.kts +35 -0
  406. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/AndroidManifest.xml +49 -0
  407. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/A11yNode.java +274 -0
  408. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/ArtemisAccessibilityService.java +227 -0
  409. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/CommandServer.java +463 -0
  410. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/DisplayUtils.java +111 -0
  411. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/GestureController.java +232 -0
  412. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/HierarchyDumper.java +832 -0
  413. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/TokenReceiver.java +36 -0
  414. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/TokenStore.java +38 -0
  415. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/XmlUtils.java +102 -0
  416. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/res/values/strings.xml +4 -0
  417. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/res/xml/accessibility_service_config.xml +10 -0
  418. package/artemis-main/packages/artemis-accessibility-helper/build.gradle.kts +3 -0
  419. package/artemis-main/packages/artemis-accessibility-helper/build_apk.sh +215 -0
  420. package/artemis-main/packages/artemis-accessibility-helper/debug.keystore +0 -0
  421. package/artemis-main/packages/artemis-accessibility-helper/gradle.properties +2 -0
  422. package/artemis-main/packages/artemis-accessibility-helper/helper_manifest.json +7 -0
  423. package/artemis-main/packages/artemis-accessibility-helper/settings.gradle.kts +17 -0
  424. package/artemis-main/packages/artemis-client/DESIGN.md +89 -0
  425. package/artemis-main/packages/artemis-client/README.md +112 -0
  426. package/artemis-main/packages/artemis-client/examples/basic.py +30 -0
  427. package/artemis-main/packages/artemis-client/pyproject.toml +36 -0
  428. package/artemis-main/packages/artemis-client/src/artemis_client/__init__.py +45 -0
  429. package/artemis-main/packages/artemis-client/src/artemis_client/client.py +401 -0
  430. package/artemis-main/packages/artemis-client/src/artemis_client/errors.py +66 -0
  431. package/artemis-main/packages/artemis-client/src/artemis_client/models.py +212 -0
  432. package/artemis-main/packages/artemis-client/src/artemis_client/py.typed +1 -0
  433. package/artemis-main/packages/artemis-client/src/artemis_client/transport.py +134 -0
  434. package/artemis-main/packages/artemis-client/tests/test_client.py +289 -0
  435. package/artemis-main/packages/artemis-client/tests/test_transport.py +85 -0
  436. package/artemis-main/pyproject.toml +259 -0
  437. package/artemis-main/pyright-core.json +17 -0
  438. package/artemis-main/scripts/check_dependency_sources.py +68 -0
  439. package/artemis-main/scripts/install_deps.ps1 +520 -0
  440. package/artemis-main/scripts/install_deps.sh +533 -0
  441. package/artemis-main/scripts/quality_ratchet.py +97 -0
  442. package/artemis-main/scripts/real_device_video_acceptance.py +483 -0
  443. package/artemis-main/scripts/run_edge_case_part.py +327 -0
  444. package/artemis-main/scripts/run_ui.py +242 -0
  445. package/artemis-main/scripts/start.ps1 +500 -0
  446. package/artemis-main/scripts/test_all_edge_cases.py +269 -0
  447. package/artemis-main/scripts/verify_concurrency_and_devices.py +385 -0
  448. package/artemis-main/setup.py +164 -0
  449. package/artemis-main/start.bat +35 -0
  450. package/artemis-main/start.sh +423 -0
  451. package/artemis-main/third_party/mobile_use/LICENSE +190 -0
  452. package/artemis-main/third_party/mobile_use/METADATA +27 -0
  453. package/artemis-main/third_party/mobile_use/NOTICE +12 -0
  454. package/artemis-main/third_party/mobile_use/__init__.py +19 -0
  455. package/artemis-main/third_party/mobile_use/agents/__init__.py +13 -0
  456. package/artemis-main/third_party/mobile_use/agents/hopper/__init__.py +13 -0
  457. package/artemis-main/third_party/mobile_use/agents/hopper/hopper.md +22 -0
  458. package/artemis-main/third_party/mobile_use/agents/hopper/hopper.py +80 -0
  459. package/artemis-main/third_party/mobile_use/clients/__init__.py +13 -0
  460. package/artemis-main/third_party/mobile_use/clients/ui_automator_client.py +482 -0
  461. package/artemis-main/third_party/mobile_use/config/__init__.py +13 -0
  462. package/artemis-main/third_party/mobile_use/config/llm.py +183 -0
  463. package/artemis-main/third_party/mobile_use/config/output.py +184 -0
  464. package/artemis-main/third_party/mobile_use/context.py +119 -0
  465. package/artemis-main/third_party/mobile_use/controllers/__init__.py +13 -0
  466. package/artemis-main/third_party/mobile_use/controllers/platform_specific_commands_controller.py +151 -0
  467. package/artemis-main/third_party/mobile_use/controllers/types.py +92 -0
  468. package/artemis-main/third_party/mobile_use/controllers/unified_controller.py +183 -0
  469. package/artemis-main/third_party/mobile_use/graph.py +54 -0
  470. package/artemis-main/third_party/mobile_use/llm_service.py +193 -0
  471. package/artemis-main/third_party/mobile_use/main.py +130 -0
  472. package/artemis-main/third_party/mobile_use/sdk/__init__.py +13 -0
  473. package/artemis-main/third_party/mobile_use/sdk/agent.py +1060 -0
  474. package/artemis-main/third_party/mobile_use/sdk/builders/__init__.py +13 -0
  475. package/artemis-main/third_party/mobile_use/sdk/builders/agent_config_builder.py +225 -0
  476. package/artemis-main/third_party/mobile_use/sdk/builders/index.py +33 -0
  477. package/artemis-main/third_party/mobile_use/sdk/builders/task_request_builder.py +243 -0
  478. package/artemis-main/third_party/mobile_use/sdk/types/__init__.py +13 -0
  479. package/artemis-main/third_party/mobile_use/sdk/types/agent.py +98 -0
  480. package/artemis-main/third_party/mobile_use/sdk/types/exceptions.py +109 -0
  481. package/artemis-main/third_party/mobile_use/sdk/types/task.py +234 -0
  482. package/artemis-main/third_party/mobile_use/testing.py +59 -0
  483. package/artemis-main/third_party/mobile_use/tools/__init__.py +13 -0
  484. package/artemis-main/third_party/mobile_use/tools/index.py +44 -0
  485. package/artemis-main/third_party/mobile_use/tools/mobile/__init__.py +13 -0
  486. package/artemis-main/third_party/mobile_use/tools/mobile/launch_app.py +89 -0
  487. package/artemis-main/third_party/mobile_use/tools/tool_wrapper.py +52 -0
  488. package/artemis-main/third_party/mobile_use/utils/__init__.py +13 -0
  489. package/artemis-main/third_party/mobile_use/utils/app_launch_utils.py +217 -0
  490. package/artemis-main/third_party/mobile_use/utils/cli_helpers.py +60 -0
  491. package/artemis-main/third_party/mobile_use/utils/decorators.py +158 -0
  492. package/artemis-main/third_party/mobile_use/utils/file.py +31 -0
  493. package/artemis-main/third_party/mobile_use/utils/logger.py +216 -0
  494. package/artemis-main/third_party/mobile_use/utils/media.py +213 -0
  495. package/artemis-main/third_party/mobile_use/utils/shell_utils.py +39 -0
  496. package/artemis-main/third_party/mobile_use/utils/video.py +398 -0
  497. package/artemis-main/uv.lock +4908 -0
  498. package/bin/android-midscene-automation.js +1 -5
  499. package/bin/artemis-config.mjs +15 -0
  500. package/bin/artemis-embedded.py +56 -0
  501. package/bin/start-artemis.mjs +30 -0
  502. package/bin/start-dev.mjs +112 -0
  503. package/bin/test_artemis_embedded.py +44 -0
  504. package/package.json +4 -2
  505. package/server/artemis.ts +34 -0
  506. package/server/http-api.ts +12 -0
  507. package/src/App.vue +10 -5
  508. package/src/api.ts +5 -0
  509. package/src/appium-recorder/AppiumPage.vue +1 -1
  510. package/src/appium-recorder/components/ComponentInspector.vue +1 -1
  511. package/src/appium-recorder/components/FlowNodeSearch.vue +3 -3
  512. package/src/appium-recorder/components/ImageCheckDialog.vue +3 -3
  513. package/src/appium-recorder/components/RecorderWorkspace.vue +4 -4
  514. package/src/appium-recorder/flow-appearance.ts +4 -4
  515. package/src/components/AppSidebar.vue +8 -7
  516. package/src/components/MidsceneMaintenanceNotice.vue +17 -0
  517. package/src/components/analytics/AnalysisCharts.vue +4 -4
  518. package/src/components/analytics/ReportAnalysis.vue +1 -1
  519. package/src/components/generator/TestCaseFileUpload.vue +1 -1
  520. package/src/components/help/HelpCenter.vue +56 -37
  521. package/src/pages/AiGeneratorPage.vue +1 -1
  522. package/src/pages/ArtemisPage.vue +72 -0
  523. package/src/pages/ConfigPage.vue +18 -19
  524. package/src/style.css +340 -121
  525. package/src/types.ts +1 -1
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Copyright 2026 Google LLC
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ .floating-nav-switcher {
18
+ position: fixed;
19
+ top: 12px;
20
+ left: 24px;
21
+ z-index: 1100;
22
+ height: 36px;
23
+ box-sizing: border-box;
24
+ display: inline-flex;
25
+ align-items: center;
26
+ background: rgba(255, 255, 255, 0.72);
27
+ backdrop-filter: blur(16px) saturate(180%);
28
+ -webkit-backdrop-filter: blur(16px) saturate(180%);
29
+ border: 1px solid rgba(226, 232, 240, 0.85);
30
+ border-radius: 18px;
31
+ padding: 3px;
32
+ gap: 3px;
33
+ box-shadow: 0 2px 8px -1px rgba(15, 23, 42, 0.06), 0 1px 2px rgba(0, 0, 0, 0.02), inset 0 1px 1px rgba(255, 255, 255, 0.9);
34
+ user-select: none;
35
+ font-family: 'Google Sans', 'Google Sans Text', 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
36
+ transition: background 0.25s cubic-bezier(0.4, 0, 0.2, 1),
37
+ box-shadow 0.25s cubic-bezier(0.4, 0, 0.2, 1),
38
+ border-color 0.25s cubic-bezier(0.4, 0, 0.2, 1);
39
+
40
+ &:hover {
41
+ background: rgba(255, 255, 255, 0.85);
42
+ box-shadow: 0 4px 14px -2px rgba(15, 23, 42, 0.1), 0 2px 4px rgba(0, 0, 0, 0.03), inset 0 1px 1px rgba(255, 255, 255, 1);
43
+ border-color: rgba(203, 213, 225, 0.9);
44
+ }
45
+
46
+ .nav-tab-btn {
47
+ display: inline-flex;
48
+ align-items: center;
49
+ justify-content: center;
50
+ gap: 6px;
51
+ height: 30px;
52
+ padding: 0 14px;
53
+ box-sizing: border-box;
54
+ border-radius: 15px;
55
+ font-size: 13px;
56
+ font-weight: 600;
57
+ color: #475569;
58
+ text-decoration: none;
59
+ cursor: pointer;
60
+ line-height: 1;
61
+ transition: background 0.2s cubic-bezier(0.4, 0, 0.2, 1),
62
+ color 0.2s cubic-bezier(0.4, 0, 0.2, 1),
63
+ box-shadow 0.2s cubic-bezier(0.4, 0, 0.2, 1),
64
+ transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);
65
+ outline: none;
66
+ white-space: nowrap;
67
+
68
+ .tab-icon {
69
+ font-size: 16px;
70
+ width: 16px;
71
+ height: 16px;
72
+ display: inline-flex;
73
+ align-items: center;
74
+ justify-content: center;
75
+ color: #64748b;
76
+ transition: color 0.15s ease, transform 0.15s ease;
77
+ }
78
+
79
+ .tab-label {
80
+ color: #475569;
81
+ transition: color 0.15s ease;
82
+ }
83
+
84
+ &:hover:not(.active) {
85
+ background: rgba(255, 255, 255, 0.7);
86
+ color: #0f172a;
87
+
88
+ .tab-icon {
89
+ color: #1a73e8;
90
+ }
91
+ .tab-label {
92
+ color: #0f172a;
93
+ }
94
+ }
95
+
96
+ &.active {
97
+ background: rgba(26, 115, 232, 0.95) !important;
98
+ backdrop-filter: blur(12px);
99
+ -webkit-backdrop-filter: blur(12px);
100
+ color: #ffffff !important;
101
+ font-weight: 700;
102
+ box-shadow: 0 2px 8px rgba(26, 115, 232, 0.35), inset 0 1px 1px rgba(255, 255, 255, 0.35);
103
+
104
+ .tab-icon,
105
+ .tab-label,
106
+ span {
107
+ color: #ffffff !important;
108
+ }
109
+ }
110
+
111
+ &:active {
112
+ transform: scale(0.97);
113
+ }
114
+ }
115
+ }
@@ -0,0 +1,52 @@
1
+ import { TranslatePipe } from '../../core/i18n/translate.pipe';
2
+ /**
3
+ * Copyright 2026 Google LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ import { Component, ChangeDetectionStrategy } from '@angular/core';
19
+
20
+ import { RouterLink, RouterLinkActive } from '@angular/router';
21
+
22
+ @Component({
23
+ selector: 'app-nav-switcher',
24
+ standalone: true,
25
+ imports: [TranslatePipe, RouterLink, RouterLinkActive],
26
+ template: `
27
+ <nav class="floating-nav-switcher" [attr.aria-label]="'Main Navigation' | t">
28
+ <a
29
+ routerLink="/"
30
+ routerLinkActive="active"
31
+ [routerLinkActiveOptions]="{exact: true}"
32
+ class="nav-tab-btn"
33
+ [title]="'Return to Home Launcher to start a new task' | t"
34
+ >
35
+ <span class="material-symbols-outlined tab-icon">add_task</span>
36
+ <span class="tab-label">{{ 'New / Home' | t }}</span>
37
+ </a>
38
+ <a
39
+ routerLink="/workspace"
40
+ routerLinkActive="active"
41
+ class="nav-tab-btn"
42
+ [title]="'Open Workspace' | t"
43
+ >
44
+ <span class="material-symbols-outlined tab-icon">space_dashboard</span>
45
+ <span class="tab-label">{{ 'Workspace' | t }}</span>
46
+ </a>
47
+ </nav>
48
+ `,
49
+ changeDetection: ChangeDetectionStrategy.Eager,
50
+ styleUrls: ['./nav-switcher.component.scss']
51
+ })
52
+ export class NavSwitcherComponent {}
@@ -0,0 +1,334 @@
1
+ /**
2
+ * Copyright 2026 Google LLC
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ export interface AppReference {
18
+ name: string;
19
+ icon: string;
20
+ pkg?: string;
21
+ category?: string;
22
+ }
23
+
24
+ export type SuggestionCategory =
25
+ | 'all'
26
+ | 'flash'
27
+ | 'pro'
28
+ | 'cross_app'
29
+ | 'monitor';
30
+
31
+ export interface SmartSuggestion {
32
+ id: string;
33
+ title: string;
34
+ description: string;
35
+ goal: string;
36
+ profile: 'flash' | 'pro';
37
+ category: 'flash' | 'pro' | 'cross_app' | 'monitor';
38
+ tag: string;
39
+ apps: AppReference[];
40
+ requiredPackages?: string[];
41
+ matchMode?: 'any' | 'all';
42
+ priority?: number;
43
+ }
44
+
45
+ /**
46
+ * Recognized Android App Package Registry
47
+ */
48
+ export const APP_REGISTRY: Record<string, AppReference> = {
49
+ // Google Suite & System
50
+ 'com.google.android.apps.maps': { name: 'Maps', icon: 'explore', pkg: 'com.google.android.apps.maps', category: 'navigation' },
51
+ 'com.google.android.gm': { name: 'Gmail', icon: 'mail', pkg: 'com.google.android.gm', category: 'productivity' },
52
+ 'com.android.chrome': { name: 'Chrome', icon: 'public', pkg: 'com.android.chrome', category: 'browser' },
53
+ 'com.google.android.youtube': { name: 'YouTube', icon: 'smart_display', pkg: 'com.google.android.youtube', category: 'entertainment' },
54
+ 'com.android.settings': { name: 'Settings', icon: 'settings', pkg: 'com.android.settings', category: 'system' },
55
+ 'com.google.android.deskclock': { name: 'Clock', icon: 'timer', pkg: 'com.google.android.deskclock', category: 'utility' },
56
+ 'com.android.deskclock': { name: 'Clock', icon: 'timer', pkg: 'com.android.deskclock', category: 'utility' },
57
+ 'com.google.android.calculator': { name: 'Calculator', icon: 'calculate', pkg: 'com.google.android.calculator', category: 'utility' },
58
+ 'com.android.calculator2': { name: 'Calculator', icon: 'calculate', pkg: 'com.android.calculator2', category: 'utility' },
59
+ 'com.google.android.apps.photos': { name: 'Photos', icon: 'photo_library', pkg: 'com.google.android.apps.photos', category: 'media' },
60
+ 'com.google.android.calendar': { name: 'Calendar', icon: 'calendar_month', pkg: 'com.google.android.calendar', category: 'productivity' },
61
+ 'com.google.android.keep': { name: 'Keep Notes', icon: 'note_alt', pkg: 'com.google.android.keep', category: 'productivity' },
62
+ 'com.android.vending': { name: 'Play Store', icon: 'storefront', pkg: 'com.android.vending', category: 'tools' },
63
+ 'com.google.android.apps.messaging': { name: 'Messages', icon: 'chat', pkg: 'com.google.android.apps.messaging', category: 'communication' },
64
+
65
+ // Popular Ecosystem Apps
66
+ 'com.tencent.mm': { name: 'WeChat', icon: 'forum', pkg: 'com.tencent.mm', category: 'social' },
67
+ 'com.xingin.xhs': { name: 'Xiaohongshu', icon: 'auto_stories', pkg: 'com.xingin.xhs', category: 'social' },
68
+ 'com.sankuai.meituan': { name: 'Meituan', icon: 'restaurant', pkg: 'com.sankuai.meituan', category: 'lifestyle' },
69
+ 'com.dianping.v1': { name: 'Dianping', icon: 'star', pkg: 'com.dianping.v1', category: 'lifestyle' },
70
+ 'tv.danmaku.bili': { name: 'Bilibili', icon: 'video_library', pkg: 'tv.danmaku.bili', category: 'entertainment' },
71
+ 'com.eg.android.AlipayGphone': { name: 'Alipay', icon: 'account_balance_wallet', pkg: 'com.eg.android.AlipayGphone', category: 'finance' },
72
+ 'com.netease.cloudmusic': { name: 'NetEase Music', icon: 'headphones', pkg: 'com.netease.cloudmusic', category: 'entertainment' },
73
+ 'com.spotify.music': { name: 'Spotify', icon: 'music_note', pkg: 'com.spotify.music', category: 'entertainment' }
74
+ };
75
+
76
+ /**
77
+ * Curated, clean task preset library (1~2 representative tasks per common app)
78
+ */
79
+ export const SMART_TASK_LIBRARY: SmartSuggestion[] = [
80
+ // 1. Google Maps
81
+ {
82
+ id: 'maps_coffee',
83
+ title: 'Find Specialty Coffee',
84
+ description: 'Search nearby top-rated cafes in Google Maps',
85
+ goal: 'Open Google Maps, search for top-rated specialty coffee shops nearby, and view the top result details.',
86
+ profile: 'flash',
87
+ category: 'flash',
88
+ tag: 'Maps',
89
+ apps: [{ name: 'Maps', icon: 'explore', pkg: 'com.google.android.apps.maps' }],
90
+ requiredPackages: ['com.google.android.apps.maps'],
91
+ priority: 95
92
+ },
93
+ {
94
+ id: 'pro_commute_share',
95
+ title: 'Commute ETA & Message Draft',
96
+ description: 'Check transit time on Maps and draft arrival ETA in Messages',
97
+ goal: 'Open Google Maps to check commute time to the International Airport, calculate arrival time, then open Messages and draft an ETA text message.',
98
+ profile: 'pro',
99
+ category: 'cross_app',
100
+ tag: 'Maps + Messages',
101
+ apps: [
102
+ { name: 'Maps', icon: 'explore', pkg: 'com.google.android.apps.maps' },
103
+ { name: 'Messages', icon: 'chat', pkg: 'com.google.android.apps.messaging' }
104
+ ],
105
+ requiredPackages: ['com.google.android.apps.maps', 'com.google.android.apps.messaging'],
106
+ matchMode: 'all',
107
+ priority: 92
108
+ },
109
+
110
+ // 2. Gmail
111
+ {
112
+ id: 'gmail_receipts',
113
+ title: 'Search Order Receipts',
114
+ description: 'Find recent flight or delivery confirmation emails in Gmail',
115
+ goal: 'Open Gmail and search for recent flight or package delivery confirmation emails.',
116
+ profile: 'flash',
117
+ category: 'flash',
118
+ tag: 'Gmail',
119
+ apps: [{ name: 'Gmail', icon: 'mail', pkg: 'com.google.android.gm' }],
120
+ requiredPackages: ['com.google.android.gm'],
121
+ priority: 90
122
+ },
123
+ {
124
+ id: 'pro_email_to_calendar',
125
+ title: 'Email Itinerary to Calendar',
126
+ description: 'Extract flight or event dates from Gmail and schedule in Calendar',
127
+ goal: 'Open Gmail to find the latest event invitation or itinerary, extract dates and location, then open Google Calendar and create a corresponding calendar event.',
128
+ profile: 'pro',
129
+ category: 'cross_app',
130
+ tag: 'Gmail + Calendar',
131
+ apps: [
132
+ { name: 'Gmail', icon: 'mail', pkg: 'com.google.android.gm' },
133
+ { name: 'Calendar', icon: 'calendar_month', pkg: 'com.google.android.calendar' }
134
+ ],
135
+ requiredPackages: ['com.google.android.gm'],
136
+ priority: 94
137
+ },
138
+
139
+ // 3. Chrome
140
+ {
141
+ id: 'chrome_research',
142
+ title: 'Search AI News Breakthroughs',
143
+ description: 'Search latest multimodal AI developments in Chrome browser',
144
+ goal: 'Open Chrome browser and search for latest breakthroughs in multimodal mobile AI agents.',
145
+ profile: 'flash',
146
+ category: 'flash',
147
+ tag: 'Chrome',
148
+ apps: [{ name: 'Chrome', icon: 'public', pkg: 'com.android.chrome' }],
149
+ requiredPackages: ['com.android.chrome'],
150
+ priority: 88
151
+ },
152
+ {
153
+ id: 'pro_research_keep',
154
+ title: 'Product Research & Notes Note',
155
+ description: 'Compare top 3 headphones on Chrome and record comparison in Keep',
156
+ goal: 'Open Chrome, research top 3 noise-cancelling headphones comparing price and battery life, then write a structured comparison summary note in Keep Notes.',
157
+ profile: 'pro',
158
+ category: 'pro',
159
+ tag: 'Chrome + Keep',
160
+ apps: [
161
+ { name: 'Chrome', icon: 'public', pkg: 'com.android.chrome' },
162
+ { name: 'Keep Notes', icon: 'note_alt', pkg: 'com.google.android.keep' }
163
+ ],
164
+ requiredPackages: ['com.android.chrome'],
165
+ priority: 91
166
+ },
167
+
168
+ // 4. YouTube
169
+ {
170
+ id: 'youtube_lofi',
171
+ title: 'Play Lo-Fi Music Radio',
172
+ description: 'Search and play a Lo-Fi hip hop live stream on YouTube',
173
+ goal: 'Open YouTube, search for "Lofi hip hop beats relaxing radio" and tap on the live stream.',
174
+ profile: 'flash',
175
+ category: 'flash',
176
+ tag: 'YouTube',
177
+ apps: [{ name: 'YouTube', icon: 'smart_display', pkg: 'com.google.android.youtube' }],
178
+ requiredPackages: ['com.google.android.youtube'],
179
+ priority: 85
180
+ },
181
+
182
+ // 5. Settings
183
+ {
184
+ id: 'settings_display_wifi',
185
+ title: 'Dark Mode & Wi-Fi Check',
186
+ description: 'Toggle dark theme and verify network connection in Settings',
187
+ goal: 'Open Settings app, navigate to Display settings, ensure Dark theme is enabled, and check Wi-Fi connection status.',
188
+ profile: 'flash',
189
+ category: 'flash',
190
+ tag: 'Settings',
191
+ apps: [{ name: 'Settings', icon: 'settings', pkg: 'com.android.settings' }],
192
+ requiredPackages: ['com.android.settings'],
193
+ priority: 87
194
+ },
195
+ {
196
+ id: 'pro_settings_qa',
197
+ title: 'Subsystem Health & Crash Probe',
198
+ description: 'Traverse Settings submenus to verify screens and check for crash dialogs',
199
+ goal: 'Explore Settings submenus (Network, Connected devices, Apps, Battery, Storage), verify each screen loads properly without ANR or crash dialogs, and summarize results.',
200
+ profile: 'pro',
201
+ category: 'monitor',
202
+ tag: 'Settings QA',
203
+ apps: [{ name: 'Settings', icon: 'settings', pkg: 'com.android.settings' }],
204
+ requiredPackages: ['com.android.settings'],
205
+ priority: 93
206
+ },
207
+
208
+ // 6. Clock
209
+ {
210
+ id: 'clock_timer',
211
+ title: '25-Min Pomodoro Timer',
212
+ description: 'Start a 25-minute focus countdown timer in Clock app',
213
+ goal: 'Open Clock app, switch to Timer tab, set 25 minutes and start the countdown timer.',
214
+ profile: 'flash',
215
+ category: 'flash',
216
+ tag: 'Clock',
217
+ apps: [{ name: 'Clock', icon: 'timer', pkg: 'com.google.android.deskclock' }],
218
+ requiredPackages: ['com.google.android.deskclock', 'com.android.deskclock'],
219
+ priority: 86
220
+ },
221
+
222
+ // 7. Calculator
223
+ {
224
+ id: 'calc_gratuity',
225
+ title: 'Split Bill & Calculate Tip',
226
+ description: 'Calculate 18% gratuity on $186.40 for 3 people in Calculator',
227
+ goal: 'Open Calculator and calculate 18% tip on a bill of $186.40, then divide by 3 people.',
228
+ profile: 'flash',
229
+ category: 'flash',
230
+ tag: 'Calculator',
231
+ apps: [{ name: 'Calculator', icon: 'calculate', pkg: 'com.google.android.calculator' }],
232
+ requiredPackages: ['com.google.android.calculator', 'com.android.calculator2'],
233
+ priority: 84
234
+ },
235
+
236
+ // 8. Photos
237
+ {
238
+ id: 'photos_inspect',
239
+ title: 'Inspect Recent Screenshot',
240
+ description: 'Open Google Photos and review the latest screenshot taken',
241
+ goal: 'Open Google Photos and view the most recent screenshot in the screenshots album.',
242
+ profile: 'flash',
243
+ category: 'flash',
244
+ tag: 'Photos',
245
+ apps: [{ name: 'Photos', icon: 'photo_library', pkg: 'com.google.android.apps.photos' }],
246
+ requiredPackages: ['com.google.android.apps.photos'],
247
+ priority: 82
248
+ },
249
+
250
+ // 9. WeChat
251
+ {
252
+ id: 'wechat_browse',
253
+ title: 'Check WeChat Messages',
254
+ description: 'Open WeChat and view top recent chat conversations',
255
+ goal: 'Open WeChat and view the top recent chat messages.',
256
+ profile: 'flash',
257
+ category: 'flash',
258
+ tag: 'WeChat',
259
+ apps: [{ name: 'WeChat', icon: 'forum', pkg: 'com.tencent.mm' }],
260
+ requiredPackages: ['com.tencent.mm'],
261
+ priority: 89
262
+ },
263
+ {
264
+ id: 'pro_wechat_to_calendar',
265
+ title: 'WeChat Notice to Calendar',
266
+ description: 'Extract meeting notice from WeChat chat and add to Calendar',
267
+ goal: 'Open WeChat, locate the latest meeting announcement or event message in the top chat, extract the time and topic, then open Calendar and schedule an event.',
268
+ profile: 'pro',
269
+ category: 'cross_app',
270
+ tag: 'WeChat + Calendar',
271
+ apps: [
272
+ { name: 'WeChat', icon: 'forum', pkg: 'com.tencent.mm' },
273
+ { name: 'Calendar', icon: 'calendar_month', pkg: 'com.google.android.calendar' }
274
+ ],
275
+ requiredPackages: ['com.tencent.mm'],
276
+ priority: 93
277
+ },
278
+
279
+ // 10. Xiaohongshu
280
+ {
281
+ id: 'xhs_coffee_guide',
282
+ title: 'RED Cafe Guide Search',
283
+ description: 'Search trending specialty cafe reviews on Xiaohongshu',
284
+ goal: 'Open Xiaohongshu, search for top-rated specialty coffee shops, and view the top post.',
285
+ profile: 'flash',
286
+ category: 'flash',
287
+ tag: 'Xiaohongshu',
288
+ apps: [{ name: 'Xiaohongshu', icon: 'auto_stories', pkg: 'com.xingin.xhs' }],
289
+ requiredPackages: ['com.xingin.xhs'],
290
+ priority: 87
291
+ },
292
+
293
+ // 11. Meituan / Dianping
294
+ {
295
+ id: 'meituan_ramen_search',
296
+ title: 'Meituan Food Search',
297
+ description: 'Search top-rated Ramen nearby on Meituan or Dianping',
298
+ goal: 'Open Meituan or Dianping, search for top-rated Ramen nearby, and view top restaurant rating.',
299
+ profile: 'flash',
300
+ category: 'flash',
301
+ tag: 'Meituan',
302
+ apps: [{ name: 'Meituan', icon: 'restaurant', pkg: 'com.sankuai.meituan' }],
303
+ requiredPackages: ['com.sankuai.meituan', 'com.dianping.v1'],
304
+ priority: 86
305
+ },
306
+
307
+ // 12. Bilibili
308
+ {
309
+ id: 'bilibili_stream',
310
+ title: 'Bilibili Tech Video',
311
+ description: 'Search and play an AI Agent tutorial video on Bilibili',
312
+ goal: 'Open Bilibili, search for "AI Agent Architecture", and play the top matching video.',
313
+ profile: 'flash',
314
+ category: 'flash',
315
+ tag: 'Bilibili',
316
+ apps: [{ name: 'Bilibili', icon: 'video_library', pkg: 'tv.danmaku.bili' }],
317
+ requiredPackages: ['tv.danmaku.bili'],
318
+ priority: 85
319
+ },
320
+
321
+ // 13. Play Store
322
+ {
323
+ id: 'pro_playstore_review',
324
+ title: 'Play Store App Review Study',
325
+ description: 'Compare top task management apps and ratings on Google Play',
326
+ goal: 'Open Google Play Store, search for top rated task management apps, compare ratings and latest user reviews of the top 2 candidates, and record recommendations.',
327
+ profile: 'pro',
328
+ category: 'pro',
329
+ tag: 'Play Store',
330
+ apps: [{ name: 'Play Store', icon: 'storefront', pkg: 'com.android.vending' }],
331
+ requiredPackages: ['com.android.vending'],
332
+ priority: 88
333
+ }
334
+ ];
@@ -0,0 +1,55 @@
1
+ import { DOCUMENT } from '@angular/common';
2
+ import { TestBed } from '@angular/core/testing';
3
+ import { LanguageService, LANGUAGE_STORAGE_KEY } from './language.service';
4
+ import { TranslatePipe } from './translate.pipe';
5
+
6
+ describe('UI language', () => {
7
+ let storage: Map<string, string>;
8
+ let documentStub: { documentElement: { lang: string }; defaultView: { localStorage: { getItem: jasmine.Spy; setItem: jasmine.Spy } } };
9
+
10
+ beforeEach(() => {
11
+ storage = new Map();
12
+ documentStub = {
13
+ documentElement: { lang: '' },
14
+ defaultView: { localStorage: {
15
+ getItem: jasmine.createSpy().and.callFake((key: string) => storage.get(key) ?? null),
16
+ setItem: jasmine.createSpy().and.callFake((key: string, value: string) => storage.set(key, value))
17
+ } }
18
+ };
19
+ TestBed.configureTestingModule({ providers: [{ provide: DOCUMENT, useValue: documentStub }] });
20
+ });
21
+
22
+ it('defaults to Chinese and switches an existing pipe without changing input text', () => {
23
+ const service = TestBed.inject(LanguageService);
24
+ const pipe = TestBed.runInInjectionContext(() => new TranslatePipe());
25
+ expect(pipe.transform('Recommended Tasks')).toBe('推荐任务');
26
+ service.setLanguage('en');
27
+ expect(pipe.transform('Recommended Tasks')).toBe('Recommended Tasks');
28
+ expect(documentStub.documentElement.lang).toBe('en');
29
+ expect(storage.get(LANGUAGE_STORAGE_KEY)).toBe('en');
30
+ });
31
+
32
+ it('restores the saved English preference', () => {
33
+ storage.set(LANGUAGE_STORAGE_KEY, 'en');
34
+ expect(TestBed.inject(LanguageService).language()).toBe('en');
35
+ });
36
+
37
+ it('works when storage access is blocked', () => {
38
+ documentStub.defaultView.localStorage.getItem.and.throwError('blocked');
39
+ documentStub.defaultView.localStorage.setItem.and.throwError('blocked');
40
+ const service = TestBed.inject(LanguageService);
41
+ expect(service.language()).toBe('zh');
42
+ expect(() => service.setLanguage('en')).not.toThrow();
43
+ expect(service.translate('Workspace')).toBe('Workspace');
44
+ });
45
+
46
+ it('preserves unknown text, identifiers and English whitespace', () => {
47
+ const service = TestBed.inject(LanguageService);
48
+ expect(service.translate('855ef847 / gemini-3.8-flash')).toBe('855ef847 / gemini-3.8-flash');
49
+ expect(service.translate('Custom server error: foo')).toBe('Custom server error: foo');
50
+ expect(service.translate('Python 3.12.14 Ready')).toBe('Python 3.12.14 已就绪');
51
+ expect(service.translate(' Workspace ')).toBe(' 工作区 ');
52
+ service.setLanguage('en');
53
+ expect(service.translate(' Workspace ')).toBe(' Workspace ');
54
+ });
55
+ });
@@ -0,0 +1,61 @@
1
+ import { DOCUMENT } from '@angular/common';
2
+ import { inject, Injectable, signal } from '@angular/core';
3
+ import { ZH_CN } from './zh-cn';
4
+
5
+ export type UiLanguage = 'zh' | 'en';
6
+ export const LANGUAGE_STORAGE_KEY = 'artemis.ui.language';
7
+
8
+ /** UI copy only: task text, model output and device identifiers stay untouched. */
9
+ @Injectable({ providedIn: 'root' })
10
+ export class LanguageService {
11
+ private readonly document = inject(DOCUMENT);
12
+ private readonly current = signal<UiLanguage>(this.readPreference());
13
+ readonly language = this.current.asReadonly();
14
+
15
+ constructor() {
16
+ this.updateDocument();
17
+ }
18
+
19
+ setLanguage(language: UiLanguage): void {
20
+ this.current.set(language);
21
+ this.updateDocument();
22
+ try {
23
+ this.document.defaultView?.localStorage.setItem(LANGUAGE_STORAGE_KEY, language);
24
+ } catch {
25
+ // The switch still works if the browser disallows persistent storage.
26
+ }
27
+ }
28
+
29
+ translate(value: string | null | undefined): string {
30
+ if (!value) return '';
31
+ if (this.language() === 'en') return value;
32
+ const key = value.replace(/\s+/g, ' ').trim();
33
+ const translated = ZH_CN[key];
34
+ if (translated === undefined) {
35
+ // Known diagnostic templates preserve variable identifiers verbatim.
36
+ return value
37
+ .replace(/^Python ([\d.]+) Ready$/, 'Python $1 已就绪')
38
+ .replace(/^Python ([\d.]+) Unsupported$/, 'Python $1 不受支持')
39
+ .replace(/^(\d+) failed$/, '$1 项失败')
40
+ .replace(/^Stop current task: /, '停止当前任务:')
41
+ .replace(/^Stop current task \((.+)\)$/, '停止当前任务($1)')
42
+ .replace(/^ADB connected to (.+), but the Android lock screen is active\. Unlock the device and enter the home screen before running a task\.$/,
43
+ 'ADB 已连接到 $1,但设备处于锁屏状态。请解锁并进入主屏幕后再运行任务。')
44
+ .replace(/^ADB connected to (.+), but Android lock-screen state could not be verified\. Keep the device unlocked on the home screen and check again\.$/,
45
+ 'ADB 已连接到 $1,但无法确认锁屏状态。请保持设备解锁并停留在主屏幕,然后再次检查。');
46
+ }
47
+ return value.match(/^\s*/)?.[0] + translated + value.match(/\s*$/)?.[0];
48
+ }
49
+
50
+ private readPreference(): UiLanguage {
51
+ try {
52
+ return this.document.defaultView?.localStorage.getItem(LANGUAGE_STORAGE_KEY) === 'en' ? 'en' : 'zh';
53
+ } catch {
54
+ return 'zh';
55
+ }
56
+ }
57
+
58
+ private updateDocument(): void {
59
+ this.document.documentElement.lang = this.language() === 'zh' ? 'zh-CN' : 'en';
60
+ }
61
+ }
@@ -0,0 +1,11 @@
1
+ import { inject, Pipe, PipeTransform } from '@angular/core';
2
+ import { LanguageService } from './language.service';
3
+
4
+ @Pipe({ name: 't', standalone: true, pure: false })
5
+ export class TranslatePipe implements PipeTransform {
6
+ private readonly language = inject(LanguageService);
7
+
8
+ transform(value: string | null | undefined): string {
9
+ return this.language.translate(value);
10
+ }
11
+ }