@monkey-mini-app/dsh-mini-app 0.1.0 → 0.1.4

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 (317) hide show
  1. package/README.md +59 -0
  2. package/assets/apps-list.png +0 -0
  3. package/assets/complex-demo-app.png +0 -0
  4. package/assets/demo-app.png +0 -0
  5. package/assets/home.png +0 -0
  6. package/assets/sidebar-mode.png +0 -0
  7. package/lib/client.js +2024 -783
  8. package/lib/index.d.ts +5 -2
  9. package/lib/index.js +29 -16
  10. package/package.json +12 -10
  11. package/skills/monkey-mini-app/SKILL.md +182 -106
  12. package/skills/monkey-mini-app/references/catalog.md +236 -99
  13. package/skills/monkey-mini-app/references/contracts/accordion.md +30 -0
  14. package/skills/monkey-mini-app/references/contracts/activity-feed.md +12 -2
  15. package/skills/monkey-mini-app/references/contracts/alert-dialog.md +38 -0
  16. package/skills/monkey-mini-app/references/contracts/alert.md +38 -0
  17. package/skills/monkey-mini-app/references/contracts/app-shell.md +2 -2
  18. package/skills/monkey-mini-app/references/contracts/aspect-ratio.md +25 -0
  19. package/skills/monkey-mini-app/references/contracts/attachment-gallery.md +12 -2
  20. package/skills/monkey-mini-app/references/contracts/attachment.md +35 -0
  21. package/skills/monkey-mini-app/references/contracts/autocomplete.md +12 -2
  22. package/skills/monkey-mini-app/references/contracts/avatar.md +24 -0
  23. package/skills/monkey-mini-app/references/contracts/badge.md +26 -0
  24. package/skills/monkey-mini-app/references/contracts/breadcrumb.md +33 -0
  25. package/skills/monkey-mini-app/references/contracts/bubble.md +30 -0
  26. package/skills/monkey-mini-app/references/contracts/button-group.md +36 -0
  27. package/skills/monkey-mini-app/references/contracts/button.md +26 -0
  28. package/skills/monkey-mini-app/references/contracts/calendar.md +20 -0
  29. package/skills/monkey-mini-app/references/contracts/card.md +41 -0
  30. package/skills/monkey-mini-app/references/contracts/cascader.md +12 -2
  31. package/skills/monkey-mini-app/references/contracts/chart.md +32 -0
  32. package/skills/monkey-mini-app/references/contracts/checkbox.md +19 -0
  33. package/skills/monkey-mini-app/references/contracts/code-block.md +20 -2
  34. package/skills/monkey-mini-app/references/contracts/code-editor.md +30 -4
  35. package/skills/monkey-mini-app/references/contracts/collapsible.md +21 -0
  36. package/skills/monkey-mini-app/references/contracts/color-picker.md +12 -2
  37. package/skills/monkey-mini-app/references/contracts/combobox.md +34 -0
  38. package/skills/monkey-mini-app/references/contracts/command.md +41 -0
  39. package/skills/monkey-mini-app/references/contracts/comment-thread.md +12 -2
  40. package/skills/monkey-mini-app/references/contracts/commit-graph.md +50 -0
  41. package/skills/monkey-mini-app/references/contracts/confirm-dialog.md +20 -2
  42. package/skills/monkey-mini-app/references/contracts/context-menu.md +41 -0
  43. package/skills/monkey-mini-app/references/contracts/copyable.md +12 -2
  44. package/skills/monkey-mini-app/references/contracts/currency-input.md +12 -2
  45. package/skills/monkey-mini-app/references/contracts/dashboard-shell.md +41 -0
  46. package/skills/monkey-mini-app/references/contracts/data-grid.md +13 -6
  47. package/skills/monkey-mini-app/references/contracts/date-picker.md +20 -2
  48. package/skills/monkey-mini-app/references/contracts/date-range-picker.md +10 -2
  49. package/skills/monkey-mini-app/references/contracts/date-time-picker.md +20 -2
  50. package/skills/monkey-mini-app/references/contracts/date-time-range-picker.md +20 -2
  51. package/skills/monkey-mini-app/references/contracts/description-list.md +12 -2
  52. package/skills/monkey-mini-app/references/contracts/detail-panel.md +10 -2
  53. package/skills/monkey-mini-app/references/contracts/dialog.md +44 -0
  54. package/skills/monkey-mini-app/references/contracts/diff-viewer.md +21 -2
  55. package/skills/monkey-mini-app/references/contracts/donut-chart.md +14 -2
  56. package/skills/monkey-mini-app/references/contracts/drawer.md +29 -0
  57. package/skills/monkey-mini-app/references/contracts/dropdown-menu.md +49 -0
  58. package/skills/monkey-mini-app/references/contracts/duration-input.md +20 -2
  59. package/skills/monkey-mini-app/references/contracts/empty.md +40 -0
  60. package/skills/monkey-mini-app/references/contracts/env-badge.md +12 -2
  61. package/skills/monkey-mini-app/references/contracts/env-table.md +46 -0
  62. package/skills/monkey-mini-app/references/contracts/event-calendar.md +28 -20
  63. package/skills/monkey-mini-app/references/contracts/field.md +34 -0
  64. package/skills/monkey-mini-app/references/contracts/file-dropzone.md +20 -2
  65. package/skills/monkey-mini-app/references/contracts/file-tree.md +12 -2
  66. package/skills/monkey-mini-app/references/contracts/filter-bar.md +2 -2
  67. package/skills/monkey-mini-app/references/contracts/form-sheet.md +51 -0
  68. package/skills/monkey-mini-app/references/contracts/gantt.md +20 -2
  69. package/skills/monkey-mini-app/references/contracts/gauge.md +14 -2
  70. package/skills/monkey-mini-app/references/contracts/hover-card.md +21 -0
  71. package/skills/monkey-mini-app/references/contracts/input-group.md +24 -0
  72. package/skills/monkey-mini-app/references/contracts/input.md +33 -0
  73. package/skills/monkey-mini-app/references/contracts/item.md +36 -0
  74. package/skills/monkey-mini-app/references/contracts/jira-wiki.md +3 -4
  75. package/skills/monkey-mini-app/references/contracts/jql-input.md +15 -8
  76. package/skills/monkey-mini-app/references/contracts/json-viewer.md +20 -2
  77. package/skills/monkey-mini-app/references/contracts/kanban-issue-panel.md +12 -2
  78. package/skills/monkey-mini-app/references/contracts/kanban.md +10 -2
  79. package/skills/monkey-mini-app/references/contracts/kbd.md +20 -0
  80. package/skills/monkey-mini-app/references/contracts/label.md +33 -0
  81. package/skills/monkey-mini-app/references/contracts/list-detail.md +46 -0
  82. package/skills/monkey-mini-app/references/contracts/log-viewer.md +45 -4
  83. package/skills/monkey-mini-app/references/contracts/markdown-editor.md +20 -2
  84. package/skills/monkey-mini-app/references/contracts/markdown.md +12 -2
  85. package/skills/monkey-mini-app/references/contracts/marker.md +30 -0
  86. package/skills/monkey-mini-app/references/contracts/menubar.md +34 -0
  87. package/skills/monkey-mini-app/references/contracts/message-scroller.md +24 -0
  88. package/skills/monkey-mini-app/references/contracts/message.md +24 -0
  89. package/skills/monkey-mini-app/references/contracts/mini-calendar.md +20 -2
  90. package/skills/monkey-mini-app/references/contracts/native-select.md +21 -0
  91. package/skills/monkey-mini-app/references/contracts/navigation-menu.md +27 -0
  92. package/skills/monkey-mini-app/references/contracts/notification-center.md +12 -2
  93. package/skills/monkey-mini-app/references/contracts/number-field.md +20 -2
  94. package/skills/monkey-mini-app/references/contracts/page-header.md +2 -2
  95. package/skills/monkey-mini-app/references/contracts/pagination.md +33 -0
  96. package/skills/monkey-mini-app/references/contracts/password-field.md +12 -2
  97. package/skills/monkey-mini-app/references/contracts/phone-input.md +12 -2
  98. package/skills/monkey-mini-app/references/contracts/popover.md +32 -0
  99. package/skills/monkey-mini-app/references/contracts/progress-ring.md +14 -2
  100. package/skills/monkey-mini-app/references/contracts/progress.md +39 -0
  101. package/skills/monkey-mini-app/references/contracts/questionnaire.md +33 -0
  102. package/skills/monkey-mini-app/references/contracts/radar-chart.md +14 -2
  103. package/skills/monkey-mini-app/references/contracts/radio-group.md +20 -0
  104. package/skills/monkey-mini-app/references/contracts/rating.md +12 -2
  105. package/skills/monkey-mini-app/references/contracts/relative-date-picker.md +20 -2
  106. package/skills/monkey-mini-app/references/contracts/request-inspector.md +12 -2
  107. package/skills/monkey-mini-app/references/contracts/resizable.md +29 -0
  108. package/skills/monkey-mini-app/references/contracts/reveal.md +31 -0
  109. package/skills/monkey-mini-app/references/contracts/rich-text-editor.md +14 -2
  110. package/skills/monkey-mini-app/references/contracts/run-timeline.md +20 -2
  111. package/skills/monkey-mini-app/references/contracts/scroll-area.md +20 -0
  112. package/skills/monkey-mini-app/references/contracts/scrollspy.md +20 -2
  113. package/skills/monkey-mini-app/references/contracts/search-input.md +12 -2
  114. package/skills/monkey-mini-app/references/contracts/select.md +36 -0
  115. package/skills/monkey-mini-app/references/contracts/separator.md +27 -0
  116. package/skills/monkey-mini-app/references/contracts/settings-split.md +53 -0
  117. package/skills/monkey-mini-app/references/contracts/severity-chip.md +12 -2
  118. package/skills/monkey-mini-app/references/contracts/sheet.md +34 -0
  119. package/skills/monkey-mini-app/references/contracts/sidebar.md +42 -0
  120. package/skills/monkey-mini-app/references/contracts/skeleton.md +33 -0
  121. package/skills/monkey-mini-app/references/contracts/slider-range.md +12 -2
  122. package/skills/monkey-mini-app/references/contracts/slider.md +25 -0
  123. package/skills/monkey-mini-app/references/contracts/sortable-list.md +20 -2
  124. package/skills/monkey-mini-app/references/contracts/sparkline.md +13 -2
  125. package/skills/monkey-mini-app/references/contracts/spinner.md +31 -0
  126. package/skills/monkey-mini-app/references/contracts/stacked-bar-chart.md +17 -2
  127. package/skills/monkey-mini-app/references/contracts/stat-card.md +2 -2
  128. package/skills/monkey-mini-app/references/contracts/status-badge.md +2 -2
  129. package/skills/monkey-mini-app/references/contracts/stepper-item.md +39 -0
  130. package/skills/monkey-mini-app/references/contracts/stepper.md +38 -11
  131. package/skills/monkey-mini-app/references/contracts/switch.md +25 -0
  132. package/skills/monkey-mini-app/references/contracts/table-page.md +41 -0
  133. package/skills/monkey-mini-app/references/contracts/table.md +42 -0
  134. package/skills/monkey-mini-app/references/contracts/tabs.md +31 -0
  135. package/skills/monkey-mini-app/references/contracts/tag-input.md +12 -2
  136. package/skills/monkey-mini-app/references/contracts/terminal.md +13 -2
  137. package/skills/monkey-mini-app/references/contracts/test-step-list.md +12 -2
  138. package/skills/monkey-mini-app/references/contracts/textarea.md +25 -0
  139. package/skills/monkey-mini-app/references/contracts/time-picker.md +18 -2
  140. package/skills/monkey-mini-app/references/contracts/time-range-picker.md +20 -2
  141. package/skills/monkey-mini-app/references/contracts/time-wheel.md +6 -2
  142. package/skills/monkey-mini-app/references/contracts/timeline.md +20 -2
  143. package/skills/monkey-mini-app/references/contracts/timezone-select.md +20 -2
  144. package/skills/monkey-mini-app/references/contracts/toast.md +39 -0
  145. package/skills/monkey-mini-app/references/contracts/toggle-group.md +28 -0
  146. package/skills/monkey-mini-app/references/contracts/toggle.md +26 -0
  147. package/skills/monkey-mini-app/references/contracts/tooltip.md +30 -0
  148. package/skills/monkey-mini-app/references/contracts/transfer.md +12 -2
  149. package/skills/monkey-mini-app/references/contracts/tree-view.md +20 -2
  150. package/skills/monkey-mini-app/references/contracts/trend-card.md +12 -2
  151. package/skills/monkey-mini-app/references/contracts/user-picker.md +12 -2
  152. package/skills/monkey-mini-app/references/contracts/wizard-shell.md +56 -0
  153. package/skills/monkey-mini-app/references/ctx.md +123 -53
  154. package/skills/monkey-mini-app/references/eval.md +115 -0
  155. package/skills/monkey-mini-app/references/examples/alert/alert-01.tsx +17 -0
  156. package/skills/monkey-mini-app/references/examples/aspect-ratio/aspect-ratio-01.tsx +16 -0
  157. package/skills/monkey-mini-app/references/examples/attachment/attachment-01.tsx +19 -0
  158. package/skills/monkey-mini-app/references/examples/breadcrumb/breadcrumb-01.tsx +31 -0
  159. package/skills/monkey-mini-app/references/examples/bubble/bubble-01.tsx +16 -0
  160. package/skills/monkey-mini-app/references/examples/button-group/button-group-01.tsx +23 -0
  161. package/skills/monkey-mini-app/references/examples/card/card-01.tsx +19 -0
  162. package/skills/monkey-mini-app/references/examples/charts-blocks.md +15 -0
  163. package/skills/monkey-mini-app/references/examples/code-block/code-block-01.tsx +14 -0
  164. package/skills/monkey-mini-app/references/examples/code-editor/code-editor-01.tsx +15 -0
  165. package/skills/monkey-mini-app/references/examples/command/command-01.tsx +30 -0
  166. package/skills/monkey-mini-app/references/examples/confirm-dialog/confirm-dialog-01.tsx +33 -0
  167. package/skills/monkey-mini-app/references/examples/context-menu/context-menu-01.tsx +29 -0
  168. package/skills/monkey-mini-app/references/examples/dashboard-shell/dashboard-shell-01.tsx +78 -0
  169. package/skills/monkey-mini-app/references/examples/data-grid/data-grid-01.tsx +34 -0
  170. package/skills/monkey-mini-app/references/examples/data-grid/data-grid-02.tsx +88 -0
  171. package/skills/monkey-mini-app/references/examples/date-picker/date-picker-01.tsx +14 -0
  172. package/skills/monkey-mini-app/references/examples/date-range-picker/date-range-picker-01.tsx +14 -0
  173. package/skills/monkey-mini-app/references/examples/date-time-picker/date-time-picker-01.tsx +16 -0
  174. package/skills/monkey-mini-app/references/examples/date-time-range-picker/date-time-range-picker-01.tsx +15 -0
  175. package/skills/monkey-mini-app/references/examples/detail-panel/detail-panel-01.tsx +21 -0
  176. package/skills/monkey-mini-app/references/examples/dialog/dialog-01.tsx +30 -0
  177. package/skills/monkey-mini-app/references/examples/diff-viewer/diff-viewer-01.tsx +10 -0
  178. package/skills/monkey-mini-app/references/examples/dropdown-menu/dropdown-menu-01.tsx +31 -0
  179. package/skills/monkey-mini-app/references/examples/duration-input/duration-input-01.tsx +20 -0
  180. package/skills/monkey-mini-app/references/examples/editors.md +11 -0
  181. package/skills/monkey-mini-app/references/examples/empty/empty-01.tsx +19 -0
  182. package/skills/monkey-mini-app/references/examples/event-calendar/event-calendar-01.tsx +53 -0
  183. package/skills/monkey-mini-app/references/examples/file-dropzone/file-dropzone-01.tsx +14 -0
  184. package/skills/monkey-mini-app/references/examples/form-sheet/form-sheet-01.tsx +85 -0
  185. package/skills/monkey-mini-app/references/examples/forms.md +16 -0
  186. package/skills/monkey-mini-app/references/examples/gantt/gantt-01.tsx +29 -0
  187. package/skills/monkey-mini-app/references/examples/input/input-01.tsx +10 -0
  188. package/skills/monkey-mini-app/references/examples/item/item-01.tsx +19 -0
  189. package/skills/monkey-mini-app/references/examples/jql-input/jql-input-01.tsx +33 -0
  190. package/skills/monkey-mini-app/references/examples/json-viewer/json-viewer-01.tsx +10 -0
  191. package/skills/monkey-mini-app/references/examples/kanban/kanban-01.tsx +60 -0
  192. package/skills/monkey-mini-app/references/examples/label/label-01.tsx +10 -0
  193. package/skills/monkey-mini-app/references/examples/list-detail/list-detail-01.tsx +117 -0
  194. package/skills/monkey-mini-app/references/examples/log-viewer/log-viewer-01.tsx +20 -0
  195. package/skills/monkey-mini-app/references/examples/markdown-editor/markdown-editor-01.tsx +18 -0
  196. package/skills/monkey-mini-app/references/examples/marker/marker-01.tsx +16 -0
  197. package/skills/monkey-mini-app/references/examples/mini-calendar/mini-calendar-01.tsx +14 -0
  198. package/skills/monkey-mini-app/references/examples/misc-charts-blocks/misc-charts-blocks-01.tsx +20 -0
  199. package/skills/monkey-mini-app/references/examples/misc-charts-blocks/misc-charts-blocks-02.tsx +32 -0
  200. package/skills/monkey-mini-app/references/examples/misc-charts-blocks/misc-charts-blocks-03.tsx +47 -0
  201. package/skills/monkey-mini-app/references/examples/misc-charts-blocks/misc-charts-blocks-04.tsx +20 -0
  202. package/skills/monkey-mini-app/references/examples/misc-charts-blocks/misc-charts-blocks-05.tsx +24 -0
  203. package/skills/monkey-mini-app/references/examples/misc-charts-blocks/misc-charts-blocks-06.tsx +89 -0
  204. package/skills/monkey-mini-app/references/examples/misc-editors/misc-editors-01.tsx +114 -0
  205. package/skills/monkey-mini-app/references/examples/misc-editors/misc-editors-02.tsx +33 -0
  206. package/skills/monkey-mini-app/references/examples/misc-forms/misc-forms-01.tsx +22 -0
  207. package/skills/monkey-mini-app/references/examples/misc-forms/misc-forms-02.tsx +37 -0
  208. package/skills/monkey-mini-app/references/examples/misc-forms/misc-forms-03.tsx +42 -0
  209. package/skills/monkey-mini-app/references/examples/misc-forms/misc-forms-04.tsx +25 -0
  210. package/skills/monkey-mini-app/references/examples/misc-forms/misc-forms-05.tsx +27 -0
  211. package/skills/monkey-mini-app/references/examples/misc-forms/misc-forms-06.tsx +33 -0
  212. package/skills/monkey-mini-app/references/examples/misc-forms/misc-forms-07.tsx +44 -0
  213. package/skills/monkey-mini-app/references/examples/misc-overlays/misc-overlays-01.tsx +43 -0
  214. package/skills/monkey-mini-app/references/examples/misc-overlays/misc-overlays-02.tsx +38 -0
  215. package/skills/monkey-mini-app/references/examples/misc-primitives/misc-primitives-01.tsx +21 -0
  216. package/skills/monkey-mini-app/references/examples/misc-primitives/misc-primitives-02.tsx +52 -0
  217. package/skills/monkey-mini-app/references/examples/misc-primitives/misc-primitives-03.tsx +44 -0
  218. package/skills/monkey-mini-app/references/examples/number-field/number-field-01.tsx +22 -0
  219. package/skills/monkey-mini-app/references/examples/overlays.md +11 -0
  220. package/skills/monkey-mini-app/references/examples/pagination/pagination-01.tsx +51 -0
  221. package/skills/monkey-mini-app/references/examples/primitives.md +12 -0
  222. package/skills/monkey-mini-app/references/examples/progress/progress-01.tsx +10 -0
  223. package/skills/monkey-mini-app/references/examples/relative-date-picker/relative-date-picker-01.tsx +19 -0
  224. package/skills/monkey-mini-app/references/examples/rich-text-editor/rich-text-editor-01.tsx +15 -0
  225. package/skills/monkey-mini-app/references/examples/run-timeline/run-timeline-01.tsx +17 -0
  226. package/skills/monkey-mini-app/references/examples/scrollspy/scrollspy-01.tsx +29 -0
  227. package/skills/monkey-mini-app/references/examples/separator/separator-01.tsx +10 -0
  228. package/skills/monkey-mini-app/references/examples/settings-split/settings-split-01.tsx +84 -0
  229. package/skills/monkey-mini-app/references/examples/shared/jira-wiki-sample.ts +38 -0
  230. package/skills/monkey-mini-app/references/examples/skeleton/skeleton-01.tsx +10 -0
  231. package/skills/monkey-mini-app/references/examples/slider/slider-01.tsx +22 -0
  232. package/skills/monkey-mini-app/references/examples/sortable-list/sortable-list-01.tsx +18 -0
  233. package/skills/monkey-mini-app/references/examples/spinner/spinner-01.tsx +10 -0
  234. package/skills/monkey-mini-app/references/examples/stepper/stepper-01.tsx +41 -0
  235. package/skills/monkey-mini-app/references/examples/table/table-01.tsx +25 -0
  236. package/skills/monkey-mini-app/references/examples/table-page/table-page-01.tsx +103 -0
  237. package/skills/monkey-mini-app/references/examples/time-picker/time-picker-01.tsx +19 -0
  238. package/skills/monkey-mini-app/references/examples/time-range-picker/time-range-picker-01.tsx +14 -0
  239. package/skills/monkey-mini-app/references/examples/timeline/timeline-01.tsx +18 -0
  240. package/skills/monkey-mini-app/references/examples/timezone-select/timezone-select-01.tsx +15 -0
  241. package/skills/monkey-mini-app/references/examples/toggle/toggle-01.tsx +20 -0
  242. package/skills/monkey-mini-app/references/examples/toggle-group/toggle-group-01.tsx +22 -0
  243. package/skills/monkey-mini-app/references/examples/tree-view/tree-view-01.tsx +25 -0
  244. package/skills/monkey-mini-app/references/examples/wizard-shell/wizard-shell-01.tsx +58 -0
  245. package/skills/monkey-mini-app/references/history.md +23 -7
  246. package/skills/monkey-mini-app/references/icons.md +99 -69
  247. package/skills/monkey-mini-app/references/llm-json.md +39 -26
  248. package/skills/monkey-mini-app/references/loader.md +47 -20
  249. package/skills/monkey-mini-app/references/looks/aurora-bento.md +88 -0
  250. package/skills/monkey-mini-app/references/looks/desk-split.md +89 -0
  251. package/skills/monkey-mini-app/references/looks/editorial.md +78 -0
  252. package/skills/monkey-mini-app/references/looks/glass-island.md +90 -0
  253. package/skills/monkey-mini-app/references/looks/index.md +18 -0
  254. package/skills/monkey-mini-app/references/looks/signage.md +74 -0
  255. package/skills/monkey-mini-app/references/looks/tape.md +22 -0
  256. package/skills/monkey-mini-app/references/looks/terminal.md +88 -0
  257. package/skills/monkey-mini-app/references/looks/void.md +22 -0
  258. package/skills/monkey-mini-app/references/styling.md +189 -0
  259. package/skills/monkey-mini-app/references/test.md +12 -12
  260. package/skills/monkey-mini-app/references/theme.md +197 -0
  261. package/skills/monkey-mini-app/references/tools.md +12 -12
  262. package/skills/monkey-mini-app/references/troubleshoot.md +165 -12
  263. package/skills/monkey-mini-app/templates/README.md +33 -21
  264. package/skills/monkey-mini-app/templates/{jira → board}/main.api.ts +11 -11
  265. package/skills/monkey-mini-app/templates/board/manifest.json +8 -0
  266. package/skills/monkey-mini-app/templates/board/theme.css +46 -0
  267. package/skills/monkey-mini-app/templates/board/ui.tsx +546 -0
  268. package/skills/monkey-mini-app/templates/chores/main.api.ts +35 -0
  269. package/skills/monkey-mini-app/templates/chores/manifest.json +8 -0
  270. package/skills/monkey-mini-app/templates/chores/theme.css +46 -0
  271. package/skills/monkey-mini-app/templates/chores/ui.tsx +117 -0
  272. package/skills/monkey-mini-app/templates/minimal/main.api.ts +5 -5
  273. package/skills/monkey-mini-app/templates/minimal/manifest.json +1 -2
  274. package/skills/monkey-mini-app/templates/minimal/ui.tsx +17 -8
  275. package/skills/monkey-mini-app/templates/radar/api/scan.ts +79 -0
  276. package/skills/monkey-mini-app/templates/radar/main.api.ts +29 -0
  277. package/skills/monkey-mini-app/templates/radar/manifest.json +8 -0
  278. package/skills/monkey-mini-app/templates/radar/shared/events.ts +25 -0
  279. package/skills/monkey-mini-app/templates/{insights/lib → radar/shared}/sample.ts +1 -1
  280. package/skills/monkey-mini-app/templates/radar/theme.css +47 -0
  281. package/skills/monkey-mini-app/templates/radar/ui/brief.tsx +31 -0
  282. package/skills/monkey-mini-app/templates/radar/ui.tsx +131 -0
  283. package/skills/monkey-mini-app/templates/runner/main.api.ts +91 -0
  284. package/skills/monkey-mini-app/templates/runner/manifest.json +8 -0
  285. package/skills/monkey-mini-app/templates/runner/shared/events.ts +14 -0
  286. package/skills/monkey-mini-app/templates/runner/theme.css +46 -0
  287. package/skills/monkey-mini-app/templates/runner/ui.tsx +213 -0
  288. package/skills/monkey-mini-app/templates/sheets/main.api.ts +276 -0
  289. package/skills/monkey-mini-app/templates/sheets/manifest.json +8 -0
  290. package/skills/monkey-mini-app/templates/sheets/theme.css +46 -0
  291. package/skills/monkey-mini-app/templates/sheets/ui.tsx +419 -0
  292. package/skills/monkey-mini-app/templates/{todo → today}/main.api.ts +8 -8
  293. package/skills/monkey-mini-app/templates/today/manifest.json +8 -0
  294. package/skills/monkey-mini-app/templates/today/theme.css +45 -0
  295. package/skills/monkey-mini-app/templates/today/ui/item-row.tsx +25 -0
  296. package/skills/monkey-mini-app/templates/today/ui.tsx +406 -0
  297. package/skills/monkey-mini-app/templates/{monitor → watch}/main.api.ts +8 -8
  298. package/skills/monkey-mini-app/templates/watch/manifest.json +8 -0
  299. package/skills/monkey-mini-app/templates/watch/ui.tsx +187 -0
  300. package/skills/monkey-mini-app/tsconfig.json +31 -0
  301. package/skills/monkey-mini-app/types/cdn.d.ts +1 -0
  302. package/tsup.config.ts +1 -0
  303. package/skills/monkey-mini-app/templates/agentrun/main.api.ts +0 -78
  304. package/skills/monkey-mini-app/templates/agentrun/manifest.json +0 -9
  305. package/skills/monkey-mini-app/templates/agentrun/ui.tsx +0 -125
  306. package/skills/monkey-mini-app/templates/insights/main.api.ts +0 -101
  307. package/skills/monkey-mini-app/templates/insights/manifest.json +0 -9
  308. package/skills/monkey-mini-app/templates/insights/ui.tsx +0 -138
  309. package/skills/monkey-mini-app/templates/jira/manifest.json +0 -9
  310. package/skills/monkey-mini-app/templates/jira/ui.tsx +0 -318
  311. package/skills/monkey-mini-app/templates/monitor/manifest.json +0 -9
  312. package/skills/monkey-mini-app/templates/monitor/ui.tsx +0 -176
  313. package/skills/monkey-mini-app/templates/review/main.api.ts +0 -68
  314. package/skills/monkey-mini-app/templates/review/manifest.json +0 -9
  315. package/skills/monkey-mini-app/templates/review/ui.tsx +0 -159
  316. package/skills/monkey-mini-app/templates/todo/manifest.json +0 -8
  317. package/skills/monkey-mini-app/templates/todo/ui.tsx +0 -177
package/lib/client.js CHANGED
@@ -56,295 +56,12 @@ function capabilitiesOf(host) {
56
56
  };
57
57
  }
58
58
 
59
- // ../panel/src/rest.ts
60
- function isRecord(value) {
61
- return typeof value === "object" && value !== null && !Array.isArray(value);
62
- }
63
- async function readJson(url, init2) {
64
- let res;
65
- try {
66
- res = await fetch(url, init2);
67
- } catch (cause) {
68
- throw new HostUnreachableError(url, cause);
69
- }
70
- if (!res.ok) throw new Error(`HTTP ${res.status}`);
71
- return res.json();
72
- }
73
- var HostUnreachableError = class extends Error {
74
- constructor(url, cause) {
75
- super(`Cannot reach apps host: ${url}`);
76
- __publicField(this, "url");
77
- this.name = "HostUnreachableError";
78
- this.url = url;
79
- if (cause !== void 0) {
80
- Object.defineProperty(this, "cause", { value: cause, configurable: true, writable: true });
81
- }
82
- }
83
- };
84
- function isHostUnreachable(e) {
85
- return e instanceof HostUnreachableError;
86
- }
87
- function appFrameUrl(origin, appId, query) {
88
- return `${origin}/app/${encodeURIComponent(appId)}?${new URLSearchParams(query).toString()}`;
89
- }
90
- function parseAppTheme(raw) {
91
- if (raw === null) return null;
92
- if (!isRecord(raw)) return void 0;
93
- const theme = typeof raw.theme === "string" ? raw.theme : "";
94
- const palette = typeof raw.palette === "string" ? raw.palette : "";
95
- if (!theme && !palette) return null;
96
- return { theme, palette };
97
- }
98
- function parseAppsResponse(raw) {
99
- if (!isRecord(raw) || !Array.isArray(raw.apps)) return [];
100
- const out = [];
101
- for (const item of raw.apps) {
102
- if (!isRecord(item) || typeof item.id !== "string") continue;
103
- out.push({
104
- id: item.id,
105
- name: typeof item.name === "string" ? item.name : item.id,
106
- description: typeof item.description === "string" ? item.description : void 0,
107
- acronym: typeof item.acronym === "string" ? item.acronym : void 0,
108
- commits: typeof item.commits === "number" ? item.commits : void 0,
109
- version: typeof item.version === "string" ? item.version : void 0,
110
- theme: parseAppTheme(item.theme)
111
- });
112
- }
113
- return out;
114
- }
115
- function hostConfigToForm(raw, cardStyle) {
116
- const rec = isRecord(raw) ? raw : {};
117
- const llm = isRecord(rec.llm) ? rec.llm : null;
118
- const locale = typeof rec.locale === "string" ? rec.locale : "";
119
- const chatLanguage = typeof rec.chatLanguage === "string" ? rec.chatLanguage : "";
120
- return {
121
- hostPort: rec.hostPort != null ? String(rec.hostPort) : "",
122
- locale: locale || chatLanguage,
123
- chatLanguage: chatLanguage || locale,
124
- theme: typeof rec.theme === "string" ? rec.theme : "",
125
- palette: typeof rec.palette === "string" ? rec.palette : "",
126
- cardStyle,
127
- provider: llm && typeof llm.provider === "string" ? llm.provider : "",
128
- model: llm && typeof llm.model === "string" ? llm.model : ""
129
- };
130
- }
131
- function formToHostConfigBody(form) {
132
- const provider = (form.provider ?? "").trim();
133
- const model = (form.model ?? "").trim();
134
- const locale = form.locale || form.chatLanguage;
135
- const body = { locale, chatLanguage: form.chatLanguage || locale, theme: form.theme, palette: form.palette };
136
- const port = Number(form.hostPort);
137
- if (Number.isInteger(port) && port >= 0 && port <= 65535) body.hostPort = port;
138
- if (provider && model) body.llm = { provider, model };
139
- else if (!provider && !model) body.llm = null;
140
- return body;
141
- }
142
- function asCommit(raw) {
143
- if (!isRecord(raw) || typeof raw.id !== "string") return null;
144
- const files = Array.isArray(raw.files) ? raw.files.flatMap((f) => {
145
- if (!isRecord(f) || typeof f.path !== "string") return [];
146
- return [{ path: f.path, add: typeof f.add === "number" ? f.add : void 0, del: typeof f.del === "number" ? f.del : void 0, preview: typeof f.preview === "string" ? f.preview : void 0 }];
147
- }) : void 0;
148
- return { id: raw.id, message: typeof raw.message === "string" ? raw.message : "", time: typeof raw.time === "string" ? raw.time : "", files };
149
- }
150
- function parseCommitList(raw) {
151
- const rec = isRecord(raw) ? raw : {};
152
- const list = Array.isArray(rec.commits) ? rec.commits : Array.isArray(rec.nodes) ? rec.nodes : [];
153
- return list.map((c) => asCommit(c)).filter((c) => c !== null);
154
- }
155
- function parseCommitDetail(raw, id) {
156
- const rec = isRecord(raw) ? raw : {};
157
- const inner = isRecord(rec.commit) ? rec.commit : rec;
158
- return asCommit(inner) ?? { id, message: "", time: "", files: [] };
159
- }
160
- function parseStorageTables(raw) {
161
- const rec = isRecord(raw) ? raw : {};
162
- const list = Array.isArray(rec.tables) ? rec.tables : [];
163
- const out = [];
164
- for (const row of list) {
165
- if (!isRecord(row) || typeof row.name !== "string") continue;
166
- out.push({
167
- name: row.name,
168
- size: typeof row.size === "number" ? row.size : void 0,
169
- updatedAt: typeof row.updatedAt === "string" ? row.updatedAt : void 0
170
- });
171
- }
172
- return out;
173
- }
174
- function parsePalettes(raw) {
175
- const rec = isRecord(raw) ? raw : {};
176
- const list = Array.isArray(rec.palettes) ? rec.palettes : [];
177
- const out = [];
178
- for (const p of list) {
179
- if (!isRecord(p) || typeof p.id !== "string") continue;
180
- if (p.custom === false) continue;
181
- out.push({
182
- id: p.id,
183
- label: typeof p.label === "string" ? p.label : p.id,
184
- swatch: typeof p.swatch === "string" ? p.swatch : "#888",
185
- tokens: isRecord(p.tokens) ? p.tokens : void 0
186
- });
187
- }
188
- return out;
189
- }
190
- function createRestPanelHost(opts) {
191
- const storage = opts.storage ?? (typeof window !== "undefined" ? window.localStorage : null);
192
- function origin() {
193
- return (opts.getHostUrl ? opts.getHostUrl() : opts.hostUrl).replace(/\/$/, "");
194
- }
195
- const host = {
196
- locale: opts.locale,
197
- emptyText: opts.emptyText,
198
- fetchApps: async () => parseAppsResponse(await readJson(`${origin()}/api/apps`)),
199
- palettes: async () => {
200
- try {
201
- return parsePalettes(await readJson(`${origin()}/api/palettes`));
202
- } catch {
203
- return [];
204
- }
205
- },
206
- openPanel: () => opts.onOpen?.(),
207
- closePanel: () => opts.onClose?.(),
208
- persistTheme: (theme, palette) => {
209
- try {
210
- storage?.setItem("mma-theme-mode", theme);
211
- storage?.setItem("mma-palette", palette);
212
- } catch {
213
- }
214
- void fetch(`${origin()}/api/host-config`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ theme, palette }) }).catch(() => {
215
- });
216
- },
217
- appTheme: {
218
- save: async (appId, t2) => {
219
- await fetch(`${origin()}/api/apps/${encodeURIComponent(appId)}/theme`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(t2) }).catch(() => {
220
- });
221
- },
222
- clear: async (appId) => {
223
- await fetch(`${origin()}/api/apps/${encodeURIComponent(appId)}/theme`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ reset: true }) }).catch(() => {
224
- });
225
- }
226
- },
227
- config: {
228
- load: async () => hostConfigToForm(await readJson(`${origin()}/api/host-config`), opts.getCardStyle?.() ?? opts.cardStyle ?? "stamp"),
229
- save: async (cfg) => {
230
- const body = formToHostConfigBody(cfg);
231
- const res = await fetch(`${origin()}/api/host-config`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) });
232
- const raw = await res.json().catch(() => ({}));
233
- if (!res.ok || isRecord(raw) && raw.ok === false) {
234
- throw new Error(isRecord(raw) && typeof raw.error === "string" ? raw.error : `HTTP ${res.status}`);
235
- }
236
- const hostPort = isRecord(raw) ? raw.hostPort : void 0;
237
- if (typeof hostPort === "number" && Number.isInteger(hostPort) && hostPort > 0) {
238
- const next = `${new URL(origin()).protocol}//${new URL(origin()).hostname}:${hostPort}`;
239
- if (next !== origin()) opts.onHostChange?.(next);
240
- }
241
- opts.onConfigSaved?.(cfg);
242
- }
243
- },
244
- history: {
245
- list: async (appId) => parseCommitList(await readJson(`${origin()}/api/apps/${encodeURIComponent(appId)}/history?limit=50`)),
246
- detail: async (appId, id) => parseCommitDetail(await readJson(`${origin()}/api/apps/${encodeURIComponent(appId)}/history/${encodeURIComponent(id)}`), id)
247
- },
248
- storage: {
249
- listTables: async (appId) => parseStorageTables(await readJson(`${origin()}/api/apps/${encodeURIComponent(appId)}/storage`)),
250
- readTable: async (appId, name2) => {
251
- const raw = await readJson(`${origin()}/api/apps/${encodeURIComponent(appId)}/storage/${encodeURIComponent(name2)}`);
252
- return isRecord(raw) && "value" in raw ? raw.value : raw;
253
- }
254
- },
255
- deleteApp: opts.deleteApp ?? (async (appId) => {
256
- await fetch(`${origin()}/api/app/${encodeURIComponent(appId)}`, { method: "DELETE" }).catch(() => {
257
- });
258
- }),
259
- frame: opts.frameController ?? {
260
- url: (appId) => `${origin()}/app/${encodeURIComponent(appId)}`,
261
- mount: () => void 0,
262
- unmount: () => void 0,
263
- reload: () => void 0,
264
- syncEnv: () => void 0
265
- }
266
- };
267
- return host;
268
- }
269
-
270
- // ../panel/src/store.ts
271
- var import_react = require("react");
272
- var none = {
273
- history: false,
274
- storage: false,
275
- config: false,
276
- appTheme: false,
277
- customPalettes: false,
278
- deleteApp: false
279
- };
280
- var initial = {
281
- tabs: [{ id: "all", title: "\u5168\u90E8", kind: "all" }],
282
- active: "all",
283
- apps: [],
284
- error: null,
285
- loading: false,
286
- query: "",
287
- dock: "fill",
288
- theme: "light",
289
- palette: "default",
290
- themeScope: "global",
291
- customPalettes: {},
292
- cardStyle: "stamp",
293
- visible: false,
294
- pendingDelete: null,
295
- themePopOpen: false,
296
- settingsOpen: false,
297
- cfgMsg: "",
298
- cfgVersion: 0,
299
- cfg: {},
300
- emptyText: void 0,
301
- capabilities: none,
302
- locale: "zh-CN",
303
- browseOpen: false,
304
- browseKind: "history",
305
- browseAppId: null,
306
- browseAppName: "",
307
- browseLoading: false,
308
- browseError: null,
309
- browseList: [],
310
- browseDetail: null,
311
- browseTable: null,
312
- browseTableValue: null,
313
- browseOpenFile: null
314
- };
315
- var state = { ...initial, capabilities: { ...none } };
316
- var listeners = /* @__PURE__ */ new Set();
317
- function getPanelState() {
318
- return state;
319
- }
320
- function setPanelState(patch) {
321
- state = { ...state, ...patch };
322
- for (const fn of listeners) fn();
323
- return state;
324
- }
325
- function subscribePanel(listener) {
326
- listeners.add(listener);
327
- return () => {
328
- listeners.delete(listener);
329
- };
330
- }
331
- function resetPanelState(seed) {
332
- state = {
333
- ...initial,
334
- capabilities: { ...none },
335
- customPalettes: {},
336
- cfg: {},
337
- tabs: [{ id: "all", title: "\u5168\u90E8", kind: "all" }],
338
- ...seed || {}
339
- };
340
- for (const fn of listeners) fn();
341
- return state;
342
- }
343
- function usePanelState() {
344
- return (0, import_react.useSyncExternalStore)(subscribePanel, getPanelState);
345
- }
346
-
347
59
  // ../panel/src/themes.ts
60
+ var LOCAL_PALETTE_ID = "__local__";
61
+ var GLOBAL_PALETTE_ID = "__global__";
62
+ function selectedPalette(stored, hasLocalTheme) {
63
+ return stored ?? (hasLocalTheme ? LOCAL_PALETTE_ID : GLOBAL_PALETTE_ID);
64
+ }
348
65
  var PALETTES = [
349
66
  { id: "default", label: "\u9ED8\u8BA4", swatch: "#2563eb" },
350
67
  { id: "tokyo", label: "\u4E1C\u4EAC\u591C", swatch: "#7aa2f7" },
@@ -464,312 +181,768 @@ var TOKENS = {
464
181
  ring: "#93b259",
465
182
  input: "#f7f1e0",
466
183
  radius: "12px",
467
- shadow: "rgba(70, 90, 60, 0.12)"
184
+ shadow: "rgba(70, 90, 60, 0.12)"
185
+ },
186
+ dark: {
187
+ bg: "#2d353b",
188
+ fg: "#d3c6aa",
189
+ surface: "#333c43",
190
+ surfaceFg: "#d3c6aa",
191
+ border: "#414c52",
192
+ muted: "#3d484d",
193
+ mutedFg: "#859289",
194
+ primary: "#a7c080",
195
+ primaryFg: "#2d353b",
196
+ secondary: "#3d484d",
197
+ secondaryFg: "#d3c6aa",
198
+ accent: "#475258",
199
+ accentFg: "#d3c6aa",
200
+ destructive: "#e67e80",
201
+ destructiveFg: "#2d353b",
202
+ ring: "#a7c080",
203
+ input: "#272e33",
204
+ radius: "12px",
205
+ shadow: "rgba(0, 0, 0, 0.4)"
206
+ }
207
+ },
208
+ matcha: {
209
+ light: {
210
+ bg: "#faf5f3",
211
+ fg: "#4c4147",
212
+ surface: "#ffffff",
213
+ surfaceFg: "#4c4147",
214
+ border: "#eadcd8",
215
+ muted: "#f3eae6",
216
+ mutedFg: "#98857f",
217
+ primary: "#e08a9c",
218
+ primaryFg: "#ffffff",
219
+ secondary: "#f0e4df",
220
+ secondaryFg: "#4c4147",
221
+ accent: "#e8f0da",
222
+ accentFg: "#5a7a3f",
223
+ destructive: "#d95f7f",
224
+ destructiveFg: "#ffffff",
225
+ ring: "#e08a9c",
226
+ input: "#f8f1ee",
227
+ radius: "14px",
228
+ shadow: "rgba(180, 90, 110, 0.12)"
229
+ },
230
+ dark: {
231
+ bg: "#28242b",
232
+ fg: "#e9d8d2",
233
+ surface: "#302b33",
234
+ surfaceFg: "#e9d8d2",
235
+ border: "#443a44",
236
+ muted: "#38313a",
237
+ mutedFg: "#b3a09f",
238
+ primary: "#ee9aa6",
239
+ primaryFg: "#28242b",
240
+ secondary: "#38313a",
241
+ secondaryFg: "#e9d8d2",
242
+ accent: "#3f4a38",
243
+ accentFg: "#c8e3b2",
244
+ destructive: "#e06c8a",
245
+ destructiveFg: "#28242b",
246
+ ring: "#ee9aa6",
247
+ input: "#231f26",
248
+ radius: "14px",
249
+ shadow: "rgba(0, 0, 0, 0.45)"
250
+ }
251
+ },
252
+ yellow: {
253
+ light: {
254
+ bg: "#faf6ef",
255
+ fg: "#3d3326",
256
+ surface: "#ffffff",
257
+ surfaceFg: "#3d3326",
258
+ border: "#e8dfcd",
259
+ muted: "#f2ecdf",
260
+ mutedFg: "#9a8a6f",
261
+ primary: "#a37b16",
262
+ primaryFg: "#ffffff",
263
+ secondary: "#ede5d3",
264
+ secondaryFg: "#3d3326",
265
+ accent: "#f7edcb",
266
+ accentFg: "#6b4e0a",
267
+ destructive: "#dc2626",
268
+ destructiveFg: "#ffffff",
269
+ ring: "#a37b16",
270
+ input: "#f7f1e4",
271
+ radius: "12px",
272
+ shadow: "rgba(140, 110, 40, 0.12)"
273
+ },
274
+ dark: {
275
+ bg: "#1c1917",
276
+ fg: "#f5f0e6",
277
+ surface: "#242019",
278
+ surfaceFg: "#f5f0e6",
279
+ border: "#3a332a",
280
+ muted: "#2a2520",
281
+ mutedFg: "#b3a68f",
282
+ primary: "#facc15",
283
+ primaryFg: "#1c1917",
284
+ secondary: "#2a2520",
285
+ secondaryFg: "#f5f0e6",
286
+ accent: "#3f3518",
287
+ accentFg: "#fde68a",
288
+ destructive: "#f87171",
289
+ destructiveFg: "#1c1917",
290
+ ring: "#facc15",
291
+ input: "#211d18",
292
+ radius: "12px",
293
+ shadow: "rgba(0, 0, 0, 0.5)"
294
+ }
295
+ },
296
+ zoro: {
297
+ light: {
298
+ bg: "#f2f6f0",
299
+ fg: "#24302a",
300
+ surface: "#ffffff",
301
+ surfaceFg: "#24302a",
302
+ border: "#d8e4d6",
303
+ muted: "#e9efe6",
304
+ mutedFg: "#7d907e",
305
+ primary: "#15803d",
306
+ primaryFg: "#ffffff",
307
+ secondary: "#e3ecdf",
308
+ secondaryFg: "#24302a",
309
+ accent: "#dcf2e2",
310
+ accentFg: "#166534",
311
+ destructive: "#dc2626",
312
+ destructiveFg: "#ffffff",
313
+ ring: "#15803d",
314
+ input: "#f4f8f2",
315
+ radius: "12px",
316
+ shadow: "rgba(30, 80, 50, 0.1)"
317
+ },
318
+ dark: {
319
+ bg: "#101512",
320
+ fg: "#d8e0d4",
321
+ surface: "#18211b",
322
+ surfaceFg: "#d8e0d4",
323
+ border: "#2c3a30",
324
+ muted: "#1d2720",
325
+ mutedFg: "#93a393",
326
+ primary: "#4ade80",
327
+ primaryFg: "#101512",
328
+ secondary: "#1d2720",
329
+ secondaryFg: "#d8e0d4",
330
+ accent: "#14532d",
331
+ accentFg: "#bbf7d0",
332
+ destructive: "#f87171",
333
+ destructiveFg: "#101512",
334
+ ring: "#4ade80",
335
+ input: "#141b16",
336
+ radius: "12px",
337
+ shadow: "rgba(0, 0, 0, 0.5)"
338
+ }
339
+ },
340
+ hokage: {
341
+ light: {
342
+ bg: "#faf3ea",
343
+ fg: "#4a3a2c",
344
+ surface: "#ffffff",
345
+ surfaceFg: "#4a3a2c",
346
+ border: "#eadcc8",
347
+ muted: "#f3e9db",
348
+ mutedFg: "#a0886a",
349
+ primary: "#c2660f",
350
+ primaryFg: "#ffffff",
351
+ secondary: "#efe2d0",
352
+ secondaryFg: "#4a3a2c",
353
+ accent: "#fbe6c8",
354
+ accentFg: "#7c4a10",
355
+ destructive: "#dc2626",
356
+ destructiveFg: "#ffffff",
357
+ ring: "#c2660f",
358
+ input: "#f8f0e4",
359
+ radius: "12px",
360
+ shadow: "rgba(150, 90, 30, 0.12)"
468
361
  },
469
362
  dark: {
470
- bg: "#2d353b",
471
- fg: "#d3c6aa",
472
- surface: "#333c43",
473
- surfaceFg: "#d3c6aa",
474
- border: "#414c52",
475
- muted: "#3d484d",
476
- mutedFg: "#859289",
477
- primary: "#a7c080",
478
- primaryFg: "#2d353b",
479
- secondary: "#3d484d",
480
- secondaryFg: "#d3c6aa",
481
- accent: "#475258",
482
- accentFg: "#d3c6aa",
483
- destructive: "#e67e80",
484
- destructiveFg: "#2d353b",
485
- ring: "#a7c080",
486
- input: "#272e33",
363
+ bg: "#241a17",
364
+ fg: "#f0e3d8",
365
+ surface: "#2e211c",
366
+ surfaceFg: "#f0e3d8",
367
+ border: "#45372e",
368
+ muted: "#33251f",
369
+ mutedFg: "#b59f8c",
370
+ primary: "#f59e0b",
371
+ primaryFg: "#241a17",
372
+ secondary: "#33251f",
373
+ secondaryFg: "#f0e3d8",
374
+ accent: "#4a3024",
375
+ accentFg: "#fcd9b6",
376
+ destructive: "#ef4444",
377
+ destructiveFg: "#241a17",
378
+ ring: "#f59e0b",
379
+ input: "#201612",
487
380
  radius: "12px",
488
- shadow: "rgba(0, 0, 0, 0.4)"
381
+ shadow: "rgba(0, 0, 0, 0.5)"
489
382
  }
490
383
  },
491
- matcha: {
384
+ slate: {
492
385
  light: {
493
- bg: "#faf5f3",
494
- fg: "#4c4147",
386
+ bg: "#ececee",
387
+ fg: "#09090b",
495
388
  surface: "#ffffff",
496
- surfaceFg: "#4c4147",
497
- border: "#eadcd8",
498
- muted: "#f3eae6",
499
- mutedFg: "#98857f",
500
- primary: "#e08a9c",
501
- primaryFg: "#ffffff",
502
- secondary: "#f0e4df",
503
- secondaryFg: "#4c4147",
504
- accent: "#e8f0da",
505
- accentFg: "#5a7a3f",
506
- destructive: "#d95f7f",
389
+ surfaceFg: "#09090b",
390
+ border: "#c8c8ce",
391
+ muted: "#e0e0e5",
392
+ mutedFg: "#52525b",
393
+ primary: "#27272a",
394
+ primaryFg: "#fafafa",
395
+ secondary: "#e0e0e5",
396
+ secondaryFg: "#09090b",
397
+ accent: "#d4d4d8",
398
+ accentFg: "#18181b",
399
+ destructive: "#dc2626",
507
400
  destructiveFg: "#ffffff",
508
- ring: "#e08a9c",
509
- input: "#f8f1ee",
510
- radius: "14px",
511
- shadow: "rgba(180, 90, 110, 0.12)"
401
+ ring: "#27272a",
402
+ input: "#c8c8ce",
403
+ radius: "8px",
404
+ shadow: "rgba(24, 24, 27, 0.12)"
405
+ },
406
+ dark: {
407
+ bg: "#050506",
408
+ fg: "#fafafa",
409
+ surface: "#141416",
410
+ surfaceFg: "#fafafa",
411
+ border: "#3f3f46",
412
+ muted: "#1f1f23",
413
+ mutedFg: "#a1a1aa",
414
+ primary: "#e4e4e7",
415
+ primaryFg: "#09090b",
416
+ secondary: "#1f1f23",
417
+ secondaryFg: "#fafafa",
418
+ accent: "#27272a",
419
+ accentFg: "#f4f4f5",
420
+ destructive: "#f87171",
421
+ destructiveFg: "#09090b",
422
+ ring: "#e4e4e7",
423
+ input: "#3f3f46",
424
+ radius: "8px",
425
+ shadow: "rgba(0, 0, 0, 0.6)"
426
+ }
427
+ }
428
+ };
429
+ var THEME_VAR_KEYS = [
430
+ "bg",
431
+ "fg",
432
+ "surface",
433
+ "surface-fg",
434
+ "border",
435
+ "muted",
436
+ "muted-fg",
437
+ "primary",
438
+ "primary-fg",
439
+ "secondary",
440
+ "secondary-fg",
441
+ "accent",
442
+ "accent-fg",
443
+ "destructive",
444
+ "destructive-fg",
445
+ "ring",
446
+ "input",
447
+ "radius",
448
+ "shadow"
449
+ ];
450
+ function kebabToCamel(k) {
451
+ return k.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
452
+ }
453
+ function parseThemeCss(css2, _id) {
454
+ const out = {};
455
+ const blockRe = /:root\[data-mode="(light|dark)"\]\s*\{([^}]*)\}/g;
456
+ let m;
457
+ while (m = blockRe.exec(String(css2 || ""))) {
458
+ const mode = m[1];
459
+ const vars = {};
460
+ for (const key of THEME_VAR_KEYS) {
461
+ const vm = new RegExp(`--${key}\\s*:\\s*([^;\\n]+)`).exec(m[2]);
462
+ if (vm) vars[kebabToCamel(key)] = vm[1].trim();
463
+ }
464
+ if (vars.bg && vars.fg && vars.primary) out[mode] = vars;
465
+ }
466
+ if (!out.light || !out.dark) return null;
467
+ const base = {
468
+ surfaceFg: out.light.fg,
469
+ secondary: out.light.muted,
470
+ secondaryFg: out.light.fg,
471
+ destructiveFg: "#ffffff",
472
+ radius: "12px",
473
+ shadow: "rgba(15, 23, 42, 0.1)"
474
+ };
475
+ const fill = (v) => Object.assign({}, base, v);
476
+ return { light: fill(out.light), dark: fill(out.dark) };
477
+ }
478
+ function themeLabelFromCss(css2, fallback) {
479
+ const m = /\/\*\s*name\s*:\s*([^*]+)\*\//.exec(String(css2 || ""));
480
+ return m && m[1].trim() || fallback;
481
+ }
482
+ function clampPalette(value) {
483
+ if (value === "tokyo" || value === "forest" || value === "matcha" || value === "yellow" || value === "zoro" || value === "hokage" || value === "slate" || value === "default") {
484
+ return value;
485
+ }
486
+ if (value === "ocean" || value === "mist") return "tokyo";
487
+ if (value === "violet" || value === "ink") return "matcha";
488
+ if (value === "paper" || value === "noir") return "slate";
489
+ return "default";
490
+ }
491
+ function clampMode(value) {
492
+ return value === "dark" ? "dark" : "light";
493
+ }
494
+ function resolveMode(value) {
495
+ if (value === "system") {
496
+ try {
497
+ if (typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches) {
498
+ return "dark";
499
+ }
500
+ } catch {
501
+ }
502
+ return "light";
503
+ }
504
+ return clampMode(value);
505
+ }
506
+ function tokensOf(palette, mode) {
507
+ return TOKENS[clampPalette(palette)][clampMode(mode)];
508
+ }
509
+ function themeCssVars(theme, palette, custom) {
510
+ const mode = clampMode(theme);
511
+ const c = custom && palette ? custom[String(palette)] : void 0;
512
+ const pal = c ? String(palette) : clampPalette(palette);
513
+ const t2 = c && c.tokens && c.tokens[mode] || tokensOf(pal, mode);
514
+ return {
515
+ "--dsw-alias-bg": t2.bg,
516
+ "--dsw-alias-fg": t2.fg,
517
+ "--dsw-alias-surface": t2.surface,
518
+ "--dsw-alias-border": t2.border,
519
+ "--dsw-alias-muted": t2.muted,
520
+ "--dsw-alias-primary": t2.primary,
521
+ "--dsw-alias-primary-fg": t2.primaryFg,
522
+ "--dsw-alias-accent": t2.accent,
523
+ "--dsw-alias-shadow": t2.shadow,
524
+ "--background": t2.bg,
525
+ "--foreground": t2.fg,
526
+ "--card": t2.surface,
527
+ "--card-foreground": t2.surfaceFg,
528
+ "--primary": t2.primary,
529
+ "--primary-foreground": t2.primaryFg,
530
+ "--secondary": t2.secondary,
531
+ "--secondary-foreground": t2.secondaryFg,
532
+ "--muted": t2.muted,
533
+ "--muted-foreground": t2.mutedFg,
534
+ "--accent": t2.accent,
535
+ "--accent-foreground": t2.accentFg,
536
+ "--border": t2.border,
537
+ "--input": t2.input,
538
+ "--ring": t2.ring,
539
+ "--destructive": t2.destructive,
540
+ "--destructive-foreground": t2.destructiveFg,
541
+ "--radius": t2.radius,
542
+ "--shadow": t2.shadow
543
+ };
544
+ }
545
+ function applyThemeTo(el, theme, palette, custom) {
546
+ const mode = resolveMode(theme);
547
+ const c = custom && palette ? custom[String(palette)] : void 0;
548
+ const pal = c ? String(palette) : clampPalette(palette);
549
+ const t2 = c && c.tokens && c.tokens[mode] || tokensOf(pal, mode);
550
+ el.setAttribute("data-theme", mode);
551
+ if (theme === "system") el.setAttribute("data-theme-pref", "system");
552
+ else el.removeAttribute("data-theme-pref");
553
+ el.setAttribute("data-palette", pal);
554
+ const map = themeCssVars(mode, palette, custom);
555
+ for (const k of Object.keys(map)) el.style.setProperty(k, map[k]);
556
+ el.style.background = t2.bg;
557
+ el.style.color = t2.fg;
558
+ return { theme: mode, palette: pal };
559
+ }
560
+
561
+ // ../panel/src/rest.ts
562
+ function isRecord(value) {
563
+ return typeof value === "object" && value !== null && !Array.isArray(value);
564
+ }
565
+ async function readJson(url, init2) {
566
+ let res;
567
+ try {
568
+ res = await fetch(url, init2);
569
+ } catch (cause) {
570
+ throw new HostUnreachableError(url, cause);
571
+ }
572
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
573
+ return res.json();
574
+ }
575
+ var HostUnreachableError = class extends Error {
576
+ constructor(url, cause) {
577
+ super(`Cannot reach apps host: ${url}`);
578
+ __publicField(this, "url");
579
+ this.name = "HostUnreachableError";
580
+ this.url = url;
581
+ if (cause !== void 0) {
582
+ Object.defineProperty(this, "cause", { value: cause, configurable: true, writable: true });
583
+ }
584
+ }
585
+ };
586
+ function isHostUnreachable(e) {
587
+ return e instanceof HostUnreachableError;
588
+ }
589
+ function appFrameUrl(origin, appId, query) {
590
+ const params = new URLSearchParams();
591
+ for (const key of ["theme", "palette", "dock"]) {
592
+ const value = query[key];
593
+ if (value) params.set(key, value);
594
+ }
595
+ return `${origin}/app/${encodeURIComponent(appId)}?${params.toString()}`;
596
+ }
597
+ function relayViewEval(origin, frames, query) {
598
+ if (!query.requestId || !query.appId) return;
599
+ if (frames.postViewEval(query)) return;
600
+ const body = { appId: query.appId, requestId: query.requestId, view: "not-open" };
601
+ fetch(`${origin.replace(/\/$/, "")}/api/app/${encodeURIComponent(query.appId)}/view/eval`, {
602
+ method: "POST",
603
+ headers: { "content-type": "application/json" },
604
+ body: JSON.stringify(body)
605
+ }).catch(() => {
606
+ });
607
+ }
608
+ function parseLocalPalette(css2) {
609
+ if (typeof css2 !== "string" || !css2.trim()) return null;
610
+ const tokens = parseThemeCss(css2, LOCAL_PALETTE_ID);
611
+ if (!tokens) return null;
612
+ return {
613
+ label: themeLabelFromCss(css2, LOCAL_PALETTE_ID),
614
+ swatch: tokens.dark.primary || tokens.light.primary,
615
+ tokens
616
+ };
617
+ }
618
+ function parseAppTheme(raw) {
619
+ if (raw === null) return null;
620
+ if (!isRecord(raw)) return void 0;
621
+ const theme = typeof raw.theme === "string" ? raw.theme : "";
622
+ const palette = typeof raw.palette === "string" ? raw.palette : "";
623
+ if (!theme && !palette) return null;
624
+ return { theme, palette };
625
+ }
626
+ function parseAbout(raw) {
627
+ const rec = isRecord(raw) ? raw : {};
628
+ const packages = [];
629
+ if (Array.isArray(rec.packages)) {
630
+ for (const p of rec.packages) {
631
+ if (!isRecord(p) || typeof p.name !== "string" || typeof p.version !== "string") continue;
632
+ packages.push({ name: p.name, version: p.version });
633
+ }
634
+ }
635
+ return {
636
+ adapter: typeof rec.adapter === "string" ? rec.adapter : "host",
637
+ env: typeof rec.env === "string" ? rec.env : "unknown",
638
+ packages
639
+ };
640
+ }
641
+ function parseUpdateCheck(raw) {
642
+ const rec = isRecord(raw) ? raw : {};
643
+ return {
644
+ name: typeof rec.name === "string" ? rec.name : "",
645
+ current: typeof rec.current === "string" ? rec.current : "",
646
+ latest: typeof rec.latest === "string" ? rec.latest : null,
647
+ updateAvailable: rec.updateAvailable === true,
648
+ error: typeof rec.error === "string" ? rec.error : void 0
649
+ };
650
+ }
651
+ function parseAppsResponse(raw) {
652
+ if (!isRecord(raw) || !Array.isArray(raw.apps)) return [];
653
+ const out = [];
654
+ for (const item of raw.apps) {
655
+ if (!isRecord(item) || typeof item.id !== "string") continue;
656
+ out.push({
657
+ id: item.id,
658
+ name: typeof item.name === "string" ? item.name : item.id,
659
+ description: typeof item.description === "string" ? item.description : void 0,
660
+ acronym: typeof item.acronym === "string" ? item.acronym : void 0,
661
+ commits: typeof item.commits === "number" ? item.commits : void 0,
662
+ version: typeof item.version === "string" ? item.version : void 0,
663
+ theme: parseAppTheme(item.theme),
664
+ localPalette: parseLocalPalette(item.localThemeCss)
665
+ });
666
+ }
667
+ return out;
668
+ }
669
+ function hostConfigToForm(raw, cardStyle) {
670
+ const rec = isRecord(raw) ? raw : {};
671
+ const llm = isRecord(rec.llm) ? rec.llm : null;
672
+ const locale = typeof rec.locale === "string" ? rec.locale : "";
673
+ const chatLanguage = typeof rec.chatLanguage === "string" ? rec.chatLanguage : "";
674
+ return {
675
+ hostPort: rec.hostPort != null ? String(rec.hostPort) : "",
676
+ locale: locale || chatLanguage,
677
+ chatLanguage: chatLanguage || locale,
678
+ theme: typeof rec.theme === "string" ? rec.theme : "",
679
+ palette: typeof rec.palette === "string" ? rec.palette : "",
680
+ cardStyle,
681
+ provider: llm && typeof llm.provider === "string" ? llm.provider : "",
682
+ model: llm && typeof llm.model === "string" ? llm.model : ""
683
+ };
684
+ }
685
+ function formToHostConfigBody(form) {
686
+ const provider = (form.provider ?? "").trim();
687
+ const model = (form.model ?? "").trim();
688
+ const locale = form.locale || form.chatLanguage;
689
+ const body = { locale, chatLanguage: form.chatLanguage || locale, theme: form.theme, palette: form.palette };
690
+ const port = Number(form.hostPort);
691
+ if (Number.isInteger(port) && port >= 0 && port <= 65535) body.hostPort = port;
692
+ if (provider && model) body.llm = { provider, model };
693
+ else if (!provider && !model) body.llm = null;
694
+ return body;
695
+ }
696
+ function asCommit(raw) {
697
+ if (!isRecord(raw) || typeof raw.id !== "string") return null;
698
+ const files = Array.isArray(raw.files) ? raw.files.flatMap((f) => {
699
+ if (!isRecord(f) || typeof f.path !== "string") return [];
700
+ return [{ path: f.path, add: typeof f.add === "number" ? f.add : void 0, del: typeof f.del === "number" ? f.del : void 0, preview: typeof f.preview === "string" ? f.preview : void 0 }];
701
+ }) : void 0;
702
+ return { id: raw.id, message: typeof raw.message === "string" ? raw.message : "", time: typeof raw.time === "string" ? raw.time : "", files };
703
+ }
704
+ function parseCommitList(raw) {
705
+ const rec = isRecord(raw) ? raw : {};
706
+ const list = Array.isArray(rec.commits) ? rec.commits : Array.isArray(rec.nodes) ? rec.nodes : [];
707
+ return list.map((c) => asCommit(c)).filter((c) => c !== null);
708
+ }
709
+ function parseCommitDetail(raw, id) {
710
+ const rec = isRecord(raw) ? raw : {};
711
+ const inner = isRecord(rec.commit) ? rec.commit : rec;
712
+ return asCommit(inner) ?? { id, message: "", time: "", files: [] };
713
+ }
714
+ function parseStorageTables(raw) {
715
+ const rec = isRecord(raw) ? raw : {};
716
+ const list = Array.isArray(rec.tables) ? rec.tables : [];
717
+ const out = [];
718
+ for (const row of list) {
719
+ if (!isRecord(row) || typeof row.name !== "string") continue;
720
+ out.push({
721
+ name: row.name,
722
+ size: typeof row.size === "number" ? row.size : void 0,
723
+ updatedAt: typeof row.updatedAt === "string" ? row.updatedAt : void 0,
724
+ keys: typeof row.keys === "number" ? row.keys : void 0
725
+ });
726
+ }
727
+ return out;
728
+ }
729
+ function parseStorageNotices(raw, appId) {
730
+ const rec = isRecord(raw) ? raw : {};
731
+ const list = Array.isArray(rec.notices) ? rec.notices : [];
732
+ const out = [];
733
+ for (const row of list) {
734
+ if (!isRecord(row) || typeof row.table !== "string" || typeof row.prompt !== "string") continue;
735
+ const heavyRaw = Array.isArray(row.heavy) ? row.heavy : [];
736
+ const heavy = heavyRaw.flatMap((h) => {
737
+ if (!isRecord(h) || typeof h.key !== "string" || typeof h.bytes !== "number") return [];
738
+ const kind = h.kind === "list" || h.kind === "map" || h.kind === "value" ? h.kind : "value";
739
+ return [{
740
+ key: h.key,
741
+ bytes: h.bytes,
742
+ kind,
743
+ entries: typeof h.entries === "number" ? h.entries : void 0
744
+ }];
745
+ });
746
+ out.push({
747
+ appId: typeof row.appId === "string" ? row.appId : appId,
748
+ table: row.table,
749
+ bytes: typeof row.bytes === "number" ? row.bytes : 0,
750
+ keys: typeof row.keys === "number" ? row.keys : 0,
751
+ heavy,
752
+ prompt: row.prompt
753
+ });
754
+ }
755
+ return out;
756
+ }
757
+ function parsePalettes(raw) {
758
+ const rec = isRecord(raw) ? raw : {};
759
+ const list = Array.isArray(rec.palettes) ? rec.palettes : [];
760
+ const out = [];
761
+ for (const p of list) {
762
+ if (!isRecord(p) || typeof p.id !== "string") continue;
763
+ if (p.custom === false) continue;
764
+ out.push({
765
+ id: p.id,
766
+ label: typeof p.label === "string" ? p.label : p.id,
767
+ swatch: typeof p.swatch === "string" ? p.swatch : "#888",
768
+ tokens: isRecord(p.tokens) ? p.tokens : void 0
769
+ });
770
+ }
771
+ return out;
772
+ }
773
+ function createRestPanelHost(opts) {
774
+ const storage = opts.storage ?? (typeof window !== "undefined" ? window.localStorage : null);
775
+ function origin() {
776
+ return (opts.getHostUrl ? opts.getHostUrl() : opts.hostUrl).replace(/\/$/, "");
777
+ }
778
+ const host = {
779
+ locale: opts.locale,
780
+ emptyText: opts.emptyText,
781
+ fetchApps: async () => parseAppsResponse(await readJson(`${origin()}/api/apps`)),
782
+ palettes: async () => {
783
+ try {
784
+ return parsePalettes(await readJson(`${origin()}/api/palettes`));
785
+ } catch {
786
+ return [];
787
+ }
512
788
  },
513
- dark: {
514
- bg: "#28242b",
515
- fg: "#e9d8d2",
516
- surface: "#302b33",
517
- surfaceFg: "#e9d8d2",
518
- border: "#443a44",
519
- muted: "#38313a",
520
- mutedFg: "#b3a09f",
521
- primary: "#ee9aa6",
522
- primaryFg: "#28242b",
523
- secondary: "#38313a",
524
- secondaryFg: "#e9d8d2",
525
- accent: "#3f4a38",
526
- accentFg: "#c8e3b2",
527
- destructive: "#e06c8a",
528
- destructiveFg: "#28242b",
529
- ring: "#ee9aa6",
530
- input: "#231f26",
531
- radius: "14px",
532
- shadow: "rgba(0, 0, 0, 0.45)"
533
- }
534
- },
535
- yellow: {
536
- light: {
537
- bg: "#faf6ef",
538
- fg: "#3d3326",
539
- surface: "#ffffff",
540
- surfaceFg: "#3d3326",
541
- border: "#e8dfcd",
542
- muted: "#f2ecdf",
543
- mutedFg: "#9a8a6f",
544
- primary: "#a37b16",
545
- primaryFg: "#ffffff",
546
- secondary: "#ede5d3",
547
- secondaryFg: "#3d3326",
548
- accent: "#f7edcb",
549
- accentFg: "#6b4e0a",
550
- destructive: "#dc2626",
551
- destructiveFg: "#ffffff",
552
- ring: "#a37b16",
553
- input: "#f7f1e4",
554
- radius: "12px",
555
- shadow: "rgba(140, 110, 40, 0.12)"
789
+ openPanel: () => opts.onOpen?.(),
790
+ closePanel: () => opts.onClose?.(),
791
+ persistTheme: (theme, palette) => {
792
+ try {
793
+ storage?.setItem("mma-theme-mode", theme);
794
+ storage?.setItem("mma-palette", palette);
795
+ } catch {
796
+ }
797
+ void fetch(`${origin()}/api/host-config`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ theme, palette }) }).catch(() => {
798
+ });
556
799
  },
557
- dark: {
558
- bg: "#1c1917",
559
- fg: "#f5f0e6",
560
- surface: "#242019",
561
- surfaceFg: "#f5f0e6",
562
- border: "#3a332a",
563
- muted: "#2a2520",
564
- mutedFg: "#b3a68f",
565
- primary: "#facc15",
566
- primaryFg: "#1c1917",
567
- secondary: "#2a2520",
568
- secondaryFg: "#f5f0e6",
569
- accent: "#3f3518",
570
- accentFg: "#fde68a",
571
- destructive: "#f87171",
572
- destructiveFg: "#1c1917",
573
- ring: "#facc15",
574
- input: "#211d18",
575
- radius: "12px",
576
- shadow: "rgba(0, 0, 0, 0.5)"
577
- }
578
- },
579
- zoro: {
580
- light: {
581
- bg: "#f2f6f0",
582
- fg: "#24302a",
583
- surface: "#ffffff",
584
- surfaceFg: "#24302a",
585
- border: "#d8e4d6",
586
- muted: "#e9efe6",
587
- mutedFg: "#7d907e",
588
- primary: "#15803d",
589
- primaryFg: "#ffffff",
590
- secondary: "#e3ecdf",
591
- secondaryFg: "#24302a",
592
- accent: "#dcf2e2",
593
- accentFg: "#166534",
594
- destructive: "#dc2626",
595
- destructiveFg: "#ffffff",
596
- ring: "#15803d",
597
- input: "#f4f8f2",
598
- radius: "12px",
599
- shadow: "rgba(30, 80, 50, 0.1)"
800
+ appTheme: {
801
+ save: async (appId, t2) => {
802
+ await fetch(`${origin()}/api/apps/${encodeURIComponent(appId)}/theme`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(t2) }).catch(() => {
803
+ });
804
+ },
805
+ clear: async (appId) => {
806
+ await fetch(`${origin()}/api/apps/${encodeURIComponent(appId)}/theme`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ reset: true }) }).catch(() => {
807
+ });
808
+ }
600
809
  },
601
- dark: {
602
- bg: "#101512",
603
- fg: "#d8e0d4",
604
- surface: "#18211b",
605
- surfaceFg: "#d8e0d4",
606
- border: "#2c3a30",
607
- muted: "#1d2720",
608
- mutedFg: "#93a393",
609
- primary: "#4ade80",
610
- primaryFg: "#101512",
611
- secondary: "#1d2720",
612
- secondaryFg: "#d8e0d4",
613
- accent: "#14532d",
614
- accentFg: "#bbf7d0",
615
- destructive: "#f87171",
616
- destructiveFg: "#101512",
617
- ring: "#4ade80",
618
- input: "#141b16",
619
- radius: "12px",
620
- shadow: "rgba(0, 0, 0, 0.5)"
621
- }
622
- },
623
- hokage: {
624
- light: {
625
- bg: "#faf3ea",
626
- fg: "#4a3a2c",
627
- surface: "#ffffff",
628
- surfaceFg: "#4a3a2c",
629
- border: "#eadcc8",
630
- muted: "#f3e9db",
631
- mutedFg: "#a0886a",
632
- primary: "#c2660f",
633
- primaryFg: "#ffffff",
634
- secondary: "#efe2d0",
635
- secondaryFg: "#4a3a2c",
636
- accent: "#fbe6c8",
637
- accentFg: "#7c4a10",
638
- destructive: "#dc2626",
639
- destructiveFg: "#ffffff",
640
- ring: "#c2660f",
641
- input: "#f8f0e4",
642
- radius: "12px",
643
- shadow: "rgba(150, 90, 30, 0.12)"
810
+ config: {
811
+ load: async () => hostConfigToForm(await readJson(`${origin()}/api/host-config`), opts.getCardStyle?.() ?? opts.cardStyle ?? "stamp"),
812
+ save: async (cfg) => {
813
+ const body = formToHostConfigBody(cfg);
814
+ const res = await fetch(`${origin()}/api/host-config`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) });
815
+ const raw = await res.json().catch(() => ({}));
816
+ if (!res.ok || isRecord(raw) && raw.ok === false) {
817
+ throw new Error(isRecord(raw) && typeof raw.error === "string" ? raw.error : `HTTP ${res.status}`);
818
+ }
819
+ const hostPort = isRecord(raw) ? raw.hostPort : void 0;
820
+ if (typeof hostPort === "number" && Number.isInteger(hostPort) && hostPort > 0) {
821
+ const next = `${new URL(origin()).protocol}//${new URL(origin()).hostname}:${hostPort}`;
822
+ if (next !== origin()) opts.onHostChange?.(next);
823
+ }
824
+ opts.onConfigSaved?.(cfg);
825
+ }
644
826
  },
645
- dark: {
646
- bg: "#241a17",
647
- fg: "#f0e3d8",
648
- surface: "#2e211c",
649
- surfaceFg: "#f0e3d8",
650
- border: "#45372e",
651
- muted: "#33251f",
652
- mutedFg: "#b59f8c",
653
- primary: "#f59e0b",
654
- primaryFg: "#241a17",
655
- secondary: "#33251f",
656
- secondaryFg: "#f0e3d8",
657
- accent: "#4a3024",
658
- accentFg: "#fcd9b6",
659
- destructive: "#ef4444",
660
- destructiveFg: "#241a17",
661
- ring: "#f59e0b",
662
- input: "#201612",
663
- radius: "12px",
664
- shadow: "rgba(0, 0, 0, 0.5)"
665
- }
666
- },
667
- slate: {
668
- light: {
669
- bg: "#ececee",
670
- fg: "#09090b",
671
- surface: "#ffffff",
672
- surfaceFg: "#09090b",
673
- border: "#c8c8ce",
674
- muted: "#e0e0e5",
675
- mutedFg: "#52525b",
676
- primary: "#27272a",
677
- primaryFg: "#fafafa",
678
- secondary: "#e0e0e5",
679
- secondaryFg: "#09090b",
680
- accent: "#d4d4d8",
681
- accentFg: "#18181b",
682
- destructive: "#dc2626",
683
- destructiveFg: "#ffffff",
684
- ring: "#27272a",
685
- input: "#c8c8ce",
686
- radius: "8px",
687
- shadow: "rgba(24, 24, 27, 0.12)"
827
+ about: {
828
+ load: async () => parseAbout(await readJson(`${origin()}/api/about`)),
829
+ checkUpdates: async () => parseUpdateCheck(await readJson(`${origin()}/api/updates`))
688
830
  },
689
- dark: {
690
- bg: "#050506",
691
- fg: "#fafafa",
692
- surface: "#141416",
693
- surfaceFg: "#fafafa",
694
- border: "#3f3f46",
695
- muted: "#1f1f23",
696
- mutedFg: "#a1a1aa",
697
- primary: "#e4e4e7",
698
- primaryFg: "#09090b",
699
- secondary: "#1f1f23",
700
- secondaryFg: "#fafafa",
701
- accent: "#27272a",
702
- accentFg: "#f4f4f5",
703
- destructive: "#f87171",
704
- destructiveFg: "#09090b",
705
- ring: "#e4e4e7",
706
- input: "#3f3f46",
707
- radius: "8px",
708
- shadow: "rgba(0, 0, 0, 0.6)"
831
+ history: {
832
+ list: async (appId) => parseCommitList(await readJson(`${origin()}/api/apps/${encodeURIComponent(appId)}/history?limit=50`)),
833
+ detail: async (appId, id) => parseCommitDetail(await readJson(`${origin()}/api/apps/${encodeURIComponent(appId)}/history/${encodeURIComponent(id)}`), id)
834
+ },
835
+ storage: {
836
+ listTables: async (appId) => {
837
+ const raw = await readJson(`${origin()}/api/apps/${encodeURIComponent(appId)}/storage`);
838
+ return {
839
+ tables: parseStorageTables(raw),
840
+ notices: parseStorageNotices(raw, appId)
841
+ };
842
+ },
843
+ readTable: async (appId, name2) => {
844
+ const raw = await readJson(`${origin()}/api/apps/${encodeURIComponent(appId)}/storage/${encodeURIComponent(name2)}`);
845
+ return isRecord(raw) && "value" in raw ? raw.value : raw;
846
+ }
847
+ },
848
+ deleteApp: opts.deleteApp ?? (async (appId) => {
849
+ await fetch(`${origin()}/api/app/${encodeURIComponent(appId)}`, { method: "DELETE" }).catch(() => {
850
+ });
851
+ }),
852
+ frame: opts.frameController ?? {
853
+ url: (appId) => `${origin()}/app/${encodeURIComponent(appId)}`,
854
+ mount: () => void 0,
855
+ unmount: () => void 0,
856
+ reload: () => void 0,
857
+ syncEnv: () => void 0
709
858
  }
710
- }
859
+ };
860
+ return host;
861
+ }
862
+
863
+ // ../panel/src/store.ts
864
+ var import_react = require("react");
865
+ var none = {
866
+ history: false,
867
+ storage: false,
868
+ config: false,
869
+ appTheme: false,
870
+ customPalettes: false,
871
+ deleteApp: false
711
872
  };
712
- function clampPalette(value) {
713
- if (value === "tokyo" || value === "forest" || value === "matcha" || value === "yellow" || value === "zoro" || value === "hokage" || value === "slate" || value === "default") {
714
- return value;
715
- }
716
- if (value === "ocean" || value === "mist") return "tokyo";
717
- if (value === "violet" || value === "ink") return "matcha";
718
- if (value === "paper" || value === "noir") return "slate";
719
- return "default";
873
+ var initial = {
874
+ tabs: [{ id: "all", title: "\u5168\u90E8", kind: "all" }],
875
+ active: "all",
876
+ apps: [],
877
+ error: null,
878
+ loading: false,
879
+ query: "",
880
+ dock: "fill",
881
+ theme: "light",
882
+ palette: "default",
883
+ themeScope: "global",
884
+ customPalettes: {},
885
+ cardStyle: "stamp",
886
+ visible: false,
887
+ pendingDelete: null,
888
+ themePopOpen: false,
889
+ settingsOpen: false,
890
+ cfgMsg: "",
891
+ cfgVersion: 0,
892
+ cfg: {},
893
+ about: null,
894
+ updateCheck: null,
895
+ emptyText: void 0,
896
+ capabilities: none,
897
+ locale: "zh-CN",
898
+ browseOpen: false,
899
+ browseKind: "history",
900
+ browseAppId: null,
901
+ browseAppName: "",
902
+ browseLoading: false,
903
+ browseError: null,
904
+ browseList: [],
905
+ browseDetail: null,
906
+ browseTable: null,
907
+ browseTableValue: null,
908
+ browseOpenFile: null,
909
+ storageNotice: null
910
+ };
911
+ var state = { ...initial, capabilities: { ...none } };
912
+ var listeners = /* @__PURE__ */ new Set();
913
+ function getPanelState() {
914
+ return state;
720
915
  }
721
- function clampMode(value) {
722
- return value === "dark" ? "dark" : "light";
916
+ function setPanelState(patch) {
917
+ state = { ...state, ...patch };
918
+ for (const fn of listeners) fn();
919
+ return state;
723
920
  }
724
- function tokensOf(palette, mode) {
725
- return TOKENS[clampPalette(palette)][clampMode(mode)];
921
+ function subscribePanel(listener) {
922
+ listeners.add(listener);
923
+ return () => {
924
+ listeners.delete(listener);
925
+ };
726
926
  }
727
- function applyThemeTo(el, theme, palette, custom) {
728
- const mode = clampMode(theme);
729
- const c = custom && palette ? custom[String(palette)] : void 0;
730
- const pal = c ? String(palette) : clampPalette(palette);
731
- const t2 = c && c.tokens && c.tokens[mode] || tokensOf(pal, mode);
732
- el.setAttribute("data-theme", mode);
733
- el.setAttribute("data-palette", pal);
734
- const map = {
735
- "--dsw-alias-bg": t2.bg,
736
- "--dsw-alias-fg": t2.fg,
737
- "--dsw-alias-surface": t2.surface,
738
- "--dsw-alias-border": t2.border,
739
- "--dsw-alias-muted": t2.muted,
740
- "--dsw-alias-primary": t2.primary,
741
- "--dsw-alias-primary-fg": t2.primaryFg,
742
- "--dsw-alias-accent": t2.accent,
743
- "--dsw-alias-shadow": t2.shadow,
744
- "--background": t2.bg,
745
- "--foreground": t2.fg,
746
- "--card": t2.surface,
747
- "--card-foreground": t2.surfaceFg,
748
- "--primary": t2.primary,
749
- "--primary-foreground": t2.primaryFg,
750
- "--secondary": t2.secondary,
751
- "--secondary-foreground": t2.secondaryFg,
752
- "--muted": t2.muted,
753
- "--muted-foreground": t2.mutedFg,
754
- "--accent": t2.accent,
755
- "--accent-foreground": t2.accentFg,
756
- "--border": t2.border,
757
- "--input": t2.input,
758
- "--ring": t2.ring,
759
- "--destructive": t2.destructive,
760
- "--destructive-foreground": t2.destructiveFg,
761
- "--radius": t2.radius,
762
- "--shadow": t2.shadow
927
+ function resetPanelState(seed) {
928
+ state = {
929
+ ...initial,
930
+ capabilities: { ...none },
931
+ customPalettes: {},
932
+ cfg: {},
933
+ tabs: [{ id: "all", title: "\u5168\u90E8", kind: "all" }],
934
+ ...seed || {}
763
935
  };
764
- for (const k of Object.keys(map)) el.style.setProperty(k, map[k]);
765
- el.style.background = t2.bg;
766
- el.style.color = t2.fg;
767
- return { theme: mode, palette: pal };
936
+ for (const fn of listeners) fn();
937
+ return state;
938
+ }
939
+ function usePanelState() {
940
+ return (0, import_react.useSyncExternalStore)(subscribePanel, getPanelState);
768
941
  }
769
942
 
770
943
  // ../panel/src/actions.ts
771
944
  function activeAppFrom(state2) {
772
- const tab = state2.tabs.find((t2) => t2.kind === "app" && t2.id === state2.active) || state2.tabs.find((t2) => t2.kind === "app");
945
+ const tab = state2.tabs.find((t2) => t2.kind === "app" && t2.id === state2.active);
773
946
  return tab?.app ?? null;
774
947
  }
775
948
  function errorMessage(err) {
@@ -786,6 +959,17 @@ function pushAppTab(app) {
786
959
  function asCustomPalettes(value) {
787
960
  return value;
788
961
  }
962
+ function loadCustomPalettes(host) {
963
+ if (!host.palettes) return;
964
+ host.palettes().then((list) => {
965
+ const custom = {};
966
+ for (const p of list) {
967
+ custom[p.id] = { label: p.label, swatch: p.swatch, tokens: p.tokens };
968
+ }
969
+ setPanelState({ customPalettes: custom });
970
+ }).catch(() => {
971
+ });
972
+ }
789
973
  function createPanelActions(host, getRootEl, i18n) {
790
974
  setPanelState({ capabilities: capabilitiesOf(host), locale: i18n.locale, emptyText: host.emptyText });
791
975
  return {
@@ -800,14 +984,18 @@ function createPanelActions(host, getRootEl, i18n) {
800
984
  const tab = s.tabs.find((t2) => t2.id === id);
801
985
  setPanelState({
802
986
  tabs: s.tabs.filter((t2) => t2.id !== id),
803
- active: s.active === id ? "all" : s.active
987
+ active: s.active === id ? "all" : s.active,
988
+ ...s.active === id ? { themeScope: "global" } : {}
804
989
  });
805
990
  if (tab?.app) host.frame.unmount(tab.app.id);
806
991
  },
807
992
  switchTab: (id) => {
808
- setPanelState({ active: id });
809
993
  const s = getPanelState();
810
994
  const tab = s.tabs.find((t2) => t2.id === id && t2.kind === "app");
995
+ setPanelState({
996
+ active: id,
997
+ ...!tab?.app ? { themeScope: "global" } : {}
998
+ });
811
999
  if (tab?.app) host.frame.mount(tab.app.id);
812
1000
  },
813
1001
  setDock: (next) => {
@@ -818,31 +1006,38 @@ function createPanelActions(host, getRootEl, i18n) {
818
1006
  }
819
1007
  },
820
1008
  setQuery: (q) => setPanelState({ query: q }),
821
- toggleThemePop: () => setPanelState({ themePopOpen: !getPanelState().themePopOpen }),
1009
+ toggleThemePop: () => {
1010
+ const next = !getPanelState().themePopOpen;
1011
+ setPanelState({ themePopOpen: next });
1012
+ if (next) loadCustomPalettes(host);
1013
+ },
822
1014
  setAppearance: (next, scope) => {
823
1015
  const s = getPanelState();
824
- const theme = next.theme ? String(next.theme) : s.theme;
825
- const palette = next.palette ? String(next.palette) : s.palette;
826
- setPanelState({ theme, palette });
827
- const root = getRootEl();
828
- if (root) {
829
- applyThemeTo(root, theme, palette, asCustomPalettes(s.customPalettes));
830
- }
831
1016
  if (scope === "app") {
832
- const app = activeAppFrom(getPanelState());
1017
+ const app = activeAppFrom(s);
833
1018
  if (app && host.appTheme) {
834
- const nextTheme = { theme, palette };
1019
+ const base = app.theme ?? { theme: s.theme, palette: s.palette };
1020
+ const nextTheme = {
1021
+ theme: next.theme ? String(next.theme) : base.theme,
1022
+ palette: next.palette ? String(next.palette) : base.palette
1023
+ };
835
1024
  host.appTheme.save(app.id, nextTheme).catch(() => {
836
1025
  });
837
1026
  const cur = getPanelState();
1027
+ const patchApp = (a) => a.id === app.id ? { ...a, theme: nextTheme } : a;
838
1028
  setPanelState({
839
- apps: cur.apps.some((a) => a.id === app.id) ? cur.apps.map((a) => a.id === app.id ? { ...a, theme: nextTheme } : a) : [...cur.apps, { ...app, theme: nextTheme }],
840
- tabs: cur.tabs.map(
841
- (t2) => t2.app?.id === app.id ? { ...t2, app: { ...t2.app, theme: nextTheme } } : t2
842
- )
1029
+ apps: cur.apps.some((a) => a.id === app.id) ? cur.apps.map(patchApp) : [...cur.apps, { ...app, theme: nextTheme }],
1030
+ tabs: cur.tabs.map((t2) => t2.app?.id === app.id ? { ...t2, app: { ...t2.app, theme: nextTheme } } : t2)
843
1031
  });
844
1032
  }
845
1033
  } else {
1034
+ const theme = next.theme ? String(next.theme) : s.theme;
1035
+ const palette = next.palette ? String(next.palette) : s.palette;
1036
+ setPanelState({ theme, palette });
1037
+ const root = getRootEl();
1038
+ if (root) {
1039
+ applyThemeTo(root, theme, palette, asCustomPalettes(s.customPalettes));
1040
+ }
846
1041
  host.persistTheme?.(theme, palette);
847
1042
  }
848
1043
  host.frame.syncEnv?.();
@@ -865,23 +1060,71 @@ function createPanelActions(host, getRootEl, i18n) {
865
1060
  },
866
1061
  getActiveApp: () => activeAppFrom(getPanelState()),
867
1062
  toggleSettings: (open) => {
868
- setPanelState({ settingsOpen: open, cfgMsg: open ? "" : getPanelState().cfgMsg });
1063
+ setPanelState({
1064
+ settingsOpen: open,
1065
+ cfgMsg: open ? "" : getPanelState().cfgMsg,
1066
+ updateCheck: open ? null : getPanelState().updateCheck
1067
+ });
869
1068
  if (open && host.config) {
870
1069
  host.config.load().then((cfg) => {
871
1070
  setPanelState({ cfg, cfgVersion: getPanelState().cfgVersion + 1 });
872
1071
  }).catch(() => {
873
1072
  });
874
1073
  }
1074
+ if (open && host.about) {
1075
+ host.about.load().then((about) => setPanelState({ about })).catch(() => setPanelState({ about: null }));
1076
+ }
875
1077
  },
876
1078
  getCfg: () => getPanelState().cfg,
1079
+ checkUpdates: () => {
1080
+ if (!host.about) return;
1081
+ setPanelState({
1082
+ updateCheck: {
1083
+ name: "",
1084
+ current: "",
1085
+ latest: null,
1086
+ updateAvailable: false,
1087
+ status: "loading"
1088
+ }
1089
+ });
1090
+ host.about.checkUpdates().then((check) => setPanelState({ updateCheck: { ...check, status: "done" } })).catch(
1091
+ (err) => setPanelState({
1092
+ updateCheck: {
1093
+ name: "",
1094
+ current: "",
1095
+ latest: null,
1096
+ updateAvailable: false,
1097
+ status: "done",
1098
+ error: errorMessage(err)
1099
+ }
1100
+ })
1101
+ );
1102
+ },
877
1103
  saveHostConfig: (form) => {
878
1104
  if (!host.config) return;
879
1105
  host.config.save(form).then(() => {
1106
+ const style = form.cardStyle || getPanelState().cardStyle;
1107
+ const theme = form.theme || getPanelState().theme;
1108
+ const palette = form.palette || getPanelState().palette;
880
1109
  setPanelState({
881
1110
  cfg: { ...form },
882
1111
  cfgMsg: i18n.t("config.saved"),
883
- cfgVersion: getPanelState().cfgVersion + 1
1112
+ cfgVersion: getPanelState().cfgVersion + 1,
1113
+ cardStyle: style,
1114
+ theme,
1115
+ palette
884
1116
  });
1117
+ const root = getRootEl();
1118
+ if (root) {
1119
+ applyThemeTo(root, theme, palette, asCustomPalettes(getPanelState().customPalettes));
1120
+ root.setAttribute("data-cardstyle", style);
1121
+ }
1122
+ host.persistTheme?.(theme, palette);
1123
+ host.frame.syncEnv?.();
1124
+ try {
1125
+ localStorage.setItem("mma-card-style", style);
1126
+ } catch {
1127
+ }
885
1128
  }).catch((e) => {
886
1129
  setPanelState({ cfgMsg: i18n.t("config.error", { message: errorMessage(e) }) });
887
1130
  });
@@ -915,11 +1158,22 @@ function createPanelActions(host, getRootEl, i18n) {
915
1158
  setPanelState({ browseList: list, browseLoading: false });
916
1159
  }).catch((e) => setPanelState({ browseError: errorMessage(e), browseLoading: false }));
917
1160
  } else if (host.storage) {
918
- host.storage.listTables(app.id).then((list) => {
919
- setPanelState({ browseList: list, browseLoading: false });
1161
+ host.storage.listTables(app.id).then(({ tables, notices }) => {
1162
+ const notice = notices[0] ?? null;
1163
+ setPanelState({
1164
+ browseList: tables,
1165
+ browseLoading: false,
1166
+ ...notice ? { storageNotice: notice } : {}
1167
+ });
920
1168
  }).catch((e) => setPanelState({ browseError: errorMessage(e), browseLoading: false }));
921
1169
  }
922
1170
  },
1171
+ dismissStorageNotice: () => {
1172
+ setPanelState({ storageNotice: null });
1173
+ },
1174
+ applyStorageNotice: (notice) => {
1175
+ setPanelState({ storageNotice: notice });
1176
+ },
923
1177
  loadCommitDetail: (id) => {
924
1178
  const s = getPanelState();
925
1179
  if (!host.history || !s.browseAppId) return;
@@ -978,7 +1232,14 @@ function createPanelActions(host, getRootEl, i18n) {
978
1232
  });
979
1233
  });
980
1234
  },
981
- setCardStyle: (v) => setPanelState({ cardStyle: v })
1235
+ setCardStyle: (v) => {
1236
+ setPanelState({ cardStyle: v });
1237
+ getRootEl()?.setAttribute("data-cardstyle", v);
1238
+ try {
1239
+ localStorage.setItem("mma-card-style", v);
1240
+ } catch {
1241
+ }
1242
+ }
982
1243
  };
983
1244
  }
984
1245
  function defaultHideThemePop() {
@@ -1071,6 +1332,7 @@ function AppCard({ app }) {
1071
1332
  {
1072
1333
  type: "button",
1073
1334
  className: "mma-card",
1335
+ "data-app-id": app.id,
1074
1336
  style: { "--h": hue(app.id) },
1075
1337
  onClick: () => actions.openAppTab(app),
1076
1338
  children: [
@@ -1099,6 +1361,7 @@ function AppRow({ app }) {
1099
1361
  {
1100
1362
  type: "button",
1101
1363
  className: "mma-row",
1364
+ "data-app-id": app.id,
1102
1365
  style: { "--h": hue(app.id) },
1103
1366
  onClick: () => actions.openAppTab(app),
1104
1367
  children: [
@@ -1210,19 +1473,53 @@ function CommitItem({ c }) {
1210
1473
  ] })
1211
1474
  ] });
1212
1475
  }
1476
+ function humanBytes(n, t2) {
1477
+ if (n < 1024) return `${n} B`;
1478
+ if (n < 1024 * 1024) return t2("browse.sizeKb", { value: (n / 1024).toFixed(1) });
1479
+ return t2("browse.sizeMb", { value: (n / (1024 * 1024)).toFixed(1) });
1480
+ }
1213
1481
  function StorageItem({ table }) {
1214
1482
  const actions = usePanelActions();
1483
+ const { t: t2 } = usePanelI18n();
1215
1484
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("button", { type: "button", className: "mma-bitem", onClick: () => actions.loadTable(table.name), children: [
1216
1485
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("b", { children: table.name }),
1217
1486
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "meta", children: [
1218
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { children: [
1219
- table.size || 0,
1220
- " B"
1221
- ] }),
1487
+ typeof table.keys === "number" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: t2("browse.entries", { count: table.keys }) }) : null,
1488
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: humanBytes(table.size || 0, t2) }),
1222
1489
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: table.updatedAt || "" })
1223
1490
  ] })
1224
1491
  ] });
1225
1492
  }
1493
+ function StorageNoticeBanner() {
1494
+ const s = usePanelState();
1495
+ const actions = usePanelActions();
1496
+ const { t: t2 } = usePanelI18n();
1497
+ const notice = s.storageNotice;
1498
+ const [copied, setCopied] = React2.useState(false);
1499
+ if (!notice) return null;
1500
+ if (s.browseAppId && notice.appId !== s.browseAppId) return null;
1501
+ const top = notice.heavy[0];
1502
+ const summary = top ? t2("browse.noticeTop", { key: top.key, size: humanBytes(top.bytes, t2) }) : "";
1503
+ const copyPrompt = async () => {
1504
+ try {
1505
+ await navigator.clipboard.writeText(notice.prompt);
1506
+ setCopied(true);
1507
+ window.setTimeout(() => setCopied(false), 1500);
1508
+ } catch {
1509
+ }
1510
+ };
1511
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mma-storage-notice", role: "status", children: [
1512
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mma-storage-notice-body", children: [
1513
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("b", { children: t2("browse.noticeTitle", { table: notice.table, size: humanBytes(notice.bytes, t2) }) }),
1514
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { children: t2("browse.noticeBody") }),
1515
+ summary ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "mma-storage-notice-top", children: summary }) : null
1516
+ ] }),
1517
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mma-storage-notice-actions", children: [
1518
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "mma-storage-notice-copy", onClick: () => void copyPrompt(), children: copied ? t2("browse.noticeCopied") : t2("browse.noticeCopy") }),
1519
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", onClick: () => actions.dismissStorageNotice(), children: t2("browse.noticeDismiss") })
1520
+ ] })
1521
+ ] });
1522
+ }
1226
1523
  function CommitDetail() {
1227
1524
  const s = usePanelState();
1228
1525
  const actions = usePanelActions();
@@ -1317,7 +1614,10 @@ function Browse() {
1317
1614
  ) })
1318
1615
  ] });
1319
1616
  }
1320
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mma-browse", id: "mma-browse", "data-open": "1", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mma-browse-body", id: "mma-browse-body", children: content }) });
1617
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mma-browse", id: "mma-browse", "data-open": "1", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mma-browse-body", id: "mma-browse-body", children: [
1618
+ s.browseKind === "storage" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(StorageNoticeBanner, {}) : null,
1619
+ content
1620
+ ] }) });
1321
1621
  }
1322
1622
 
1323
1623
  // ../panel/src/components/Modal.tsx
@@ -1339,79 +1639,695 @@ function Modal() {
1339
1639
  }
1340
1640
 
1341
1641
  // ../panel/src/components/Settings.tsx
1642
+ var React4 = __toESM(require("react"), 1);
1643
+
1644
+ // ../panel/src/components/OptionCardGrid.tsx
1342
1645
  var React3 = __toESM(require("react"), 1);
1343
1646
  var import_jsx_runtime6 = require("react/jsx-runtime");
1647
+ function OptionCardGrid({
1648
+ options,
1649
+ value,
1650
+ onChange,
1651
+ columns = 3,
1652
+ "aria-label": ariaLabel,
1653
+ className,
1654
+ trailing
1655
+ }) {
1656
+ const refs = React3.useRef([]);
1657
+ const values = options.map((o) => o.value);
1658
+ const focusAt = (index) => {
1659
+ const el = refs.current[index];
1660
+ el?.focus();
1661
+ };
1662
+ const onKeyDown = (e, index) => {
1663
+ const enabled = options.map((o, i) => ({ o, i })).filter((x) => !x.o.disabled);
1664
+ if (!enabled.length) return;
1665
+ const pos = enabled.findIndex((x) => x.i === index);
1666
+ if (pos < 0) return;
1667
+ let nextPos = pos;
1668
+ if (e.key === "ArrowRight" || e.key === "ArrowDown") {
1669
+ e.preventDefault();
1670
+ nextPos = (pos + 1) % enabled.length;
1671
+ } else if (e.key === "ArrowLeft" || e.key === "ArrowUp") {
1672
+ e.preventDefault();
1673
+ nextPos = (pos - 1 + enabled.length) % enabled.length;
1674
+ } else if (e.key === "Home") {
1675
+ e.preventDefault();
1676
+ nextPos = 0;
1677
+ } else if (e.key === "End") {
1678
+ e.preventDefault();
1679
+ nextPos = enabled.length - 1;
1680
+ } else {
1681
+ return;
1682
+ }
1683
+ const next = enabled[nextPos];
1684
+ if (!next) return;
1685
+ focusAt(next.i);
1686
+ if (!next.o.disabled) onChange(next.o.value);
1687
+ };
1688
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1689
+ "div",
1690
+ {
1691
+ className: `mma-optgrid mma-optgrid--${columns}${className ? ` ${className}` : ""}`,
1692
+ role: "radiogroup",
1693
+ "aria-label": ariaLabel,
1694
+ children: [
1695
+ options.map((opt, index) => {
1696
+ const checked = value === opt.value;
1697
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1698
+ "button",
1699
+ {
1700
+ type: "button",
1701
+ role: "radio",
1702
+ "aria-checked": checked,
1703
+ "aria-label": opt.hint ? `${opt.label}. ${opt.hint}` : opt.label,
1704
+ disabled: opt.disabled,
1705
+ tabIndex: checked || !value && index === 0 ? 0 : -1,
1706
+ className: "mma-optcard",
1707
+ "data-on": checked ? "1" : "0",
1708
+ ref: (el) => {
1709
+ refs.current[index] = el;
1710
+ },
1711
+ onClick: () => {
1712
+ if (!opt.disabled) onChange(opt.value);
1713
+ },
1714
+ onKeyDown: (e) => onKeyDown(e, index),
1715
+ children: [
1716
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mma-optcard-preview", "aria-hidden": "true", children: opt.preview }),
1717
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mma-optcard-label", children: opt.label }),
1718
+ checked ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mma-optcard-check", "aria-hidden": "true", children: "\u2713" }) : null
1719
+ ]
1720
+ },
1721
+ opt.value
1722
+ );
1723
+ }),
1724
+ trailing ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1725
+ "button",
1726
+ {
1727
+ type: "button",
1728
+ className: "mma-optcard mma-optcard--ghost",
1729
+ disabled: trailing.disabled !== false,
1730
+ title: trailing.hint,
1731
+ onClick: () => trailing.onClick?.(),
1732
+ children: [
1733
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mma-optcard-preview mma-optcard-preview--plus", "aria-hidden": "true", children: "+" }),
1734
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mma-optcard-label", children: trailing.label })
1735
+ ]
1736
+ }
1737
+ ) : null,
1738
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { hidden: true, children: values.join(",") })
1739
+ ]
1740
+ }
1741
+ );
1742
+ }
1743
+
1744
+ // ../panel/src/components/Settings.tsx
1745
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1746
+ var SECTIONS = ["appearance", "network", "model", "about"];
1747
+ var DEFAULTS = {
1748
+ hostPort: "17880",
1749
+ provider: "",
1750
+ model: "",
1751
+ locale: "zh-CN",
1752
+ theme: "light",
1753
+ palette: "default",
1754
+ cardStyle: "stamp"
1755
+ };
1756
+ function shortPkg(name2) {
1757
+ return name2.replace(/^@monkey-mini-app\//, "");
1758
+ }
1759
+ function validatePort(raw) {
1760
+ const trimmed = raw.trim();
1761
+ if (!trimmed) return "empty";
1762
+ if (!/^\d+$/.test(trimmed)) return "nan";
1763
+ const n = Number(trimmed);
1764
+ if (!Number.isInteger(n) || n < 1024 || n > 65535) return "range";
1765
+ return null;
1766
+ }
1767
+ function ThemePreview({ mode }) {
1768
+ if (mode === "system") {
1769
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "mma-preview-theme mma-preview-theme--system", children: [
1770
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "mma-preview-theme-half mma-preview-theme-half--light" }),
1771
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "mma-preview-theme-half mma-preview-theme-half--dark" })
1772
+ ] });
1773
+ }
1774
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: `mma-preview-theme mma-preview-theme--${mode}`, children: [
1775
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "mma-preview-bar" }),
1776
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "mma-preview-bar" })
1777
+ ] });
1778
+ }
1779
+ function PalettePreview({ id, mode }) {
1780
+ const t2 = tokensOf(id, mode);
1781
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "mma-preview-swatches", children: [
1782
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { background: t2.primary } }),
1783
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { background: t2.secondary } }),
1784
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { background: t2.bg } })
1785
+ ] });
1786
+ }
1787
+ function CardStylePreview({ style }) {
1788
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: `mma-preview-cardstyle mma-preview-cardstyle--${style}` });
1789
+ }
1344
1790
  function Settings() {
1345
1791
  const s = usePanelState();
1346
1792
  const actions = usePanelActions();
1347
1793
  const { t: t2 } = usePanelI18n();
1348
- const [form, setForm] = React3.useState({});
1349
- React3.useEffect(() => {
1794
+ const [form, setForm] = React4.useState({});
1795
+ const [snapshot, setSnapshot] = React4.useState(null);
1796
+ const [section, setSection] = React4.useState("appearance");
1797
+ const [portTouched, setPortTouched] = React4.useState(false);
1798
+ const [confirm, setConfirm] = React4.useState(null);
1799
+ const [lastSavedAt, setLastSavedAt] = React4.useState(null);
1800
+ const rootRef = React4.useRef(null);
1801
+ const contentRef = React4.useRef(null);
1802
+ const sectionRefs = React4.useRef({});
1803
+ const dirtyCountRef = React4.useRef(0);
1804
+ const confirmRef = React4.useRef(confirm);
1805
+ confirmRef.current = confirm;
1806
+ const [wide, setWide] = React4.useState(false);
1807
+ React4.useEffect(() => {
1350
1808
  if (!s.settingsOpen) return;
1351
- setForm(s.cfg && Object.keys(s.cfg).length ? s.cfg : actions.getCfg());
1352
- }, [s.settingsOpen, s.cfgVersion, s.cfg, actions]);
1809
+ const next = s.cfg && Object.keys(s.cfg).length ? { ...s.cfg } : { ...actions.getCfg() };
1810
+ const snap = {
1811
+ hostPort: next.hostPort || "",
1812
+ provider: next.provider || "",
1813
+ model: next.model || "",
1814
+ theme: next.theme || s.theme || "light",
1815
+ palette: next.palette || s.palette || "default",
1816
+ cardStyle: next.cardStyle || s.cardStyle || "stamp",
1817
+ locale: next.locale || next.chatLanguage || "zh-CN"
1818
+ };
1819
+ setForm({ ...next, ...snap, chatLanguage: snap.locale });
1820
+ setSnapshot(snap);
1821
+ setPortTouched(false);
1822
+ setConfirm(null);
1823
+ setSection("appearance");
1824
+ }, [s.settingsOpen, s.cfgVersion, s.cfg, actions, s.theme, s.palette, s.cardStyle]);
1825
+ React4.useEffect(() => {
1826
+ if (!s.settingsOpen) return;
1827
+ const host = rootRef.current?.closest("#mma-host") ?? rootRef.current;
1828
+ if (!host || typeof ResizeObserver === "undefined") return;
1829
+ const ro = new ResizeObserver((entries) => {
1830
+ const w = entries[0]?.contentRect.width ?? 0;
1831
+ setWide(w >= 720);
1832
+ });
1833
+ ro.observe(host);
1834
+ return () => ro.disconnect();
1835
+ }, [s.settingsOpen]);
1836
+ React4.useEffect(() => {
1837
+ if (!s.settingsOpen) return;
1838
+ const root = contentRef.current;
1839
+ if (!root) return;
1840
+ const nodes = SECTIONS.map((id) => sectionRefs.current[id]).filter(Boolean);
1841
+ if (!nodes.length) return;
1842
+ const obs = new IntersectionObserver(
1843
+ (entries) => {
1844
+ const visible = entries.filter((e) => e.isIntersecting).sort((a, b) => b.intersectionRatio - a.intersectionRatio)[0];
1845
+ const id = visible?.target.getAttribute("data-section");
1846
+ if (id) setSection(id);
1847
+ },
1848
+ { root, threshold: [0.25, 0.5, 0.75], rootMargin: "-20% 0px -55% 0px" }
1849
+ );
1850
+ for (const n of nodes) obs.observe(n);
1851
+ return () => obs.disconnect();
1852
+ }, [s.settingsOpen, form]);
1853
+ React4.useEffect(() => {
1854
+ if (!s.settingsOpen) return;
1855
+ const onKey = (e) => {
1856
+ if (e.key !== "Escape") return;
1857
+ e.preventDefault();
1858
+ if (confirmRef.current) {
1859
+ setConfirm(null);
1860
+ return;
1861
+ }
1862
+ if (dirtyCountRef.current > 0) setConfirm("close");
1863
+ else actions.toggleSettings(false);
1864
+ };
1865
+ window.addEventListener("keydown", onKey);
1866
+ return () => window.removeEventListener("keydown", onKey);
1867
+ }, [s.settingsOpen, actions]);
1353
1868
  if (!s.capabilities.config) return null;
1354
- const set = (k) => (e) => setForm((prev) => ({ ...prev, [k]: e.target.value }));
1355
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mma-settings", id: "mma-settings", "data-open": s.settingsOpen ? "1" : "0", children: [
1356
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mma-settings-head", children: [
1357
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h3", { children: t2("settings.title") }),
1358
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "mma-iconbtn", id: "mma-cfg-close", onClick: () => actions.toggleSettings(false), children: "\u2715" })
1359
- ] }),
1360
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
1361
- t2("settings.hostPort"),
1362
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { id: "mma-cfg-port", type: "number", value: form.hostPort || "", onChange: set("hostPort") })
1363
- ] }),
1364
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
1365
- t2("settings.language"),
1366
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("select", { id: "mma-cfg-lang", value: form.locale || form.chatLanguage || "zh-CN", onChange: set("locale"), children: [
1367
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "zh-CN", children: t2("settings.langZh") }),
1368
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "en", children: t2("settings.langEn") })
1369
- ] })
1370
- ] }),
1371
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
1372
- t2("settings.theme"),
1373
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("select", { id: "mma-cfg-theme", value: form.theme || "light", onChange: set("theme"), children: [
1374
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "light", children: t2("theme.light") }),
1375
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "dark", children: t2("theme.dark") })
1376
- ] })
1377
- ] }),
1378
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
1379
- t2("settings.palette"),
1380
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("select", { id: "mma-cfg-palette", value: form.palette || "default", onChange: set("palette"), children: PALETTES.map((p) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: p.id, children: t2(`palette.${p.id}`) }, p.id)) })
1381
- ] }),
1382
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
1383
- t2("settings.cardStyle"),
1384
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("select", { id: "mma-cfg-cardstyle", value: form.cardStyle || "stamp", onChange: set("cardStyle"), children: [
1385
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "stamp", children: t2("settings.cardStamp") }),
1386
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "etch", children: t2("settings.cardEtch") }),
1387
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "hero", children: t2("settings.cardHero") }),
1388
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "list", children: t2("settings.cardList") })
1389
- ] })
1390
- ] }),
1391
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
1392
- t2("settings.llmProvider"),
1393
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { id: "mma-cfg-provider", value: form.provider || "", onChange: set("provider") })
1394
- ] }),
1395
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
1396
- t2("settings.llmModel"),
1397
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { id: "mma-cfg-model", value: form.model || "", onChange: set("model") })
1398
- ] }),
1399
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mma-settings-actions", children: [
1400
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "mma-textbtn", id: "mma-cfg-save", onClick: () => actions.saveHostConfig(form), children: t2("settings.save") }),
1401
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mma-settings-msg", id: "mma-cfg-msg", children: s.cfgMsg })
1402
- ] })
1403
- ] });
1869
+ const themeMode = form.theme === "system" || form.theme === "dark" || form.theme === "light" ? form.theme : s.theme === "system" || s.theme === "dark" || s.theme === "light" ? s.theme : "light";
1870
+ const resolvedMode = resolveMode(themeMode === "system" ? "system" : themeMode);
1871
+ const palette = form.palette || s.palette || "default";
1872
+ const cardStyle = form.cardStyle || s.cardStyle || "stamp";
1873
+ const locale = form.locale || form.chatLanguage || "zh-CN";
1874
+ const dirty = {
1875
+ hostPort: form.hostPort || "",
1876
+ provider: form.provider || "",
1877
+ model: form.model || "",
1878
+ theme: themeMode,
1879
+ palette,
1880
+ cardStyle,
1881
+ locale
1882
+ };
1883
+ const dirtyKeys = ["hostPort", "provider", "model", "theme", "palette", "cardStyle", "locale"].filter((k) => snapshot != null && dirty[k] !== snapshot[k]);
1884
+ const dirtyCount = dirtyKeys.length;
1885
+ dirtyCountRef.current = dirtyCount;
1886
+ const portDirty = snapshot != null && dirty.hostPort !== snapshot.hostPort;
1887
+ const portErrorKey = portTouched || portDirty ? validatePort(form.hostPort || "") : null;
1888
+ const portError = portErrorKey === "empty" ? t2("settings.portEmpty") : portErrorKey === "nan" ? t2("settings.portNan") : portErrorKey === "range" ? t2("settings.portRange") : null;
1889
+ const canSave = dirtyCount > 0 && !portError;
1890
+ const hostUrl = `http://127.0.0.1:${(form.hostPort || "").trim() || "\u2014"}`;
1891
+ const paletteLabel = t2(`palette.${clampPalette(palette)}`);
1892
+ const versionLabel = s.about?.packages?.[0]?.version ?? s.about?.packages?.find((p) => p.name.includes("dsh"))?.version ?? "\u2014";
1893
+ const patch = (partial) => setForm((prev) => ({ ...prev, ...partial }));
1894
+ const paintPreview = (next) => {
1895
+ const host = document.getElementById("mma-host");
1896
+ if (!host) return;
1897
+ const theme = next.theme ?? themeMode;
1898
+ const pal = next.palette ?? palette;
1899
+ const style = next.cardStyle ?? cardStyle;
1900
+ applyThemeTo(host, theme, pal, s.customPalettes);
1901
+ host.setAttribute("data-cardstyle", style);
1902
+ };
1903
+ const previewAppearance = (partial) => {
1904
+ const nextTheme = partial.theme ?? themeMode;
1905
+ const nextPalette = partial.palette ?? palette;
1906
+ const nextCard = partial.cardStyle ?? cardStyle;
1907
+ paintPreview({ theme: nextTheme, palette: nextPalette, cardStyle: nextCard });
1908
+ patch({
1909
+ ...partial.theme != null ? { theme: partial.theme } : {},
1910
+ ...partial.palette != null ? { palette: partial.palette } : {},
1911
+ ...partial.cardStyle != null ? { cardStyle: partial.cardStyle } : {},
1912
+ ...partial.locale != null ? { locale: partial.locale, chatLanguage: partial.locale } : {}
1913
+ });
1914
+ };
1915
+ const scrollTo = (id) => {
1916
+ setSection(id);
1917
+ sectionRefs.current[id]?.scrollIntoView({ behavior: "smooth", block: "start" });
1918
+ };
1919
+ const discard = () => {
1920
+ if (!snapshot) return;
1921
+ patch({ ...snapshot, chatLanguage: snapshot.locale });
1922
+ paintPreview({
1923
+ theme: snapshot.theme,
1924
+ palette: snapshot.palette,
1925
+ cardStyle: snapshot.cardStyle
1926
+ });
1927
+ setPortTouched(false);
1928
+ };
1929
+ const requestClose = () => {
1930
+ if (dirtyCount > 0) setConfirm("close");
1931
+ else actions.toggleSettings(false);
1932
+ };
1933
+ const restoreDefaults = () => {
1934
+ previewAppearance({
1935
+ theme: DEFAULTS.theme,
1936
+ palette: DEFAULTS.palette,
1937
+ cardStyle: DEFAULTS.cardStyle,
1938
+ locale: DEFAULTS.locale
1939
+ });
1940
+ patch({
1941
+ hostPort: DEFAULTS.hostPort,
1942
+ provider: DEFAULTS.provider,
1943
+ model: DEFAULTS.model,
1944
+ locale: DEFAULTS.locale,
1945
+ chatLanguage: DEFAULTS.locale,
1946
+ theme: DEFAULTS.theme,
1947
+ palette: DEFAULTS.palette,
1948
+ cardStyle: DEFAULTS.cardStyle
1949
+ });
1950
+ setPortTouched(true);
1951
+ setConfirm(null);
1952
+ };
1953
+ const save = () => {
1954
+ if (!canSave) return;
1955
+ const committed = { ...dirty };
1956
+ actions.saveHostConfig({
1957
+ ...form,
1958
+ theme: committed.theme,
1959
+ palette: committed.palette,
1960
+ cardStyle: committed.cardStyle,
1961
+ locale: committed.locale,
1962
+ chatLanguage: committed.locale
1963
+ });
1964
+ actions.setAppearance(
1965
+ { theme: committed.theme, palette: committed.palette },
1966
+ "global"
1967
+ );
1968
+ actions.setCardStyle(committed.cardStyle);
1969
+ setSnapshot(committed);
1970
+ const now = /* @__PURE__ */ new Date();
1971
+ setLastSavedAt(
1972
+ `${String(now.getHours()).padStart(2, "0")}:${String(now.getMinutes()).padStart(2, "0")}`
1973
+ );
1974
+ };
1975
+ const copyUrl = async () => {
1976
+ try {
1977
+ await navigator.clipboard.writeText(hostUrl);
1978
+ } catch {
1979
+ }
1980
+ };
1981
+ const sectionLabel = (id) => id === "appearance" ? t2("settings.sectionAppearance") : id === "network" ? t2("settings.sectionNetwork") : id === "model" ? t2("settings.sectionModel") : t2("settings.sectionAbout");
1982
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1983
+ "div",
1984
+ {
1985
+ ref: rootRef,
1986
+ className: "mma-settings",
1987
+ id: "mma-settings",
1988
+ "data-open": s.settingsOpen ? "1" : "0",
1989
+ "data-wide": wide ? "1" : "0",
1990
+ children: [
1991
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("header", { className: "mma-settings-head", children: [
1992
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "mma-settings-head-left", children: [
1993
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "mma-settings-head-icon", "aria-hidden": "true", children: "\u2699" }),
1994
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h3", { children: t2("settings.title") })
1995
+ ] }),
1996
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "mma-settings-head-right", children: [
1997
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "mma-settings-meta", children: [
1998
+ versionLabel,
1999
+ " \xB7 ",
2000
+ paletteLabel
2001
+ ] }),
2002
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2003
+ "button",
2004
+ {
2005
+ type: "button",
2006
+ className: "mma-iconbtn",
2007
+ id: "mma-cfg-close",
2008
+ "aria-label": t2("settings.close"),
2009
+ onClick: requestClose,
2010
+ children: "\u2715"
2011
+ }
2012
+ )
2013
+ ] })
2014
+ ] }),
2015
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "mma-settings-body", children: [
2016
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("nav", { className: "mma-settings-rail", role: "tablist", "aria-label": t2("settings.sectionsAria"), children: [
2017
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "mma-settings-rail-title", children: t2("settings.sectionNav") }),
2018
+ SECTIONS.map((id) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2019
+ "button",
2020
+ {
2021
+ type: "button",
2022
+ role: "tab",
2023
+ "aria-current": section === id ? "page" : void 0,
2024
+ "data-on": section === id ? "1" : "0",
2025
+ className: "mma-settings-rail-item",
2026
+ onClick: () => scrollTo(id),
2027
+ children: sectionLabel(id)
2028
+ },
2029
+ id
2030
+ ))
2031
+ ] }),
2032
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "mma-settings-content", ref: contentRef, children: [
2033
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2034
+ "section",
2035
+ {
2036
+ className: "mma-settings-section",
2037
+ "data-section": "appearance",
2038
+ ref: (el) => {
2039
+ sectionRefs.current.appearance = el;
2040
+ },
2041
+ children: [
2042
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h4", { children: t2("settings.sectionAppearance") }),
2043
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "mma-settings-field", children: [
2044
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { className: "mma-settings-label", htmlFor: "mma-cfg-lang-seg", children: t2("settings.language") }),
2045
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "mma-seg", id: "mma-cfg-lang-seg", role: "group", "aria-label": t2("settings.language"), children: [
2046
+ ["zh-CN", t2("settings.langZh")],
2047
+ ["en", t2("settings.langEn")]
2048
+ ].map(([id, label]) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2049
+ "button",
2050
+ {
2051
+ type: "button",
2052
+ "data-on": locale === id ? "1" : "0",
2053
+ onClick: () => previewAppearance({ locale: id }),
2054
+ children: label
2055
+ },
2056
+ id
2057
+ )) })
2058
+ ] }),
2059
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "mma-settings-field", children: [
2060
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "mma-settings-label", children: t2("settings.theme") }),
2061
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2062
+ OptionCardGrid,
2063
+ {
2064
+ "aria-label": t2("settings.theme"),
2065
+ columns: 3,
2066
+ value: themeMode,
2067
+ onChange: (v) => previewAppearance({ theme: v }),
2068
+ options: ["system", "light", "dark"].map((mode) => ({
2069
+ value: mode,
2070
+ label: t2(`theme.${mode}`),
2071
+ preview: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ThemePreview, { mode })
2072
+ }))
2073
+ }
2074
+ )
2075
+ ] }),
2076
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "mma-settings-field", children: [
2077
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "mma-settings-label", children: t2("settings.palette") }),
2078
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2079
+ OptionCardGrid,
2080
+ {
2081
+ "aria-label": t2("settings.palette"),
2082
+ columns: 4,
2083
+ value: palette,
2084
+ onChange: (v) => previewAppearance({ palette: v }),
2085
+ options: PALETTES.map((p) => ({
2086
+ value: p.id,
2087
+ label: t2(`palette.${p.id}`),
2088
+ preview: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PalettePreview, { id: p.id, mode: resolvedMode })
2089
+ }))
2090
+ }
2091
+ )
2092
+ ] }),
2093
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "mma-settings-field", children: [
2094
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "mma-settings-label", children: t2("settings.cardStyle") }),
2095
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2096
+ OptionCardGrid,
2097
+ {
2098
+ "aria-label": t2("settings.cardStyle"),
2099
+ columns: 3,
2100
+ value: cardStyle,
2101
+ onChange: (v) => previewAppearance({ cardStyle: v }),
2102
+ options: [
2103
+ ["stamp", t2("settings.cardStamp")],
2104
+ ["etch", t2("settings.cardEtch")],
2105
+ ["hero", t2("settings.cardHero")],
2106
+ ["list", t2("settings.cardList")]
2107
+ ].map(([value, label]) => ({
2108
+ value,
2109
+ label,
2110
+ preview: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CardStylePreview, { style: value })
2111
+ }))
2112
+ }
2113
+ )
2114
+ ] }),
2115
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "mma-settings-hint", children: t2("settings.appearanceHint") })
2116
+ ]
2117
+ }
2118
+ ),
2119
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("hr", { className: "mma-settings-rule" }),
2120
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2121
+ "section",
2122
+ {
2123
+ className: "mma-settings-section",
2124
+ "data-section": "network",
2125
+ ref: (el) => {
2126
+ sectionRefs.current.network = el;
2127
+ },
2128
+ children: [
2129
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h4", { children: t2("settings.sectionNetwork") }),
2130
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: `mma-settings-field mma-settings-field--row${portError ? " is-error" : ""}`, children: [
2131
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { className: "mma-settings-label", htmlFor: "mma-cfg-port", children: t2("settings.hostPort") }),
2132
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "mma-settings-control", children: [
2133
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "mma-port-row", children: [
2134
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "mma-port-addon", "aria-hidden": "true", children: ":" }),
2135
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2136
+ "input",
2137
+ {
2138
+ id: "mma-cfg-port",
2139
+ type: "text",
2140
+ inputMode: "numeric",
2141
+ autoComplete: "off",
2142
+ value: form.hostPort || "",
2143
+ "aria-invalid": !!portError,
2144
+ "aria-describedby": "mma-cfg-port-hint",
2145
+ onChange: (e) => {
2146
+ setPortTouched(true);
2147
+ patch({ hostPort: e.target.value });
2148
+ },
2149
+ onBlur: () => setPortTouched(true)
2150
+ }
2151
+ ),
2152
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("code", { className: "mma-port-url", children: hostUrl }),
2153
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: "mma-ghostbtn", onClick: () => void copyUrl(), children: t2("settings.copyUrl") }),
2154
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("a", { className: "mma-ghostbtn", href: hostUrl, target: "_blank", rel: "noreferrer", children: t2("settings.openUrl") })
2155
+ ] }),
2156
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "mma-settings-hint", id: "mma-cfg-port-hint", role: portError ? "alert" : void 0, children: portError ?? t2("settings.portHint") })
2157
+ ] })
2158
+ ] })
2159
+ ]
2160
+ }
2161
+ ),
2162
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("hr", { className: "mma-settings-rule" }),
2163
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2164
+ "section",
2165
+ {
2166
+ className: "mma-settings-section",
2167
+ "data-section": "model",
2168
+ ref: (el) => {
2169
+ sectionRefs.current.model = el;
2170
+ },
2171
+ children: [
2172
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h4", { children: t2("settings.sectionModel") }),
2173
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "mma-settings-field mma-settings-field--row", children: [
2174
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { className: "mma-settings-label", htmlFor: "mma-cfg-provider", children: t2("settings.llmProvider") }),
2175
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "mma-settings-control mma-settings-control--model", children: [
2176
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2177
+ "input",
2178
+ {
2179
+ id: "mma-cfg-provider",
2180
+ list: "mma-cfg-provider-list",
2181
+ placeholder: t2("settings.providerPlaceholder"),
2182
+ value: form.provider || "",
2183
+ onChange: (e) => patch({ provider: e.target.value, model: "" })
2184
+ }
2185
+ ),
2186
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("datalist", { id: "mma-cfg-provider-list", children: [
2187
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "openai" }),
2188
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "anthropic" }),
2189
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "deepseek" })
2190
+ ] })
2191
+ ] })
2192
+ ] }),
2193
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "mma-settings-field mma-settings-field--row", children: [
2194
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { className: "mma-settings-label", htmlFor: "mma-cfg-model", children: t2("settings.llmModel") }),
2195
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "mma-settings-control mma-settings-control--model", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "mma-model-row", children: [
2196
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2197
+ "input",
2198
+ {
2199
+ id: "mma-cfg-model",
2200
+ placeholder: form.provider ? t2("settings.modelPlaceholder") : t2("settings.modelNeedsProvider"),
2201
+ value: form.model || "",
2202
+ disabled: !form.provider,
2203
+ onChange: (e) => patch({ model: e.target.value })
2204
+ }
2205
+ ),
2206
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "mma-status-pill", "data-state": "idle", children: t2("settings.llmProbeIdle") })
2207
+ ] }) })
2208
+ ] })
2209
+ ]
2210
+ }
2211
+ ),
2212
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("hr", { className: "mma-settings-rule" }),
2213
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2214
+ "section",
2215
+ {
2216
+ className: "mma-settings-section",
2217
+ "data-section": "about",
2218
+ ref: (el) => {
2219
+ sectionRefs.current.about = el;
2220
+ },
2221
+ children: [
2222
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h4", { children: t2("settings.sectionAbout") }),
2223
+ s.about ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
2224
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "mma-about-meta", children: [
2225
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "mma-settings-label", children: t2("settings.env") }),
2226
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("code", { children: [
2227
+ s.about.adapter,
2228
+ " \xB7 ",
2229
+ s.about.env
2230
+ ] })
2231
+ ] }),
2232
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("ul", { className: "mma-about-pkgs", children: s.about.packages.map((pkg) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("li", { children: [
2233
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { title: pkg.name, children: shortPkg(pkg.name) }),
2234
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("code", { children: pkg.version })
2235
+ ] }, pkg.name)) }),
2236
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "mma-about-actions", children: [
2237
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2238
+ "button",
2239
+ {
2240
+ type: "button",
2241
+ className: "mma-ghostbtn",
2242
+ id: "mma-cfg-check-update",
2243
+ disabled: s.updateCheck?.status === "loading",
2244
+ onClick: () => actions.checkUpdates(),
2245
+ children: s.updateCheck?.status === "loading" ? t2("settings.checkingUpdate") : t2("settings.checkUpdate")
2246
+ }
2247
+ ),
2248
+ s.updateCheck?.status === "done" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "mma-settings-hint", children: s.updateCheck.error ? t2("settings.updateError", { message: s.updateCheck.error }) : s.updateCheck.updateAvailable ? t2("settings.updateAvailable", {
2249
+ latest: s.updateCheck.latest ?? "",
2250
+ current: s.updateCheck.current
2251
+ }) : t2("settings.upToDate", {
2252
+ version: s.updateCheck.latest ?? s.updateCheck.current
2253
+ }) }) : null
2254
+ ] })
2255
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "mma-settings-hint", children: t2("settings.aboutLoading") })
2256
+ ]
2257
+ }
2258
+ )
2259
+ ] })
2260
+ ] }),
2261
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("footer", { className: "mma-settings-foot", children: [
2262
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "mma-settings-foot-left", "aria-live": "polite", children: [
2263
+ lastSavedAt ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "mma-settings-hint", children: [
2264
+ t2("settings.lastSaved", { time: lastSavedAt }),
2265
+ dirtyCount > 0 ? " \xB7 " : ""
2266
+ ] }) : null,
2267
+ dirtyCount > 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "mma-settings-dirty", children: t2("settings.dirtyCount", { count: dirtyCount }) }) : null
2268
+ ] }),
2269
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "mma-settings-foot-right", children: [
2270
+ dirtyCount > 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: "mma-ghostbtn", onClick: discard, children: t2("settings.discard") }) : null,
2271
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: "mma-ghostbtn", onClick: () => setConfirm("restore"), children: t2("settings.restore") }),
2272
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2273
+ "button",
2274
+ {
2275
+ type: "button",
2276
+ className: "mma-primarybtn",
2277
+ id: "mma-cfg-save",
2278
+ disabled: !canSave,
2279
+ onClick: save,
2280
+ children: t2("settings.save")
2281
+ }
2282
+ )
2283
+ ] }),
2284
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "mma-settings-msg", id: "mma-cfg-msg", children: s.cfgMsg })
2285
+ ] }),
2286
+ confirm ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "mma-settings-confirm", role: "presentation", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2287
+ "div",
2288
+ {
2289
+ className: "mma-dialog",
2290
+ role: "dialog",
2291
+ "aria-modal": "true",
2292
+ "aria-labelledby": "mma-settings-confirm-title",
2293
+ children: [
2294
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h3", { id: "mma-settings-confirm-title", children: confirm === "close" ? t2("settings.confirmCloseTitle") : t2("settings.confirmRestoreTitle") }),
2295
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { children: confirm === "close" ? t2("settings.confirmCloseBody") : t2("settings.confirmRestoreBody") }),
2296
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "mma-dialog-actions", children: [
2297
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", onClick: () => setConfirm(null), children: t2("modal.cancel") }),
2298
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2299
+ "button",
2300
+ {
2301
+ type: "button",
2302
+ className: "go",
2303
+ onClick: () => {
2304
+ if (confirm === "close") {
2305
+ discard();
2306
+ actions.toggleSettings(false);
2307
+ } else restoreDefaults();
2308
+ setConfirm(null);
2309
+ },
2310
+ children: confirm === "close" ? t2("settings.confirmDiscard") : t2("settings.confirmRestoreAction")
2311
+ }
2312
+ )
2313
+ ] })
2314
+ ]
2315
+ }
2316
+ ) }) : null
2317
+ ]
2318
+ }
2319
+ );
1404
2320
  }
1405
2321
 
1406
2322
  // ../panel/src/components/Tabs.tsx
1407
- var import_jsx_runtime7 = require("react/jsx-runtime");
2323
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1408
2324
  function Tabs() {
1409
2325
  const s = usePanelState();
1410
2326
  const actions = usePanelActions();
1411
2327
  const { t: t2 } = usePanelI18n();
1412
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "mma-tabs", id: "mma-tabs", children: s.tabs.map((tb) => {
2328
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "mma-tabs", id: "mma-tabs", children: s.tabs.map((tb) => {
1413
2329
  const active = tb.id === s.active;
1414
- const close = tb.id === "all" ? null : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2330
+ const close = tb.id === "all" ? null : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1415
2331
  "span",
1416
2332
  {
1417
2333
  className: "mma-tab-x",
@@ -1424,7 +2340,7 @@ function Tabs() {
1424
2340
  children: "\xD7"
1425
2341
  }
1426
2342
  );
1427
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2343
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1428
2344
  "button",
1429
2345
  {
1430
2346
  type: "button",
@@ -1433,7 +2349,7 @@ function Tabs() {
1433
2349
  title: tb.id === "all" ? t2("tabs.allTitle") : tb.title,
1434
2350
  onClick: () => actions.switchTab(tb.id),
1435
2351
  children: [
1436
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: tb.title }),
2352
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: tb.title }),
1437
2353
  close
1438
2354
  ]
1439
2355
  },
@@ -1592,16 +2508,19 @@ var __iconNode7 = [
1592
2508
  var X = createLucideIcon("x", __iconNode7);
1593
2509
 
1594
2510
  // ../panel/src/components/ThemePop.tsx
1595
- var import_jsx_runtime8 = require("react/jsx-runtime");
2511
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1596
2512
  function ThemePop() {
1597
2513
  const s = usePanelState();
1598
2514
  const actions = usePanelActions();
1599
2515
  const { t: t2 } = usePanelI18n();
1600
2516
  const app = actions.getActiveApp();
1601
- const appTheme = app ? app.theme : null;
2517
+ const appScope = Boolean(app) && s.themeScope === "app";
2518
+ const viewTheme = appScope ? app?.theme?.theme ?? s.theme : s.theme;
2519
+ const viewPalette = appScope ? selectedPalette(app?.theme?.palette, Boolean(app?.localPalette)) : s.palette;
1602
2520
  const customs = s.customPalettes || {};
1603
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1604
- s.themePopOpen ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2521
+ const canApp = Boolean(app && s.capabilities.appTheme);
2522
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
2523
+ s.themePopOpen ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1605
2524
  "div",
1606
2525
  {
1607
2526
  className: "mma-pop-scrim",
@@ -1613,7 +2532,7 @@ function ThemePop() {
1613
2532
  }
1614
2533
  }
1615
2534
  ) : null,
1616
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2535
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1617
2536
  "div",
1618
2537
  {
1619
2538
  className: "mma-pop",
@@ -1623,35 +2542,79 @@ function ThemePop() {
1623
2542
  onClick: (e) => e.stopPropagation(),
1624
2543
  onPointerDown: (e) => e.stopPropagation(),
1625
2544
  children: [
1626
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "mma-pop-seg", children: ["light", "dark"].map((mode) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2545
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "mma-pop-lab", children: t2("theme.apply") }),
2546
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "mma-pop-seg", children: [
2547
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2548
+ "button",
2549
+ {
2550
+ type: "button",
2551
+ "data-scope": "global",
2552
+ "data-on": s.themeScope === "global" ? "1" : "0",
2553
+ onClick: () => actions.setThemeScope("global"),
2554
+ children: t2("theme.global")
2555
+ }
2556
+ ),
2557
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2558
+ "button",
2559
+ {
2560
+ type: "button",
2561
+ "data-scope": "app",
2562
+ id: "mma-scope-app",
2563
+ title: app ? t2("theme.saveTo", { name: app.name }) : t2("theme.openAppFirst"),
2564
+ "data-on": appScope ? "1" : "0",
2565
+ disabled: !canApp,
2566
+ onClick: () => actions.setThemeScope("app"),
2567
+ children: app ? app.name : t2("theme.currentApp")
2568
+ }
2569
+ )
2570
+ ] }),
2571
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "mma-pop-lab", children: t2("theme.appearance") }),
2572
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "mma-pop-seg", children: ["system", "light", "dark"].map((mode) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1627
2573
  "button",
1628
2574
  {
1629
2575
  type: "button",
1630
2576
  "data-mode": mode,
1631
- "data-on": s.theme === mode ? "1" : "0",
2577
+ "data-on": viewTheme === mode ? "1" : "0",
1632
2578
  onClick: () => actions.setAppearance({ theme: mode }, s.themeScope),
1633
2579
  children: t2(`theme.${mode}`)
1634
2580
  },
1635
2581
  mode
1636
2582
  )) }),
1637
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "mma-pop-list", children: [
1638
- PALETTES.map((p) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2583
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "mma-pop-lab", children: t2("theme.palettes") }),
2584
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "mma-pop-list", children: [
2585
+ appScope ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2586
+ "button",
2587
+ {
2588
+ type: "button",
2589
+ className: "mma-swatch",
2590
+ id: "mma-follow-global",
2591
+ "data-on": viewPalette === GLOBAL_PALETTE_ID ? "1" : "0",
2592
+ role: "menuitem",
2593
+ onClick: () => actions.setAppearance({ palette: GLOBAL_PALETTE_ID }, "app"),
2594
+ children: [
2595
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("i", { className: "mma-dot", style: { background: "linear-gradient(135deg,#888,#ddd)" } }),
2596
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: t2("theme.followGlobal") })
2597
+ ]
2598
+ }
2599
+ ) : null,
2600
+ PALETTES.map((p) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1639
2601
  "button",
1640
2602
  {
1641
2603
  type: "button",
1642
2604
  className: "mma-swatch",
1643
2605
  "data-palette": p.id,
1644
2606
  role: "menuitem",
1645
- "data-on": s.palette === p.id ? "1" : "0",
2607
+ "data-on": viewPalette === p.id ? "1" : "0",
1646
2608
  onClick: () => actions.setAppearance({ palette: p.id }, s.themeScope),
1647
2609
  children: [
1648
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("i", { className: "mma-dot", style: { background: p.swatch } }),
1649
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: t2(`palette.${p.id}`) })
2610
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("i", { className: "mma-dot", style: { background: p.swatch } }),
2611
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: t2(`palette.${p.id}`) }),
2612
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("i", { className: "mma-custom-badge", children: t2("theme.chipSystem") })
1650
2613
  ]
1651
2614
  },
1652
2615
  p.id
1653
2616
  )),
1654
- Object.keys(customs).map((id) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2617
+ Object.keys(customs).map((id) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1655
2618
  "button",
1656
2619
  {
1657
2620
  type: "button",
@@ -1659,43 +2622,43 @@ function ThemePop() {
1659
2622
  "data-palette": id,
1660
2623
  "data-custom": "1",
1661
2624
  role: "menuitem",
1662
- "data-on": s.palette === id ? "1" : "0",
2625
+ "data-on": viewPalette === id ? "1" : "0",
1663
2626
  onClick: () => actions.setAppearance({ palette: id }, s.themeScope),
1664
2627
  children: [
1665
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("i", { className: "mma-dot", style: { background: customs[id].swatch || "#888" } }),
1666
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: customs[id].label || id }),
1667
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("i", { className: "mma-custom-badge", children: t2("theme.custom") })
2628
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("i", { className: "mma-dot", style: { background: customs[id].swatch || "#888" } }),
2629
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: customs[id].label || id }),
2630
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("i", { className: "mma-custom-badge", children: t2("theme.custom") })
1668
2631
  ]
1669
2632
  },
1670
2633
  id
1671
- ))
1672
- ] }),
1673
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "mma-pop-seg mma-scope-seg", children: [
1674
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1675
- "button",
1676
- {
1677
- type: "button",
1678
- "data-scope": "global",
1679
- "data-on": s.themeScope === "global" ? "1" : "0",
1680
- onClick: () => actions.setThemeScope("global"),
1681
- children: t2("theme.global")
1682
- }
1683
- ),
1684
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2634
+ )),
2635
+ appScope && app?.localPalette ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1685
2636
  "button",
1686
2637
  {
1687
2638
  type: "button",
1688
- "data-scope": "app",
1689
- id: "mma-scope-app",
1690
- title: app ? t2("theme.saveTo", { name: app.name }) : t2("theme.openAppFirst"),
1691
- "data-on": s.themeScope === "app" ? "1" : "0",
1692
- disabled: !app || !s.capabilities.appTheme,
1693
- onClick: () => actions.setThemeScope("app"),
1694
- children: app ? app.name : t2("theme.currentApp")
2639
+ className: "mma-swatch",
2640
+ "data-palette": LOCAL_PALETTE_ID,
2641
+ role: "menuitem",
2642
+ "data-on": viewPalette === LOCAL_PALETTE_ID ? "1" : "0",
2643
+ onClick: () => actions.setAppearance({ palette: LOCAL_PALETTE_ID }, "app"),
2644
+ children: [
2645
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("i", { className: "mma-dot", style: { background: app.localPalette.swatch } }),
2646
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: app.localPalette.label }),
2647
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("i", { className: "mma-custom-badge", children: t2("theme.chipApp") })
2648
+ ]
1695
2649
  }
1696
- )
2650
+ ) : null
1697
2651
  ] }),
1698
- s.themeScope === "app" && appTheme ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", className: "mma-textbtn", id: "mma-clear-app-theme", onClick: () => actions.clearAppTheme(), children: t2("theme.followGlobal") }) : null
2652
+ appScope && app?.theme ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2653
+ "button",
2654
+ {
2655
+ type: "button",
2656
+ className: "mma-textbtn",
2657
+ id: "mma-reset-app-theme",
2658
+ onClick: () => actions.clearAppTheme(),
2659
+ children: t2("theme.resetApp")
2660
+ }
2661
+ ) : null
1699
2662
  ]
1700
2663
  }
1701
2664
  )
@@ -1703,7 +2666,7 @@ function ThemePop() {
1703
2666
  }
1704
2667
 
1705
2668
  // ../panel/src/components/Toolbar.tsx
1706
- var import_jsx_runtime9 = require("react/jsx-runtime");
2669
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1707
2670
  var iconProps = { size: 16, strokeWidth: 2, className: "mma-ico" };
1708
2671
  function Toolbar() {
1709
2672
  const s = usePanelState();
@@ -1712,8 +2675,8 @@ function Toolbar() {
1712
2675
  const side = s.dock === "side";
1713
2676
  const appTab = s.tabs.some((tab) => tab.id === s.active && tab.kind === "app");
1714
2677
  const anyApp = s.tabs.some((tab) => tab.kind === "app");
1715
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "mma-toolbar", children: [
1716
- appTab && s.capabilities.deleteApp ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2678
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "mma-toolbar", children: [
2679
+ appTab && s.capabilities.deleteApp ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1717
2680
  "button",
1718
2681
  {
1719
2682
  type: "button",
@@ -1724,7 +2687,7 @@ function Toolbar() {
1724
2687
  children: t2("toolbar.delete")
1725
2688
  }
1726
2689
  ) : null,
1727
- appTab ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2690
+ appTab ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1728
2691
  "button",
1729
2692
  {
1730
2693
  type: "button",
@@ -1733,11 +2696,11 @@ function Toolbar() {
1733
2696
  title: t2("toolbar.reload"),
1734
2697
  "aria-label": t2("toolbar.reload"),
1735
2698
  onClick: () => actions.reloadActive(),
1736
- children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(RefreshCw, { ...iconProps })
2699
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(RefreshCw, { ...iconProps })
1737
2700
  }
1738
2701
  ) : null,
1739
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "mma-theme-wrap", id: "mma-theme-wrap", children: [
1740
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2702
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "mma-theme-wrap", id: "mma-theme-wrap", children: [
2703
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1741
2704
  "button",
1742
2705
  {
1743
2706
  type: "button",
@@ -1747,12 +2710,12 @@ function Toolbar() {
1747
2710
  "aria-label": t2("toolbar.theme"),
1748
2711
  "aria-haspopup": "menu",
1749
2712
  onClick: () => actions.toggleThemePop(),
1750
- children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(LayoutGrid, { ...iconProps })
2713
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(LayoutGrid, { ...iconProps })
1751
2714
  }
1752
2715
  ),
1753
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ThemePop, {})
2716
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ThemePop, {})
1754
2717
  ] }),
1755
- anyApp && s.capabilities.history ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2718
+ anyApp && s.capabilities.history ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1756
2719
  "button",
1757
2720
  {
1758
2721
  type: "button",
@@ -1761,10 +2724,10 @@ function Toolbar() {
1761
2724
  title: t2("toolbar.history"),
1762
2725
  "aria-label": t2("toolbar.history"),
1763
2726
  onClick: () => actions.toggleBrowse("history"),
1764
- children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Clock, { ...iconProps })
2727
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Clock, { ...iconProps })
1765
2728
  }
1766
2729
  ) : null,
1767
- anyApp && s.capabilities.storage ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2730
+ anyApp && s.capabilities.storage ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1768
2731
  "button",
1769
2732
  {
1770
2733
  type: "button",
@@ -1773,10 +2736,10 @@ function Toolbar() {
1773
2736
  title: t2("toolbar.storage"),
1774
2737
  "aria-label": t2("toolbar.storage"),
1775
2738
  onClick: () => actions.toggleBrowse("storage"),
1776
- children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Database, { ...iconProps })
2739
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Database, { ...iconProps })
1777
2740
  }
1778
2741
  ) : null,
1779
- s.capabilities.config ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2742
+ s.capabilities.config ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1780
2743
  "button",
1781
2744
  {
1782
2745
  type: "button",
@@ -1785,10 +2748,10 @@ function Toolbar() {
1785
2748
  title: t2("toolbar.settings"),
1786
2749
  "aria-label": t2("toolbar.settings"),
1787
2750
  onClick: () => actions.toggleSettings(true),
1788
- children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Settings2, { ...iconProps })
2751
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Settings2, { ...iconProps })
1789
2752
  }
1790
2753
  ) : null,
1791
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2754
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1792
2755
  "button",
1793
2756
  {
1794
2757
  type: "button",
@@ -1797,10 +2760,10 @@ function Toolbar() {
1797
2760
  title: side ? t2("toolbar.dockFill") : t2("toolbar.dockSide"),
1798
2761
  "aria-label": side ? t2("toolbar.dockFill") : t2("toolbar.dockSide"),
1799
2762
  onClick: () => actions.setDock(side ? "fill" : "side"),
1800
- children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PanelRight, { ...iconProps })
2763
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PanelRight, { ...iconProps })
1801
2764
  }
1802
2765
  ),
1803
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2766
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1804
2767
  "button",
1805
2768
  {
1806
2769
  type: "button",
@@ -1809,29 +2772,29 @@ function Toolbar() {
1809
2772
  title: t2("toolbar.close"),
1810
2773
  "aria-label": t2("toolbar.close"),
1811
2774
  onClick: () => actions.closeDashboard(),
1812
- children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(X, { ...iconProps })
2775
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(X, { ...iconProps })
1813
2776
  }
1814
2777
  )
1815
2778
  ] });
1816
2779
  }
1817
2780
 
1818
2781
  // ../panel/src/components/MiniAppPanel.tsx
1819
- var import_jsx_runtime10 = require("react/jsx-runtime");
2782
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1820
2783
  function MiniAppPanel() {
1821
2784
  const s = usePanelState();
1822
2785
  const listVisible = s.active === "all";
1823
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
1824
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "mma-chrome", children: [
1825
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Tabs, {}),
1826
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Toolbar, {})
2786
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
2787
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "mma-chrome", children: [
2788
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Tabs, {}),
2789
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Toolbar, {})
1827
2790
  ] }),
1828
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "mma-stage", children: [
1829
- listVisible ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ListRegion, {}) : null,
1830
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { id: "mma-frames", className: "mma-frames", style: { display: listVisible ? "none" : "block" } })
2791
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "mma-stage", children: [
2792
+ listVisible ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ListRegion, {}) : null,
2793
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { id: "mma-frames", className: "mma-frames", style: { display: listVisible ? "none" : "block" } })
1831
2794
  ] }),
1832
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Settings, {}),
1833
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Browse, {}),
1834
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Modal, {})
2795
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Settings, {}),
2796
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Browse, {}),
2797
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Modal, {})
1835
2798
  ] });
1836
2799
  }
1837
2800
 
@@ -1884,6 +2847,13 @@ function createFrameController(opts) {
1884
2847
  const { container: initial2, urlOf, envOf } = opts;
1885
2848
  let container = initial2 ?? null;
1886
2849
  const map = /* @__PURE__ */ new Map();
2850
+ function targetOrigin(appId, rec) {
2851
+ try {
2852
+ return new URL(rec?.iframe.src || urlOf(appId)).origin;
2853
+ } catch {
2854
+ return "";
2855
+ }
2856
+ }
1887
2857
  return {
1888
2858
  map,
1889
2859
  url: (appId) => urlOf(appId),
@@ -1895,8 +2865,13 @@ function createFrameController(opts) {
1895
2865
  const w = rec?.iframe.contentWindow;
1896
2866
  if (!w) return;
1897
2867
  const env = envOf(appId);
2868
+ const target = targetOrigin(appId, rec);
2869
+ if (!target) return;
1898
2870
  try {
1899
- w.postMessage({ type: "mma-set-env", theme: env.theme, palette: env.palette, dock: env.dock }, "*");
2871
+ w.postMessage(
2872
+ { type: "mma-set-env", theme: env.theme, palette: env.palette, dock: env.dock, vars: env.vars },
2873
+ target
2874
+ );
1900
2875
  } catch {
1901
2876
  }
1902
2877
  },
@@ -1910,7 +2885,7 @@ function createFrameController(opts) {
1910
2885
  const wrap = document.createElement("div");
1911
2886
  wrap.className = "mma-frame";
1912
2887
  wrap.setAttribute("data-app", appId);
1913
- wrap.innerHTML = `${loadingMarkup()}<iframe title="${escapeHtml(title || appId)}"></iframe>`;
2888
+ wrap.innerHTML = `${loadingMarkup()}<iframe title="${escapeHtml(title || appId)}" allow="clipboard-write"></iframe>`;
1914
2889
  if (!container) return;
1915
2890
  container.appendChild(wrap);
1916
2891
  const iframe = wrap.querySelector("iframe");
@@ -1940,6 +2915,27 @@ function createFrameController(opts) {
1940
2915
  if (!rec) return;
1941
2916
  if (!rec.wrap.querySelector(".mma-load")) rec.wrap.insertAdjacentHTML("afterbegin", loadingMarkup());
1942
2917
  rec.iframe.src = `${urlOf(appId)}&_=${Date.now()}`;
2918
+ },
2919
+ postViewEval(query) {
2920
+ const rec = map.get(query.appId);
2921
+ const w = rec?.iframe.contentWindow;
2922
+ const target = rec ? targetOrigin(query.appId, rec) : "";
2923
+ if (!w || !target) return false;
2924
+ try {
2925
+ w.postMessage(
2926
+ {
2927
+ type: "mma-view-eval",
2928
+ requestId: query.requestId,
2929
+ appId: query.appId,
2930
+ code: query.code ?? "",
2931
+ maxBytes: query.maxBytes ?? 0
2932
+ },
2933
+ target
2934
+ );
2935
+ return true;
2936
+ } catch {
2937
+ return false;
2938
+ }
1943
2939
  }
1944
2940
  };
1945
2941
  }
@@ -4217,6 +5213,7 @@ var en_default = {
4217
5213
  close: "Close"
4218
5214
  },
4219
5215
  theme: {
5216
+ system: "System",
4220
5217
  light: "Light",
4221
5218
  dark: "Dark",
4222
5219
  custom: "Custom",
@@ -4224,7 +5221,13 @@ var en_default = {
4224
5221
  currentApp: "Current app",
4225
5222
  openAppFirst: "Open an app first",
4226
5223
  saveTo: 'Save to "{{name}}"',
4227
- followGlobal: "Follow global (clear app theme)"
5224
+ followGlobal: "Follow global",
5225
+ apply: "Apply to",
5226
+ appearance: "Appearance",
5227
+ palettes: "Palette",
5228
+ chipSystem: "System",
5229
+ chipApp: "This app",
5230
+ resetApp: "Restore this app default"
4228
5231
  },
4229
5232
  palette: {
4230
5233
  default: "Default",
@@ -4250,20 +5253,59 @@ var en_default = {
4250
5253
  },
4251
5254
  settings: {
4252
5255
  title: "Settings",
5256
+ close: "Close settings",
5257
+ sectionNav: "Sections",
5258
+ sectionsAria: "Settings sections",
5259
+ sectionAppearance: "Appearance",
5260
+ sectionNetwork: "Network",
5261
+ sectionModel: "Model",
5262
+ sectionAbout: "About",
5263
+ sectionHost: "Host",
4253
5264
  hostPort: "Host port",
4254
5265
  language: "Language",
4255
5266
  langZh: "Chinese",
4256
5267
  langEn: "English",
4257
5268
  theme: "Theme",
4258
5269
  palette: "Palette",
5270
+ dock: "Dock",
5271
+ dockFill: "Fill",
5272
+ dockSide: "Side",
4259
5273
  cardStyle: "Card style",
4260
5274
  cardStamp: "Stamp",
4261
5275
  cardEtch: "Etch",
4262
5276
  cardHero: "Hero",
4263
5277
  cardList: "List",
5278
+ appearanceHint: "Preview only. Click Save to persist; closing discards unsaved changes.",
5279
+ portHint: "Range 1024\u201365535. Restart the app after changing.",
5280
+ portEmpty: "Enter a port.",
5281
+ portNan: "Port must be a number.",
5282
+ portRange: "Port must be between 1024 and 65535.",
5283
+ copyUrl: "Copy",
5284
+ openUrl: "Open",
4264
5285
  llmProvider: "LLM Provider",
4265
5286
  llmModel: "LLM model",
4266
- save: "Save"
5287
+ providerPlaceholder: "Select a provider",
5288
+ modelPlaceholder: "Enter a model",
5289
+ modelNeedsProvider: "Select a provider first",
5290
+ llmProbeIdle: "Probe not wired",
5291
+ save: "Save",
5292
+ discard: "Discard",
5293
+ restore: "Restore defaults",
5294
+ lastSaved: "Saved {{time}}",
5295
+ dirtyCount: "{{count}} unsaved",
5296
+ confirmCloseTitle: "Discard unsaved changes?",
5297
+ confirmCloseBody: "Unsaved appearance, network, and model changes will be discarded and restored.",
5298
+ confirmDiscard: "Discard",
5299
+ confirmRestoreTitle: "Restore defaults?",
5300
+ confirmRestoreBody: "Resets appearance, port, and model fields (port/model still need Save).",
5301
+ confirmRestoreAction: "Restore",
5302
+ env: "Environment",
5303
+ aboutLoading: "Loading version info\u2026",
5304
+ checkUpdate: "Check for updates",
5305
+ checkingUpdate: "Checking\u2026",
5306
+ upToDate: "Up to date ({{version}})",
5307
+ updateAvailable: "Update available: {{latest}} (current {{current}})",
5308
+ updateError: "Check failed: {{message}}"
4267
5309
  },
4268
5310
  modal: {
4269
5311
  title: "Delete mini app",
@@ -4282,7 +5324,16 @@ var en_default = {
4282
5324
  noCommits: "No commits yet",
4283
5325
  noStorage: "No storage files",
4284
5326
  expandMsg: "Show more",
4285
- collapseMsg: "Show less"
5327
+ collapseMsg: "Show less",
5328
+ entries: "{{count}} entries",
5329
+ sizeKb: "{{value}} KB",
5330
+ sizeMb: "{{value}} MB",
5331
+ noticeTitle: '"{{table}}" is already {{size}}',
5332
+ noticeBody: "The bigger this file gets, the slower every save becomes. Move anything that keeps growing into its own table \u2014 copy the prompt below and paste it to an AI to do the split for you.",
5333
+ noticeTop: 'The heaviest piece right now is "{{key}}" (about {{size}}).',
5334
+ noticeCopy: "Copy split prompt",
5335
+ noticeCopied: "Copied",
5336
+ noticeDismiss: "Got it"
4286
5337
  },
4287
5338
  load: {
4288
5339
  label: "Loading"
@@ -4312,6 +5363,7 @@ var zh_CN_default = {
4312
5363
  close: "\u5173\u95ED"
4313
5364
  },
4314
5365
  theme: {
5366
+ system: "\u8DDF\u968F\u7CFB\u7EDF",
4315
5367
  light: "\u6D45\u8272",
4316
5368
  dark: "\u6DF1\u8272",
4317
5369
  custom: "\u81EA\u5B9A\u4E49",
@@ -4319,7 +5371,13 @@ var zh_CN_default = {
4319
5371
  currentApp: "\u5F53\u524D\u5C0F\u7A0B\u5E8F",
4320
5372
  openAppFirst: "\u6253\u5F00\u5C0F\u7A0B\u5E8F\u540E\u53EF\u7528",
4321
5373
  saveTo: "\u4FDD\u5B58\u5230\u300C{{name}}\u300D",
4322
- followGlobal: "\u8DDF\u968F\u5168\u5C40\uFF08\u6E05\u9664\u672C\u5E94\u7528\u4E3B\u9898\uFF09"
5374
+ followGlobal: "\u8DDF\u968F\u5168\u5C40",
5375
+ apply: "\u5E94\u7528\u5230",
5376
+ appearance: "\u5916\u89C2",
5377
+ palettes: "\u8272\u677F",
5378
+ chipSystem: "\u7CFB\u7EDF",
5379
+ chipApp: "\u672C\u5E94\u7528",
5380
+ resetApp: "\u6062\u590D\u8BE5\u5E94\u7528\u9ED8\u8BA4"
4323
5381
  },
4324
5382
  palette: {
4325
5383
  default: "\u9ED8\u8BA4",
@@ -4345,20 +5403,59 @@ var zh_CN_default = {
4345
5403
  },
4346
5404
  settings: {
4347
5405
  title: "\u8BBE\u7F6E",
5406
+ close: "\u5173\u95ED\u8BBE\u7F6E",
5407
+ sectionNav: "\u5206\u7EC4",
5408
+ sectionsAria: "\u8BBE\u7F6E\u5206\u7EC4",
5409
+ sectionAppearance: "\u5916\u89C2",
5410
+ sectionNetwork: "\u7F51\u7EDC",
5411
+ sectionModel: "\u6A21\u578B",
5412
+ sectionAbout: "\u5173\u4E8E",
5413
+ sectionHost: "\u5BBF\u4E3B",
4348
5414
  hostPort: "Host \u7AEF\u53E3",
4349
5415
  language: "\u754C\u9762\u8BED\u8A00",
4350
5416
  langZh: "\u4E2D\u6587",
4351
5417
  langEn: "English",
4352
5418
  theme: "\u4E3B\u9898",
4353
5419
  palette: "\u8C03\u8272\u677F",
5420
+ dock: "\u505C\u9760",
5421
+ dockFill: "\u94FA\u6EE1",
5422
+ dockSide: "\u4FA7\u680F",
4354
5423
  cardStyle: "\u5361\u7247\u6837\u5F0F",
4355
5424
  cardStamp: "\u5370\u7AE0",
4356
5425
  cardEtch: "\u8680\u523B",
4357
5426
  cardHero: "\u6D77\u62A5",
4358
5427
  cardList: "\u5217\u8868",
5428
+ appearanceHint: "\u4EC5\u9884\u89C8\uFF0C\u70B9\u4FDD\u5B58\u540E\u624D\u4F1A\u5199\u5165\uFF1B\u5173\u95ED\u672A\u4FDD\u5B58\u5C06\u6062\u590D\u539F\u6837\u3002",
5429
+ portHint: "\u8303\u56F4 1024\u201365535\uFF0C\u4FEE\u6539\u540E\u9700\u91CD\u542F\u5E94\u7528\u3002",
5430
+ portEmpty: "\u8BF7\u8F93\u5165\u7AEF\u53E3\u3002",
5431
+ portNan: "\u7AEF\u53E3\u5FC5\u987B\u662F\u6570\u5B57\u3002",
5432
+ portRange: "\u7AEF\u53E3\u9700\u5728 1024\u201365535 \u4E4B\u95F4\u3002",
5433
+ copyUrl: "\u590D\u5236",
5434
+ openUrl: "\u6253\u5F00",
4359
5435
  llmProvider: "LLM Provider",
4360
5436
  llmModel: "LLM \u6A21\u578B",
4361
- save: "\u4FDD\u5B58"
5437
+ providerPlaceholder: "\u9009\u62E9 Provider",
5438
+ modelPlaceholder: "\u8F93\u5165\u6A21\u578B\u540D",
5439
+ modelNeedsProvider: "\u5148\u9009\u62E9 Provider",
5440
+ llmProbeIdle: "\u672A\u63A5\u5165\u68C0\u6D4B",
5441
+ save: "\u4FDD\u5B58",
5442
+ discard: "\u653E\u5F03\u4FEE\u6539",
5443
+ restore: "\u6062\u590D\u9ED8\u8BA4",
5444
+ lastSaved: "\u4E0A\u6B21\u4FDD\u5B58 {{time}}",
5445
+ dirtyCount: "{{count}} \u9879\u672A\u4FDD\u5B58",
5446
+ confirmCloseTitle: "\u653E\u5F03\u672A\u4FDD\u5B58\u7684\u4FEE\u6539\uFF1F",
5447
+ confirmCloseBody: "\u5173\u95ED\u540E\uFF0C\u672A\u4FDD\u5B58\u7684\u5916\u89C2 / \u7F51\u7EDC / \u6A21\u578B\u6539\u52A8\u90FD\u4F1A\u4E22\u5F03\u5E76\u6062\u590D\u539F\u6837\u3002",
5448
+ confirmDiscard: "\u653E\u5F03",
5449
+ confirmRestoreTitle: "\u6062\u590D\u9ED8\u8BA4\u8BBE\u7F6E\uFF1F",
5450
+ confirmRestoreBody: "\u5C06\u91CD\u7F6E\u5916\u89C2\u3001\u7AEF\u53E3\u4E0E\u6A21\u578B\u5B57\u6BB5\u4E3A\u9ED8\u8BA4\u503C\uFF08\u4ECD\u9700\u4FDD\u5B58\u7AEF\u53E3/\u6A21\u578B\uFF09\u3002",
5451
+ confirmRestoreAction: "\u6062\u590D",
5452
+ env: "\u73AF\u5883",
5453
+ aboutLoading: "\u6B63\u5728\u8BFB\u53D6\u7248\u672C\u4FE1\u606F\u2026",
5454
+ checkUpdate: "\u68C0\u67E5\u66F4\u65B0",
5455
+ checkingUpdate: "\u68C0\u67E5\u4E2D\u2026",
5456
+ upToDate: "\u5DF2\u662F\u6700\u65B0\uFF08{{version}}\uFF09",
5457
+ updateAvailable: "\u6709\u65B0\u7248\u672C {{latest}}\uFF08\u5F53\u524D {{current}}\uFF09",
5458
+ updateError: "\u68C0\u67E5\u5931\u8D25\uFF1A{{message}}"
4362
5459
  },
4363
5460
  modal: {
4364
5461
  title: "\u5220\u9664\u5C0F\u7A0B\u5E8F",
@@ -4377,7 +5474,16 @@ var zh_CN_default = {
4377
5474
  noCommits: "\u6682\u65E0\u63D0\u4EA4\u8BB0\u5F55",
4378
5475
  noStorage: "\u6682\u65E0\u5B58\u50A8\u6587\u4EF6",
4379
5476
  expandMsg: "\u5C55\u5F00\u5168\u6587",
4380
- collapseMsg: "\u6536\u8D77"
5477
+ collapseMsg: "\u6536\u8D77",
5478
+ entries: "{{count}} \u6761",
5479
+ sizeKb: "{{value}} KB",
5480
+ sizeMb: "{{value}} MB",
5481
+ noticeTitle: "\u300C{{table}}\u300D\u5DF2\u7ECF\u6709 {{size}} \u4E86",
5482
+ noticeBody: "\u8FD9\u4E2A\u6587\u4EF6\u8D8A\u5927\uFF0C\u6BCF\u6B21\u4FDD\u5B58\u90FD\u4F1A\u8D8A\u6162\u3002\u5EFA\u8BAE\u628A\u4F1A\u4E0D\u65AD\u53D8\u591A\u7684\u6570\u636E\u62C6\u5230\u5355\u72EC\u7684\u8868\u91CC\u2014\u2014\u4F60\u53EF\u4EE5\u628A\u4E0B\u9762\u7684\u63D0\u793A\u8BCD\u590D\u5236\u7ED9 AI\uFF0C\u8BA9\u5B83\u5E2E\u4F60\u6539\u3002",
5483
+ noticeTop: "\u773C\u4E0B\u6700\u5927\u7684\u4E00\u5757\u662F\u300C{{key}}\u300D\uFF08\u7EA6 {{size}}\uFF09\u3002",
5484
+ noticeCopy: "\u590D\u5236\u62C6\u5206\u63D0\u793A\u8BCD",
5485
+ noticeCopied: "\u5DF2\u590D\u5236",
5486
+ noticeDismiss: "\u77E5\u9053\u4E86"
4381
5487
  },
4382
5488
  load: {
4383
5489
  label: "\u52A0\u8F7D\u4E2D"
@@ -4457,7 +5563,7 @@ function resolvePanelLocale(value) {
4457
5563
  // ../panel/src/styles.ts
4458
5564
  var PANEL_CSS_TAG = "panel";
4459
5565
  var CSS = [
4460
- "#mma-host{color:var(--dsw-alias-fg,#111);background:var(--dsw-alias-bg,#f7f7f8);overflow:hidden;}",
5566
+ "#mma-host{color:var(--dsw-alias-fg,#111);background:var(--dsw-alias-bg,#f7f7f8);overflow:hidden;container-type:inline-size;container-name:mma-host;}",
4461
5567
  "#mma-host button,#mma-host input,#mma-host select{color:inherit;font:inherit;}",
4462
5568
  "#mma-host .mma-chrome{display:flex;align-items:center;gap:8px;height:46px;padding:0 10px;border-bottom:1px solid var(--dsw-alias-border,#e5e7eb);background:var(--dsw-alias-surface,#fff);flex:0 0 auto;}",
4463
5569
  "#mma-host .mma-tabs{display:flex;align-items:center;gap:2px;flex:1;min-width:0;overflow-x:auto;flex-wrap:nowrap;scrollbar-width:thin;}",
@@ -4473,12 +5579,18 @@ var CSS = [
4473
5579
  "#mma-host .mma-toolbar .mma-ico{display:block;}",
4474
5580
  "#mma-host .mma-theme-wrap{position:relative;flex:0 0 auto;}",
4475
5581
  "#mma-host .mma-pop-scrim{position:fixed;inset:0;z-index:7;background:transparent;}",
4476
- "#mma-host .mma-pop{display:none;position:absolute;right:0;top:40px;z-index:8;width:232px;padding:12px 14px 14px;border-radius:12px;border:1px solid var(--dsw-alias-border,#e5e7eb);background:var(--dsw-alias-surface,#fff);box-shadow:0 12px 32px var(--dsw-alias-shadow,rgba(0,0,0,.14));}",
5582
+ "#mma-host .mma-pop{display:none;position:absolute;right:0;top:40px;z-index:8;width:260px;padding:12px 14px 14px;border-radius:12px;border:1px solid var(--dsw-alias-border,#e5e7eb);background:var(--dsw-alias-surface,#fff);box-shadow:0 12px 32px var(--dsw-alias-shadow,rgba(0,0,0,.14));}",
4477
5583
  "#mma-host .mma-pop[data-open='1']{display:block;}",
4478
5584
  "#mma-host .mma-pop-seg{display:flex;gap:4px;margin:0 0 12px;padding:3px;border-radius:9px;background:var(--dsw-alias-muted,#f3f4f6);}",
4479
5585
  "#mma-host .mma-pop-seg button{flex:1;height:28px;padding:0;line-height:1;border:0;border-radius:7px;background:transparent;cursor:pointer;font-size:12px;color:inherit;display:flex;align-items:center;justify-content:center;}",
4480
5586
  "#mma-host .mma-pop-seg button[data-on='1']{background:var(--dsw-alias-surface,#fff);font-weight:600;box-shadow:0 1px 2px var(--dsw-alias-shadow,rgba(0,0,0,.06));color:var(--dsw-alias-primary,#2563eb);}",
4481
- "#mma-host .mma-pop-list{margin:0 0 12px;display:flex;flex-direction:column;gap:6px!important;max-height:280px;overflow-y:auto;padding:4px 0;}",
5587
+ "#mma-host .mma-pop-seg button:disabled{opacity:.45;cursor:not-allowed;}",
5588
+ "#mma-host .mma-pop-lab{font-size:10px;letter-spacing:.12em;text-transform:uppercase;opacity:.55;margin:10px 2px 6px;}",
5589
+ "#mma-host .mma-pop-lab:first-child{margin-top:0;}",
5590
+ "#mma-host .mma-pop-list{margin:0;display:flex;flex-direction:column;gap:6px!important;max-height:280px;overflow-y:auto;padding:4px 0;scrollbar-width:thin;scrollbar-color:color-mix(in srgb,var(--dsw-alias-fg,#111) 35%,transparent) transparent;}",
5591
+ "#mma-host .mma-pop-list::-webkit-scrollbar{width:8px;}",
5592
+ "#mma-host .mma-pop-list::-webkit-scrollbar-track{background:transparent;}",
5593
+ "#mma-host .mma-pop-list::-webkit-scrollbar-thumb{background:color-mix(in srgb,var(--dsw-alias-fg,#111) 35%,transparent);border-radius:99px;}",
4482
5594
  "#mma-host .mma-swatch{display:flex;align-items:center;gap:10px;width:100%;min-height:34px;padding:0 10px;border:0;border-radius:8px;background:transparent;cursor:pointer;color:inherit;font-size:13px;text-align:left;white-space:nowrap;overflow:hidden;}",
4483
5595
  "#mma-host .mma-swatch:hover{background:var(--dsw-alias-accent,var(--dsw-alias-muted,#f3f4f6));}",
4484
5596
  "#mma-host .mma-swatch[data-on='1']{background:var(--dsw-alias-accent,var(--dsw-alias-muted,#f3f4f6));font-weight:600;}",
@@ -4557,17 +5669,106 @@ var CSS = [
4557
5669
  "#mma-host .mma-dialog-actions{display:flex;justify-content:flex-end;gap:8px;}",
4558
5670
  "#mma-host .mma-dialog-actions button{height:32px;padding:0 12px;border-radius:8px;border:1px solid var(--dsw-alias-border,#e5e7eb);background:var(--dsw-alias-surface,#fff);cursor:pointer;}",
4559
5671
  "#mma-host .mma-dialog-actions .go{background:#dc2626;color:#fff;border-color:#dc2626;}",
4560
- "#mma-host .mma-settings{position:absolute;inset:0;background:var(--dsw-alias-bg,#f7f7f8);z-index:4;overflow:auto;padding:20px 22px 32px;display:none;}",
4561
- "#mma-host .mma-settings[data-open='1']{display:block;}",
4562
- "#mma-host .mma-settings h3{margin:0 0 6px;font-size:16px;}",
4563
- "#mma-host .mma-settings-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:14px;}",
4564
- "#mma-host .mma-settings label{display:block;margin-bottom:12px;font-size:12px;opacity:.75;}",
4565
- "#mma-host .mma-settings input,#mma-host .mma-settings select{display:block;width:100%;margin-top:4px;height:32px;padding:0 10px;border:1px solid var(--dsw-alias-border,#e5e7eb);border-radius:8px;background:var(--dsw-alias-surface,#fff);color:inherit;font-size:13px;box-sizing:border-box;}",
4566
- "#mma-host .mma-settings-actions{display:flex;flex-direction:column;align-items:flex-end;gap:8px;margin-top:18px;}",
4567
- "#mma-host .mma-settings-msg{font-size:12px;opacity:.75;line-height:1.4;text-align:right;width:100%;}",
4568
- "#mma-host .mma-settings-msg.err{color:#dc2626;}",
4569
- "#mma-host #mma-cfg-save{height:34px;padding:0 18px;border:1px solid var(--dsw-alias-border,#e5e7eb);border-radius:8px;background:var(--dsw-alias-surface,#fff);color:inherit;font-size:14px;font-weight:600;opacity:1;cursor:pointer;}",
4570
- "#mma-host #mma-cfg-save:hover{background:var(--dsw-alias-muted,#f3f4f6);border-color:var(--dsw-alias-primary,#2563eb);color:var(--dsw-alias-primary,#2563eb);}",
5672
+ "#mma-host .mma-settings{position:absolute;inset:0;z-index:4;display:none;flex-direction:column;background:var(--dsw-alias-bg,#f7f7f8);color:inherit;overflow:hidden;}",
5673
+ "#mma-host .mma-settings[data-open='1']{display:flex;}",
5674
+ "#mma-host .mma-settings-head{flex:0 0 auto;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 14px;border-bottom:1px solid var(--dsw-alias-border,#e5e7eb);background:var(--dsw-alias-surface,#fff);}",
5675
+ "#mma-host .mma-settings-head-left{display:flex;align-items:center;gap:8px;min-width:0;}",
5676
+ "#mma-host .mma-settings-head-icon{opacity:.7;font-size:14px;}",
5677
+ "#mma-host .mma-settings-head h3{margin:0;font-size:15px;font-weight:650;letter-spacing:-.01em;}",
5678
+ "#mma-host .mma-settings-head-right{display:flex;align-items:center;gap:8px;}",
5679
+ "#mma-host .mma-settings-meta{font-size:11px;opacity:.55;white-space:nowrap;}",
5680
+ "#mma-host .mma-settings-body{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;}",
5681
+ "#mma-host .mma-settings-rail{flex:0 0 auto;display:flex;gap:4px;padding:8px 10px;border-bottom:1px solid var(--dsw-alias-border,#e5e7eb);background:var(--dsw-alias-surface,#fff);overflow-x:auto;}",
5682
+ "#mma-host .mma-settings-rail-title{display:none;}",
5683
+ "#mma-host .mma-settings-rail-item{flex:0 0 auto;height:30px;padding:0 10px;border:0;border-radius:8px;background:transparent;color:inherit;opacity:.7;font-size:12.5px;cursor:pointer;}",
5684
+ "#mma-host .mma-settings-rail-item:hover{background:var(--dsw-alias-muted,#f3f4f6);opacity:1;}",
5685
+ "#mma-host .mma-settings-rail-item[data-on='1']{background:var(--dsw-alias-muted,#f3f4f6);opacity:1;font-weight:600;color:var(--dsw-alias-primary,#2563eb);box-shadow:inset 0 -2px 0 var(--dsw-alias-primary,#2563eb);}",
5686
+ "#mma-host .mma-settings-content{flex:1 1 auto;min-height:0;overflow:auto;padding:18px 16px 24px;scroll-behavior:smooth;}",
5687
+ "#mma-host .mma-settings-section{scroll-margin-top:12px;}",
5688
+ "#mma-host .mma-settings-section h4{margin:0 0 14px;font-size:12.5px;font-weight:600;letter-spacing:.5px;text-transform:uppercase;opacity:.55;}",
5689
+ "#mma-host .mma-settings-field{display:flex;flex-direction:column;gap:8px;margin-bottom:20px;}",
5690
+ "#mma-host .mma-settings-label{font-size:12.5px;opacity:.75;}",
5691
+ "#mma-host .mma-settings-control{min-width:0;}",
5692
+ "#mma-host .mma-settings-control--model{max-width:520px;}",
5693
+ "#mma-host .mma-settings input{display:block;width:100%;height:34px;padding:0 10px;border:1px solid var(--dsw-alias-border,#e5e7eb);border-radius:9px;background:var(--dsw-alias-surface,#fff);color:inherit;font-size:13px;box-sizing:border-box;}",
5694
+ "#mma-host .mma-settings input:focus{outline:none;border-color:var(--dsw-alias-primary,#2563eb);box-shadow:0 0 0 2px color-mix(in srgb, var(--dsw-alias-primary,#2563eb) 20%, transparent);}",
5695
+ "#mma-host .mma-settings input:disabled{opacity:.55;}",
5696
+ "#mma-host .mma-settings-field.is-error input{border-color:var(--dsw-alias-destructive,#dc2626);}",
5697
+ "#mma-host .mma-seg{display:flex;gap:4px;padding:3px;border-radius:10px;background:var(--dsw-alias-muted,#f3f4f6);max-width:280px;}",
5698
+ "#mma-host .mma-seg button{flex:1;height:30px;padding:0 8px;border:0;border-radius:8px;background:transparent;cursor:pointer;font-size:12.5px;color:inherit;}",
5699
+ "#mma-host .mma-seg button[data-on='1']{background:var(--dsw-alias-surface,#fff);font-weight:600;box-shadow:0 1px 2px var(--dsw-alias-shadow,rgba(0,0,0,.06));color:var(--dsw-alias-primary,#2563eb);}",
5700
+ "#mma-host .mma-optgrid{display:grid;gap:10px;}",
5701
+ "#mma-host .mma-optgrid--2{grid-template-columns:repeat(2,minmax(0,1fr));}",
5702
+ "#mma-host .mma-optgrid--3{grid-template-columns:repeat(2,minmax(0,1fr));}",
5703
+ "#mma-host .mma-optgrid--4{grid-template-columns:repeat(2,minmax(0,1fr));}",
5704
+ "#mma-host .mma-optcard{position:relative;display:flex;flex-direction:column;gap:8px;padding:10px;border:1px solid var(--dsw-alias-border,#e5e7eb);border-radius:10px;background:var(--dsw-alias-surface,#fff);color:inherit;cursor:pointer;text-align:left;}",
5705
+ "#mma-host .mma-optcard:hover{border-color:color-mix(in srgb, var(--dsw-alias-fg,#111) 28%, var(--dsw-alias-border,#e5e7eb));}",
5706
+ "#mma-host .mma-optcard:focus-visible{outline:2px solid var(--dsw-alias-primary,#2563eb);outline-offset:2px;}",
5707
+ "#mma-host .mma-optcard[data-on='1']{border-color:var(--dsw-alias-primary,#2563eb);background:color-mix(in srgb, var(--dsw-alias-primary,#2563eb) 8%, var(--dsw-alias-surface,#fff));}",
5708
+ "#mma-host .mma-optcard:disabled,.mma-optcard--ghost{opacity:.55;cursor:default;border-style:dashed;}",
5709
+ "#mma-host .mma-optcard-preview{display:block;height:42px;border-radius:8px;overflow:hidden;background:var(--dsw-alias-muted,#f3f4f6);}",
5710
+ "#mma-host .mma-optcard-preview--plus{display:flex;align-items:center;justify-content:center;font-size:20px;opacity:.7;}",
5711
+ "#mma-host .mma-optcard-label{font-size:12px;line-height:1.2;}",
5712
+ "#mma-host .mma-optcard-check{position:absolute;top:8px;right:8px;width:18px;height:18px;border-radius:999px;background:var(--dsw-alias-primary,#2563eb);color:var(--dsw-alias-primary-fg,#fff);font-size:11px;display:flex;align-items:center;justify-content:center;}",
5713
+ "#mma-host .mma-preview-theme{display:flex;flex-direction:column;justify-content:center;gap:6px;height:100%;padding:8px;box-sizing:border-box;}",
5714
+ "#mma-host .mma-preview-theme--light{background:var(--dsw-alias-bg,#f7f7f8);}",
5715
+ "#mma-host .mma-preview-theme--dark{background:color-mix(in srgb, var(--dsw-alias-fg,#111) 88%, transparent);}",
5716
+ "#mma-host .mma-preview-theme--system{display:flex;flex-direction:row;padding:0;}",
5717
+ "#mma-host .mma-preview-theme-half{flex:1;height:100%;display:flex;flex-direction:column;justify-content:center;gap:6px;padding:8px;box-sizing:border-box;}",
5718
+ "#mma-host .mma-preview-theme-half--light{background:var(--dsw-alias-bg,#f7f7f8);}",
5719
+ "#mma-host .mma-preview-theme-half--dark{background:color-mix(in srgb, var(--dsw-alias-fg,#111) 88%, transparent);}",
5720
+ "#mma-host .mma-preview-bar{display:block;height:4px;border-radius:999px;background:color-mix(in srgb, var(--dsw-alias-fg,#111) 25%, transparent);}",
5721
+ "#mma-host .mma-preview-theme--dark .mma-preview-bar,#mma-host .mma-preview-theme-half--dark .mma-preview-bar{background:color-mix(in srgb, #fff 45%, transparent);}",
5722
+ "#mma-host .mma-preview-swatches{display:flex;align-items:center;justify-content:center;gap:8px;height:100%;}",
5723
+ "#mma-host .mma-preview-swatches > span{width:14px;height:14px;border-radius:999px;border:1px solid color-mix(in srgb, var(--dsw-alias-fg,#111) 12%, transparent);}",
5724
+ "#mma-host .mma-preview-cardstyle{display:block;height:100%;margin:8px;border-radius:6px;border:1px solid var(--dsw-alias-border,#e5e7eb);background:var(--dsw-alias-surface,#fff);box-sizing:border-box;}",
5725
+ "#mma-host .mma-preview-cardstyle--stamp{box-shadow:inset 0 0 0 1px var(--dsw-alias-border,#e5e7eb);position:relative;}",
5726
+ "#mma-host .mma-preview-cardstyle--stamp::after{content:'';position:absolute;top:4px;right:4px;width:18px;height:8px;border-radius:3px;background:var(--dsw-alias-muted,#f3f4f6);}",
5727
+ "#mma-host .mma-preview-cardstyle--etch{background:transparent;}",
5728
+ "#mma-host .mma-preview-cardstyle--hero{box-shadow:3px 3px 0 var(--dsw-alias-border,#e5e7eb);}",
5729
+ "#mma-host .mma-preview-cardstyle--list{border:0;border-bottom:1px solid var(--dsw-alias-border,#e5e7eb);border-radius:0;margin:14px 8px;}",
5730
+ "#mma-host .mma-port-row{display:flex;flex-wrap:wrap;align-items:center;gap:8px;}",
5731
+ "#mma-host .mma-port-addon{display:inline-flex;align-items:center;justify-content:center;height:34px;padding:0 8px;border:1px solid var(--dsw-alias-border,#e5e7eb);border-right:0;border-radius:9px 0 0 9px;background:var(--dsw-alias-muted,#f3f4f6);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px;}",
5732
+ "#mma-host .mma-port-row input{width:120px;border-radius:0 9px 9px 0;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;}",
5733
+ "#mma-host .mma-port-url{font-size:11px;opacity:.65;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;}",
5734
+ "#mma-host .mma-model-row{display:flex;flex-wrap:wrap;align-items:center;gap:8px;}",
5735
+ "#mma-host .mma-model-row input{flex:1 1 180px;min-width:0;}",
5736
+ "#mma-host .mma-status-pill{display:inline-flex;align-items:center;gap:6px;height:24px;padding:0 10px;border-radius:20px;border:1px solid var(--dsw-alias-border,#e5e7eb);font-size:11px;opacity:.75;white-space:nowrap;}",
5737
+ "#mma-host .mma-ghostbtn{height:30px;padding:0 10px;border:1px solid var(--dsw-alias-border,#e5e7eb);border-radius:8px;background:var(--dsw-alias-surface,#fff);color:inherit;font-size:12px;cursor:pointer;text-decoration:none;display:inline-flex;align-items:center;}",
5738
+ "#mma-host .mma-ghostbtn:hover{background:var(--dsw-alias-muted,#f3f4f6);}",
5739
+ "#mma-host .mma-ghostbtn:disabled{opacity:.55;cursor:default;}",
5740
+ "#mma-host .mma-primarybtn{height:34px;padding:0 16px;border:0;border-radius:8px;background:var(--dsw-alias-primary,#2563eb);color:var(--dsw-alias-primary-fg,#fff);font-size:13px;font-weight:600;cursor:pointer;}",
5741
+ "#mma-host .mma-primarybtn:disabled{opacity:.45;cursor:default;}",
5742
+ "#mma-host .mma-settings-hint{margin:0;font-size:11.5px;opacity:.6;line-height:1.45;}",
5743
+ "#mma-host .mma-settings-field.is-error .mma-settings-hint{color:var(--dsw-alias-destructive,#dc2626);opacity:1;}",
5744
+ "#mma-host .mma-settings-rule{border:0;border-top:1px solid var(--dsw-alias-border,#e5e7eb);margin:20px 0;}",
5745
+ "#mma-host .mma-about-meta{margin-bottom:10px;display:flex;flex-direction:column;gap:6px;}",
5746
+ "#mma-host .mma-about-meta code{display:inline-block;width:fit-content;font-size:12px;padding:3px 8px;border-radius:6px;background:var(--dsw-alias-muted,#f3f4f6);}",
5747
+ "#mma-host .mma-about-pkgs{list-style:none;margin:0 0 12px;padding:0;display:flex;flex-direction:column;gap:6px;}",
5748
+ "#mma-host .mma-about-pkgs li{display:flex;align-items:center;justify-content:space-between;gap:12px;font-size:12px;padding:8px 10px;border-radius:9px;background:var(--dsw-alias-surface,#fff);border:1px solid var(--dsw-alias-border,#e5e7eb);}",
5749
+ "#mma-host .mma-about-pkgs li span{opacity:.8;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}",
5750
+ "#mma-host .mma-about-pkgs li code{font-size:11px;font-variant-numeric:tabular-nums;opacity:.9;}",
5751
+ "#mma-host .mma-about-actions{display:flex;flex-direction:column;align-items:flex-start;gap:8px;}",
5752
+ "#mma-host .mma-settings-foot{flex:0 0 auto;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:10px;padding:10px 14px;border-top:1px solid var(--dsw-alias-border,#e5e7eb);background:var(--dsw-alias-surface,#fff);}",
5753
+ "#mma-host .mma-settings-foot-left{display:flex;flex-wrap:wrap;align-items:center;gap:4px;min-width:0;}",
5754
+ "#mma-host .mma-settings-foot-right{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-left:auto;}",
5755
+ "#mma-host .mma-settings-dirty{font-size:11.5px;color:var(--dsw-alias-primary,#2563eb);}",
5756
+ "#mma-host .mma-settings-msg{flex-basis:100%;font-size:12px;opacity:.75;line-height:1.4;}",
5757
+ "#mma-host .mma-settings-msg.err{color:var(--dsw-alias-destructive,#dc2626);}",
5758
+ "#mma-host .mma-settings-confirm{position:absolute;inset:0;z-index:5;display:flex;align-items:center;justify-content:center;padding:16px;background:color-mix(in srgb, var(--dsw-alias-fg,#111) 35%, transparent);}",
5759
+ /* Wide layout: data-wide from ResizeObserver (reliable on fixed #mma-host) + container query fallback. */
5760
+ "#mma-host .mma-settings[data-wide='1'] .mma-settings-body{flex-direction:row;}",
5761
+ "#mma-host .mma-settings[data-wide='1'] .mma-settings-rail{flex:0 0 148px;flex-direction:column;align-items:stretch;gap:2px;border-bottom:0;border-right:1px solid var(--dsw-alias-border,#e5e7eb);padding:14px 10px;overflow:visible;}",
5762
+ "#mma-host .mma-settings[data-wide='1'] .mma-settings-rail-title{display:block;font-size:10px;letter-spacing:.08em;text-transform:uppercase;opacity:.45;margin:0 8px 8px;}",
5763
+ "#mma-host .mma-settings[data-wide='1'] .mma-settings-rail-item{width:100%;justify-content:flex-start;text-align:left;box-shadow:none;border-radius:8px;position:relative;}",
5764
+ "#mma-host .mma-settings[data-wide='1'] .mma-settings-rail-item[data-on='1']{box-shadow:none;background:var(--dsw-alias-muted,#f3f4f6);}",
5765
+ "#mma-host .mma-settings[data-wide='1'] .mma-settings-rail-item[data-on='1']::before{content:'';position:absolute;left:0;top:6px;bottom:6px;width:2px;border-radius:2px;background:var(--dsw-alias-primary,#2563eb);}",
5766
+ "#mma-host .mma-settings[data-wide='1'] .mma-settings-content{padding:22px 26px 28px;}",
5767
+ "#mma-host .mma-settings[data-wide='1'] .mma-optgrid--3{grid-template-columns:repeat(3,minmax(0,1fr));}",
5768
+ "#mma-host .mma-settings[data-wide='1'] .mma-optgrid--4{grid-template-columns:repeat(4,minmax(0,1fr));}",
5769
+ "#mma-host .mma-settings[data-wide='1'] .mma-settings-field--row{flex-direction:row;align-items:flex-start;gap:16px;}",
5770
+ "#mma-host .mma-settings[data-wide='1'] .mma-settings-field--row > .mma-settings-label{flex:0 0 112px;padding-top:8px;}",
5771
+ "#mma-host .mma-settings[data-wide='1'] .mma-settings-field--row > .mma-settings-control{flex:1 1 auto;}",
4571
5772
  "#mma-host .mma-browse{position:absolute;inset:0;background:var(--dsw-alias-bg,#f7f7f8);z-index:3;overflow:auto;padding:16px 18px;display:none;}",
4572
5773
  "#mma-host .mma-browse[data-open='1']{display:block;}",
4573
5774
  "#mma-host .mma-browse-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:12px;}",
@@ -4604,7 +5805,15 @@ var CSS = [
4604
5805
  "#mma-host .mma-diff-line--del{color:#991b1b;background:rgba(220,38,38,.10);}",
4605
5806
  "#mma-host .mma-diff-line--ctx{color:inherit;opacity:.85;}",
4606
5807
  "#mma-host .mma-bempty{padding:20px;opacity:.6;text-align:center;font-size:13px;}",
4607
- "#mma-host .mma-berr{padding:16px;color:#b91c1c;font-size:13px;}"
5808
+ "#mma-host .mma-berr{padding:16px;color:#b91c1c;font-size:13px;}",
5809
+ "#mma-host .mma-storage-notice{display:flex;gap:12px;align-items:flex-start;justify-content:space-between;padding:12px 14px;margin:0 0 12px;border-radius:10px;background:color-mix(in srgb, var(--dsw-alias-warning, #f59e0b) 14%, var(--dsw-alias-bg, #f7f7f8));border:1px solid color-mix(in srgb, var(--dsw-alias-warning, #f59e0b) 35%, transparent);}",
5810
+ "#mma-host .mma-storage-notice-body{min-width:0;flex:1;}",
5811
+ "#mma-host .mma-storage-notice-body b{display:block;font-size:13px;margin:0 0 4px;}",
5812
+ "#mma-host .mma-storage-notice-body p{margin:0;font-size:12px;line-height:1.45;opacity:.85;}",
5813
+ "#mma-host .mma-storage-notice-top{margin-top:6px !important;}",
5814
+ "#mma-host .mma-storage-notice-actions{display:flex;flex-direction:column;gap:6px;flex-shrink:0;}",
5815
+ "#mma-host .mma-storage-notice-actions button{font-size:12px;padding:6px 10px;border-radius:8px;border:1px solid color-mix(in srgb, currentColor 18%, transparent);background:var(--dsw-alias-bg, #fff);cursor:pointer;}",
5816
+ "#mma-host .mma-storage-notice-copy{font-weight:600;}"
4608
5817
  ];
4609
5818
  function injectPanelCss() {
4610
5819
  if (typeof document === "undefined") return;
@@ -4619,26 +5828,21 @@ function injectPanelCss() {
4619
5828
  }
4620
5829
 
4621
5830
  // ../panel/src/panel.tsx
4622
- var import_jsx_runtime11 = require("react/jsx-runtime");
4623
- function loadCustomPalettes(host) {
4624
- if (!host.palettes) return;
4625
- host.palettes().then((list) => {
4626
- const custom = {};
4627
- for (const p of list) {
4628
- custom[p.id] = { label: p.label, swatch: p.swatch, tokens: p.tokens };
4629
- }
4630
- setPanelState({ customPalettes: custom });
4631
- }).catch(() => {
4632
- });
4633
- }
5831
+ var import_jsx_runtime12 = require("react/jsx-runtime");
4634
5832
  function createMiniAppPanel(host, options) {
4635
5833
  const locale = resolvePanelLocale(options?.locale ?? host.locale);
4636
5834
  const i18n = createPanelI18n(locale);
5835
+ const prev = getPanelState();
4637
5836
  resetPanelState({
4638
5837
  tabs: [{ id: "all", title: i18n.t("tabs.all"), kind: "all" }],
4639
5838
  capabilities: capabilitiesOf(host),
4640
5839
  locale,
4641
- emptyText: host.emptyText
5840
+ emptyText: host.emptyText,
5841
+ theme: prev.theme,
5842
+ palette: prev.palette,
5843
+ dock: prev.dock,
5844
+ cardStyle: prev.cardStyle,
5845
+ visible: prev.visible
4642
5846
  });
4643
5847
  let rootEl = null;
4644
5848
  let root = null;
@@ -4655,7 +5859,7 @@ function createMiniAppPanel(host, options) {
4655
5859
  loadCustomPalettes(host);
4656
5860
  if (!root) root = (0, import_client.createRoot)(el);
4657
5861
  root.render(
4658
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PanelProvider, { actions, i18n, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(MiniAppPanel, {}) })
5862
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PanelProvider, { actions, i18n, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(MiniAppPanel, {}) })
4659
5863
  );
4660
5864
  },
4661
5865
  unmount() {
@@ -4795,7 +5999,7 @@ function readStoredMode(storage) {
4795
5999
  if (!storage) return null;
4796
6000
  try {
4797
6001
  const m = storage.getItem("mma-theme-mode");
4798
- return m === "light" || m === "dark" ? m : null;
6002
+ return m === "light" || m === "dark" || m === "system" ? m : null;
4799
6003
  } catch {
4800
6004
  return null;
4801
6005
  }
@@ -5023,7 +6227,9 @@ var DshShell = class {
5023
6227
  emptyText: "\u8FD8\u6CA1\u6709\u5C0F\u7A0B\u5E8F\u3002\n\u5728\u5BF9\u8BDD\u91CC\u7528 skill \u751F\u6210\uFF0C\u6216\u628A\u793A\u4F8B\u653E\u5230 runtime/apps/",
5024
6228
  onOpen: () => this.openPanel(),
5025
6229
  onClose: () => this.closePanel(),
5026
- onHostChange: (next) => this.migrateOrigin(next),
6230
+ onHostChange: (next) => {
6231
+ void this.migrateOrigin(next);
6232
+ },
5027
6233
  onConfigSaved: (form) => this.afterConfigSaved(form),
5028
6234
  frameController: {
5029
6235
  url: (appId) => this.frames.url(appId),
@@ -5043,11 +6249,13 @@ var DshShell = class {
5043
6249
  envFor(appId) {
5044
6250
  const s = getPanelState();
5045
6251
  const app = s.apps.find((a) => a.id === appId);
5046
- const t2 = app?.theme;
6252
+ const theme = resolveMode(app?.theme?.theme || s.theme);
6253
+ const palette = app?.theme?.palette || s.palette;
5047
6254
  return {
5048
- theme: t2?.theme || s.theme,
5049
- palette: t2?.palette || s.palette,
5050
- dock: s.dock
6255
+ theme,
6256
+ palette,
6257
+ dock: s.dock,
6258
+ vars: themeCssVars(theme, palette, s.customPalettes)
5051
6259
  };
5052
6260
  }
5053
6261
  ensureFrameContainer() {
@@ -5077,11 +6285,20 @@ var DshShell = class {
5077
6285
  if (host) applyThemeTo(host, theme, palette);
5078
6286
  this.frames.postEnvAll();
5079
6287
  }
5080
- migrateOrigin(next) {
5081
- if (next === this.origin) return;
5082
- this.origin = next;
5083
- writeStoredAppsOrigin(safeStorage(), next);
5084
- this.frames.unmountAll();
6288
+ async migrateOrigin(next) {
6289
+ const origin = next.replace(/\/$/, "");
6290
+ if (origin === this.origin) return;
6291
+ for (let i = 0; i < 30; i++) {
6292
+ try {
6293
+ const res = await fetch(`${origin}/health`);
6294
+ if (res.ok) break;
6295
+ } catch {
6296
+ }
6297
+ await new Promise((r) => setTimeout(r, 100));
6298
+ }
6299
+ this.origin = origin;
6300
+ writeStoredAppsOrigin(safeStorage(), origin);
6301
+ for (const id of [...this.frames.map.keys()]) this.frames.reload(id);
5085
6302
  }
5086
6303
  afterConfigSaved(form) {
5087
6304
  if (typeof form.theme === "string" && typeof form.palette === "string") {
@@ -5334,15 +6551,7 @@ function subscribeAppOpen(current) {
5334
6551
  const es = new EventSource(`${current.origin}/api/events`);
5335
6552
  sse = es;
5336
6553
  es.addEventListener("app:open", (e) => {
5337
- let appId = "";
5338
- try {
5339
- const d = JSON.parse(e.data || "{}");
5340
- if (d && typeof d === "object" && "appId" in d && typeof d.appId === "string") {
5341
- appId = d.appId;
5342
- }
5343
- } catch {
5344
- return;
5345
- }
6554
+ const appId = readAppId(e.data);
5346
6555
  if (!appId) return;
5347
6556
  current.openPanel();
5348
6557
  void current.host.fetchApps().then((apps) => {
@@ -5350,10 +6559,42 @@ function subscribeAppOpen(current) {
5350
6559
  if (app) current.bindPanel().actions.openAppTab(app);
5351
6560
  });
5352
6561
  });
6562
+ es.addEventListener("app:reload", (e) => {
6563
+ const appId = readAppId(e.data);
6564
+ if (appId && current.frames.map.has(appId)) current.frames.reload(appId);
6565
+ });
6566
+ es.addEventListener("app:eval", (e) => {
6567
+ const query = readEvalQuery(e.data);
6568
+ if (query) relayViewEval(current.origin, current.frames, query);
6569
+ });
5353
6570
  } catch (err) {
5354
6571
  console.warn("[monkey-mini-app-client] sse subscribe failed", err);
5355
6572
  }
5356
6573
  }
6574
+ function readAppId(raw) {
6575
+ try {
6576
+ const d = JSON.parse(raw || "{}");
6577
+ return d && typeof d === "object" && typeof d.appId === "string" ? d.appId : "";
6578
+ } catch {
6579
+ return "";
6580
+ }
6581
+ }
6582
+ function readEvalQuery(raw) {
6583
+ try {
6584
+ const d = JSON.parse(raw || "{}");
6585
+ if (!d || typeof d !== "object") return null;
6586
+ const q = d;
6587
+ if (typeof q.requestId !== "string" || typeof q.appId !== "string") return null;
6588
+ return {
6589
+ requestId: q.requestId,
6590
+ appId: q.appId,
6591
+ code: typeof q.code === "string" ? q.code : "",
6592
+ maxBytes: typeof q.maxBytes === "number" ? q.maxBytes : 0
6593
+ };
6594
+ } catch {
6595
+ return null;
6596
+ }
6597
+ }
5357
6598
  function bindUiEvents(current) {
5358
6599
  if (window.__mmaOpenBound) return;
5359
6600
  window.__mmaOpenBound = true;