@molecule/app-ide-react 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (250) hide show
  1. package/LICENSE +115 -0
  2. package/dist/chatTimelineOrdering.d.ts +50 -0
  3. package/dist/chatTimelineOrdering.d.ts.map +1 -0
  4. package/dist/chatTimelineOrdering.js +46 -0
  5. package/dist/chatTimelineOrdering.js.map +1 -0
  6. package/dist/command-metadata.d.ts +78 -0
  7. package/dist/command-metadata.d.ts.map +1 -0
  8. package/dist/command-metadata.js +214 -0
  9. package/dist/command-metadata.js.map +1 -0
  10. package/dist/components/ActivityCard.d.ts +28 -0
  11. package/dist/components/ActivityCard.d.ts.map +1 -0
  12. package/dist/components/ActivityCard.js +62 -0
  13. package/dist/components/ActivityCard.js.map +1 -0
  14. package/dist/components/AutoCommitBadge.d.ts +58 -0
  15. package/dist/components/AutoCommitBadge.d.ts.map +1 -0
  16. package/dist/components/AutoCommitBadge.js +78 -0
  17. package/dist/components/AutoCommitBadge.js.map +1 -0
  18. package/dist/components/ChatPanel.d.ts +184 -0
  19. package/dist/components/ChatPanel.d.ts.map +1 -0
  20. package/dist/components/ChatPanel.js +6691 -0
  21. package/dist/components/ChatPanel.js.map +1 -0
  22. package/dist/components/CommandPalette.d.ts +18 -0
  23. package/dist/components/CommandPalette.d.ts.map +1 -0
  24. package/dist/components/CommandPalette.js +33 -0
  25. package/dist/components/CommandPalette.js.map +1 -0
  26. package/dist/components/DeviceFrameSelector.d.ts +60 -0
  27. package/dist/components/DeviceFrameSelector.d.ts.map +1 -0
  28. package/dist/components/DeviceFrameSelector.js +230 -0
  29. package/dist/components/DeviceFrameSelector.js.map +1 -0
  30. package/dist/components/EditorPanel.d.ts +17 -0
  31. package/dist/components/EditorPanel.d.ts.map +1 -0
  32. package/dist/components/EditorPanel.js +241 -0
  33. package/dist/components/EditorPanel.js.map +1 -0
  34. package/dist/components/FileExplorer.d.ts +64 -0
  35. package/dist/components/FileExplorer.d.ts.map +1 -0
  36. package/dist/components/FileExplorer.js +1111 -0
  37. package/dist/components/FileExplorer.js.map +1 -0
  38. package/dist/components/FileExplorerContextMenu.d.ts +37 -0
  39. package/dist/components/FileExplorerContextMenu.d.ts.map +1 -0
  40. package/dist/components/FileExplorerContextMenu.js +241 -0
  41. package/dist/components/FileExplorerContextMenu.js.map +1 -0
  42. package/dist/components/HelpCard.d.ts +34 -0
  43. package/dist/components/HelpCard.d.ts.map +1 -0
  44. package/dist/components/HelpCard.js +28 -0
  45. package/dist/components/HelpCard.js.map +1 -0
  46. package/dist/components/Icon.d.ts +50 -0
  47. package/dist/components/Icon.d.ts.map +1 -0
  48. package/dist/components/Icon.js +25 -0
  49. package/dist/components/Icon.js.map +1 -0
  50. package/dist/components/KeyboardShortcutsPanel.d.ts +19 -0
  51. package/dist/components/KeyboardShortcutsPanel.d.ts.map +1 -0
  52. package/dist/components/KeyboardShortcutsPanel.js +187 -0
  53. package/dist/components/KeyboardShortcutsPanel.js.map +1 -0
  54. package/dist/components/MarkdownContent.d.ts +52 -0
  55. package/dist/components/MarkdownContent.d.ts.map +1 -0
  56. package/dist/components/MarkdownContent.js +303 -0
  57. package/dist/components/MarkdownContent.js.map +1 -0
  58. package/dist/components/ModelsTable.d.ts +38 -0
  59. package/dist/components/ModelsTable.d.ts.map +1 -0
  60. package/dist/components/ModelsTable.js +96 -0
  61. package/dist/components/ModelsTable.js.map +1 -0
  62. package/dist/components/PreviewPanel.d.ts +66 -0
  63. package/dist/components/PreviewPanel.d.ts.map +1 -0
  64. package/dist/components/PreviewPanel.js +1752 -0
  65. package/dist/components/PreviewPanel.js.map +1 -0
  66. package/dist/components/QuickOpen.d.ts +20 -0
  67. package/dist/components/QuickOpen.d.ts.map +1 -0
  68. package/dist/components/QuickOpen.js +72 -0
  69. package/dist/components/QuickOpen.js.map +1 -0
  70. package/dist/components/QuickPicker.d.ts +21 -0
  71. package/dist/components/QuickPicker.d.ts.map +1 -0
  72. package/dist/components/QuickPicker.js +174 -0
  73. package/dist/components/QuickPicker.js.map +1 -0
  74. package/dist/components/RelevantSkillSuggestion.d.ts +41 -0
  75. package/dist/components/RelevantSkillSuggestion.d.ts.map +1 -0
  76. package/dist/components/RelevantSkillSuggestion.js +85 -0
  77. package/dist/components/RelevantSkillSuggestion.js.map +1 -0
  78. package/dist/components/ReportModal.d.ts +35 -0
  79. package/dist/components/ReportModal.d.ts.map +1 -0
  80. package/dist/components/ReportModal.js +112 -0
  81. package/dist/components/ReportModal.js.map +1 -0
  82. package/dist/components/ResizeHandle.d.ts +21 -0
  83. package/dist/components/ResizeHandle.d.ts.map +1 -0
  84. package/dist/components/ResizeHandle.js +109 -0
  85. package/dist/components/ResizeHandle.js.map +1 -0
  86. package/dist/components/ScriptsCard.d.ts +37 -0
  87. package/dist/components/ScriptsCard.d.ts.map +1 -0
  88. package/dist/components/ScriptsCard.js +177 -0
  89. package/dist/components/ScriptsCard.js.map +1 -0
  90. package/dist/components/SearchPanel.d.ts +18 -0
  91. package/dist/components/SearchPanel.d.ts.map +1 -0
  92. package/dist/components/SearchPanel.js +449 -0
  93. package/dist/components/SearchPanel.js.map +1 -0
  94. package/dist/components/SettingsCard.d.ts +49 -0
  95. package/dist/components/SettingsCard.d.ts.map +1 -0
  96. package/dist/components/SettingsCard.js +59 -0
  97. package/dist/components/SettingsCard.js.map +1 -0
  98. package/dist/components/ShareModal.d.ts +32 -0
  99. package/dist/components/ShareModal.d.ts.map +1 -0
  100. package/dist/components/ShareModal.js +126 -0
  101. package/dist/components/ShareModal.js.map +1 -0
  102. package/dist/components/SidebarTabs.d.ts +18 -0
  103. package/dist/components/SidebarTabs.d.ts.map +1 -0
  104. package/dist/components/SidebarTabs.js +20 -0
  105. package/dist/components/SidebarTabs.js.map +1 -0
  106. package/dist/components/SkillsCard.d.ts +86 -0
  107. package/dist/components/SkillsCard.d.ts.map +1 -0
  108. package/dist/components/SkillsCard.js +227 -0
  109. package/dist/components/SkillsCard.js.map +1 -0
  110. package/dist/components/StreamingIndicator.d.ts +50 -0
  111. package/dist/components/StreamingIndicator.d.ts.map +1 -0
  112. package/dist/components/StreamingIndicator.js +142 -0
  113. package/dist/components/StreamingIndicator.js.map +1 -0
  114. package/dist/components/TabBar.d.ts +37 -0
  115. package/dist/components/TabBar.d.ts.map +1 -0
  116. package/dist/components/TabBar.js +187 -0
  117. package/dist/components/TabBar.js.map +1 -0
  118. package/dist/components/TipCard.d.ts +22 -0
  119. package/dist/components/TipCard.d.ts.map +1 -0
  120. package/dist/components/TipCard.js +73 -0
  121. package/dist/components/TipCard.js.map +1 -0
  122. package/dist/components/ToolCallCard.d.ts +18 -0
  123. package/dist/components/ToolCallCard.d.ts.map +1 -0
  124. package/dist/components/ToolCallCard.js +716 -0
  125. package/dist/components/ToolCallCard.js.map +1 -0
  126. package/dist/components/UserAvatar.d.ts +45 -0
  127. package/dist/components/UserAvatar.d.ts.map +1 -0
  128. package/dist/components/UserAvatar.js +80 -0
  129. package/dist/components/UserAvatar.js.map +1 -0
  130. package/dist/components/WorkspaceLayout.d.ts +19 -0
  131. package/dist/components/WorkspaceLayout.d.ts.map +1 -0
  132. package/dist/components/WorkspaceLayout.js +56 -0
  133. package/dist/components/WorkspaceLayout.js.map +1 -0
  134. package/dist/components/activity-utilities.d.ts +107 -0
  135. package/dist/components/activity-utilities.d.ts.map +1 -0
  136. package/dist/components/activity-utilities.js +154 -0
  137. package/dist/components/activity-utilities.js.map +1 -0
  138. package/dist/components/chat-autocommit-utilities.d.ts +172 -0
  139. package/dist/components/chat-autocommit-utilities.d.ts.map +1 -0
  140. package/dist/components/chat-autocommit-utilities.js +185 -0
  141. package/dist/components/chat-autocommit-utilities.js.map +1 -0
  142. package/dist/components/chat-card-style.d.ts +64 -0
  143. package/dist/components/chat-card-style.d.ts.map +1 -0
  144. package/dist/components/chat-card-style.js +74 -0
  145. package/dist/components/chat-card-style.js.map +1 -0
  146. package/dist/components/chat-commands.d.ts +17 -0
  147. package/dist/components/chat-commands.d.ts.map +1 -0
  148. package/dist/components/chat-commands.js +17 -0
  149. package/dist/components/chat-commands.js.map +1 -0
  150. package/dist/components/chat-commit-utilities.d.ts +29 -0
  151. package/dist/components/chat-commit-utilities.d.ts.map +1 -0
  152. package/dist/components/chat-commit-utilities.js +31 -0
  153. package/dist/components/chat-commit-utilities.js.map +1 -0
  154. package/dist/components/chat-cost-utilities.d.ts +62 -0
  155. package/dist/components/chat-cost-utilities.d.ts.map +1 -0
  156. package/dist/components/chat-cost-utilities.js +66 -0
  157. package/dist/components/chat-cost-utilities.js.map +1 -0
  158. package/dist/components/chat-effort-utilities.d.ts +96 -0
  159. package/dist/components/chat-effort-utilities.d.ts.map +1 -0
  160. package/dist/components/chat-effort-utilities.js +97 -0
  161. package/dist/components/chat-effort-utilities.js.map +1 -0
  162. package/dist/components/chat-help-utilities.d.ts +89 -0
  163. package/dist/components/chat-help-utilities.d.ts.map +1 -0
  164. package/dist/components/chat-help-utilities.js +131 -0
  165. package/dist/components/chat-help-utilities.js.map +1 -0
  166. package/dist/components/chat-model-mode-utilities.d.ts +143 -0
  167. package/dist/components/chat-model-mode-utilities.d.ts.map +1 -0
  168. package/dist/components/chat-model-mode-utilities.js +191 -0
  169. package/dist/components/chat-model-mode-utilities.js.map +1 -0
  170. package/dist/components/chat-models-utilities.d.ts +98 -0
  171. package/dist/components/chat-models-utilities.d.ts.map +1 -0
  172. package/dist/components/chat-models-utilities.js +136 -0
  173. package/dist/components/chat-models-utilities.js.map +1 -0
  174. package/dist/components/chat-report-utilities.d.ts +149 -0
  175. package/dist/components/chat-report-utilities.d.ts.map +1 -0
  176. package/dist/components/chat-report-utilities.js +151 -0
  177. package/dist/components/chat-report-utilities.js.map +1 -0
  178. package/dist/components/chat-scripts-utilities.d.ts +140 -0
  179. package/dist/components/chat-scripts-utilities.d.ts.map +1 -0
  180. package/dist/components/chat-scripts-utilities.js +158 -0
  181. package/dist/components/chat-scripts-utilities.js.map +1 -0
  182. package/dist/components/chat-settings-utilities.d.ts +72 -0
  183. package/dist/components/chat-settings-utilities.d.ts.map +1 -0
  184. package/dist/components/chat-settings-utilities.js +56 -0
  185. package/dist/components/chat-settings-utilities.js.map +1 -0
  186. package/dist/components/chat-share-utilities.d.ts +104 -0
  187. package/dist/components/chat-share-utilities.d.ts.map +1 -0
  188. package/dist/components/chat-share-utilities.js +90 -0
  189. package/dist/components/chat-share-utilities.js.map +1 -0
  190. package/dist/components/chat-skills-utilities.d.ts +242 -0
  191. package/dist/components/chat-skills-utilities.d.ts.map +1 -0
  192. package/dist/components/chat-skills-utilities.js +433 -0
  193. package/dist/components/chat-skills-utilities.js.map +1 -0
  194. package/dist/components/chat-stream-utilities.d.ts +74 -0
  195. package/dist/components/chat-stream-utilities.d.ts.map +1 -0
  196. package/dist/components/chat-stream-utilities.js +103 -0
  197. package/dist/components/chat-stream-utilities.js.map +1 -0
  198. package/dist/components/chat-tips-utilities.d.ts +108 -0
  199. package/dist/components/chat-tips-utilities.d.ts.map +1 -0
  200. package/dist/components/chat-tips-utilities.js +137 -0
  201. package/dist/components/chat-tips-utilities.js.map +1 -0
  202. package/dist/components/device-cycle.d.ts +84 -0
  203. package/dist/components/device-cycle.d.ts.map +1 -0
  204. package/dist/components/device-cycle.js +77 -0
  205. package/dist/components/device-cycle.js.map +1 -0
  206. package/dist/components/index.d.ts +36 -0
  207. package/dist/components/index.d.ts.map +1 -0
  208. package/dist/components/index.js +33 -0
  209. package/dist/components/index.js.map +1 -0
  210. package/dist/components/tool-call-utilities.d.ts +69 -0
  211. package/dist/components/tool-call-utilities.d.ts.map +1 -0
  212. package/dist/components/tool-call-utilities.js +368 -0
  213. package/dist/components/tool-call-utilities.js.map +1 -0
  214. package/dist/components/user-avatar-utilities.d.ts +41 -0
  215. package/dist/components/user-avatar-utilities.d.ts.map +1 -0
  216. package/dist/components/user-avatar-utilities.js +56 -0
  217. package/dist/components/user-avatar-utilities.js.map +1 -0
  218. package/dist/components/workspace-layout-utilities.d.ts +37 -0
  219. package/dist/components/workspace-layout-utilities.d.ts.map +1 -0
  220. package/dist/components/workspace-layout-utilities.js +58 -0
  221. package/dist/components/workspace-layout-utilities.js.map +1 -0
  222. package/dist/customEventCards.d.ts +117 -0
  223. package/dist/customEventCards.d.ts.map +1 -0
  224. package/dist/customEventCards.js +36 -0
  225. package/dist/customEventCards.js.map +1 -0
  226. package/dist/hooks/index.d.ts +8 -0
  227. package/dist/hooks/index.d.ts.map +1 -0
  228. package/dist/hooks/index.js +8 -0
  229. package/dist/hooks/index.js.map +1 -0
  230. package/dist/hooks/useKeyboardShortcuts.d.ts +48 -0
  231. package/dist/hooks/useKeyboardShortcuts.d.ts.map +1 -0
  232. package/dist/hooks/useKeyboardShortcuts.js +112 -0
  233. package/dist/hooks/useKeyboardShortcuts.js.map +1 -0
  234. package/dist/hooks/useViewport.d.ts +42 -0
  235. package/dist/hooks/useViewport.d.ts.map +1 -0
  236. package/dist/hooks/useViewport.js +61 -0
  237. package/dist/hooks/useViewport.js.map +1 -0
  238. package/dist/index.d.ts +75 -0
  239. package/dist/index.d.ts.map +1 -0
  240. package/dist/index.js +74 -0
  241. package/dist/index.js.map +1 -0
  242. package/dist/settings-metadata.d.ts +78 -0
  243. package/dist/settings-metadata.d.ts.map +1 -0
  244. package/dist/settings-metadata.js +131 -0
  245. package/dist/settings-metadata.js.map +1 -0
  246. package/dist/types.d.ts +751 -0
  247. package/dist/types.d.ts.map +1 -0
  248. package/dist/types.js +25 -0
  249. package/dist/types.js.map +1 -0
  250. package/package.json +90 -0
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Command Palette (Cmd+Shift+P) — searchable command list overlay.
3
+ *
4
+ * @module
5
+ */
6
+ import type { JSX } from 'react';
7
+ import type { CommandPaletteProps } from '../types.js';
8
+ /**
9
+ * Command Palette overlay.
10
+ *
11
+ * @param props - Component props.
12
+ * @returns The command palette element.
13
+ */
14
+ export declare function CommandPalette({ commands, onDismiss }: CommandPaletteProps): JSX.Element;
15
+ export declare namespace CommandPalette {
16
+ var displayName: string;
17
+ }
18
+ //# sourceMappingURL=CommandPalette.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommandPalette.d.ts","sourceRoot":"","sources":["../../src/components/CommandPalette.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,OAAO,CAAA;AAKhC,OAAO,KAAK,EAAE,mBAAmB,EAAmB,MAAM,aAAa,CAAA;AAGvE;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,mBAAmB,GAAG,GAAG,CAAC,OAAO,CAiCxF;yBAjCe,cAAc"}
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useMemo } from 'react';
3
+ import { t } from '@molecule/app-i18n';
4
+ import { QuickPicker } from './QuickPicker.js';
5
+ /**
6
+ * Command Palette overlay.
7
+ *
8
+ * @param props - Component props.
9
+ * @returns The command palette element.
10
+ */
11
+ export function CommandPalette({ commands, onDismiss }) {
12
+ const items = useMemo(() => commands.map((cmd) => ({
13
+ id: cmd.id,
14
+ label: cmd.category ? `${cmd.category}: ${cmd.label}` : cmd.label,
15
+ detail: cmd.shortcut,
16
+ })), [commands]);
17
+ const commandMap = useMemo(() => {
18
+ const map = new Map();
19
+ for (const cmd of commands) {
20
+ map.set(cmd.id, cmd.execute);
21
+ }
22
+ return map;
23
+ }, [commands]);
24
+ return (_jsx(QuickPicker, { items: items, placeholder: t('ide.commandPalette.placeholder', undefined, {
25
+ defaultValue: 'Type a command…',
26
+ }), onSelect: (item) => {
27
+ const handler = commandMap.get(item.id);
28
+ handler?.();
29
+ onDismiss();
30
+ }, onDismiss: onDismiss }));
31
+ }
32
+ CommandPalette.displayName = 'CommandPalette';
33
+ //# sourceMappingURL=CommandPalette.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommandPalette.js","sourceRoot":"","sources":["../../src/components/CommandPalette.tsx"],"names":[],"mappings":";AAOA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAA;AAE/B,OAAO,EAAE,CAAC,EAAE,MAAM,oBAAoB,CAAA;AAGtC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAE9C;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAuB;IACzE,MAAM,KAAK,GAAsB,OAAO,CACtC,GAAG,EAAE,CACH,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACrB,EAAE,EAAE,GAAG,CAAC,EAAE;QACV,KAAK,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,QAAQ,KAAK,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK;QACjE,MAAM,EAAE,GAAG,CAAC,QAAQ;KACrB,CAAC,CAAC,EACL,CAAC,QAAQ,CAAC,CACX,CAAA;IAED,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,EAAE;QAC9B,MAAM,GAAG,GAAG,IAAI,GAAG,EAAsB,CAAA;QACzC,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;YAC3B,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;QAC9B,CAAC;QACD,OAAO,GAAG,CAAA;IACZ,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAA;IAEd,OAAO,CACL,KAAC,WAAW,IACV,KAAK,EAAE,KAAK,EACZ,WAAW,EAAE,CAAC,CAAC,gCAAgC,EAAE,SAAS,EAAE;YAC1D,YAAY,EAAE,iBAAiB;SAChC,CAAC,EACF,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE;YACjB,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;YACvC,OAAO,EAAE,EAAE,CAAA;YACX,SAAS,EAAE,CAAA;QACb,CAAC,EACD,SAAS,EAAE,SAAS,GACpB,CACH,CAAA;AACH,CAAC;AAED,cAAc,CAAC,WAAW,GAAG,gBAAgB,CAAA"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Device-frame selector for the preview panel — a themed dropdown menu.
3
+ *
4
+ * The trigger shows the CURRENT frame's icon plus a chevron-down affordance.
5
+ * Opening it lists every device frame (Responsive / Desktop / Tablet / Mobile)
6
+ * with its bonded icon + i18n label; choosing one calls `onChange`. This
7
+ * replaced the old single-button "cycle on click" affordance — selecting a
8
+ * frame directly is far less error-prone than blind-cycling through four.
9
+ *
10
+ * Below the frames (after a divider) the menu also hosts the preview's "Rotate"
11
+ * and "Open in new tab" actions (P4-04) that used to be separate toolbar
12
+ * buttons. "Rotate" is a TOGGLE — a `menuitemcheckbox` that shows a check when the
13
+ * preview is rotated to landscape — shown only "where relevant", i.e. when the
14
+ * current frame is rotatable (fixed-frame tablet/mobile) and an `onRotate` handler
15
+ * is wired;
16
+ * "Open in new tab" is shown whenever an `onOpenExternal` handler is wired.
17
+ * Both share the frame rows' icon + label styling and the same roving focus.
18
+ *
19
+ * The menu is a minimal, accessible, fully themed popover built from ClassMap
20
+ * tokens + bonded `<Icon>`s (keyboard navigable, outside-click + Escape close,
21
+ * `role="menu"` / `role="menuitemradio"` with roving focus). It deliberately
22
+ * does NOT use the framework `Dropdown`: that component imports
23
+ * `react-router-dom` via `useLocation`, and this feature package is kept free
24
+ * of that dependency (the same reason {@link Tooltip} is subpath-imported here,
25
+ * never the package barrel).
26
+ *
27
+ * The trigger carries `cm.touchTarget`, so its compact `xs` desktop size grows
28
+ * to a WCAG-compliant >=44×44px hit-area on touch (coarse-pointer) devices.
29
+ *
30
+ * @module
31
+ */
32
+ import { type JSX } from 'react';
33
+ import type { DeviceFrameSelectorProps } from '../types.js';
34
+ /**
35
+ * Props for {@link DeviceFrameSelector}, extending the shared
36
+ * {@link DeviceFrameSelectorProps} with the in-dropdown action callbacks (P4-04):
37
+ * the device dropdown now also hosts the Rotate and Open-in-new-tab controls that
38
+ * used to be separate toolbar buttons.
39
+ */
40
+ export interface DeviceFrameSelectorWithActionsProps extends DeviceFrameSelectorProps {
41
+ /** Whether the current frame can be rotated — gates the Rotate item ("when/where relevant"). */
42
+ canRotate?: boolean;
43
+ /** Whether the preview is currently rotated to landscape — renders Rotate as a CHECKED toggle. */
44
+ rotated?: boolean;
45
+ /** Rotate the current fixed-frame device portrait ⇄ landscape. Omit to hide the Rotate item. */
46
+ onRotate?: () => void;
47
+ /** Open the preview URL in a new browser tab. Omit to hide the Open-in-new-tab item. */
48
+ onOpenExternal?: () => void;
49
+ }
50
+ /**
51
+ * A dropdown that selects the preview device frame and hosts the Rotate +
52
+ * Open-in-new-tab actions.
53
+ * @param props - Component props (see {@link DeviceFrameSelectorWithActionsProps}).
54
+ * @returns The rendered device-frame selector element.
55
+ */
56
+ export declare function DeviceFrameSelector({ current, onChange, className, canRotate, rotated, onRotate, onOpenExternal, }: DeviceFrameSelectorWithActionsProps): JSX.Element;
57
+ export declare namespace DeviceFrameSelector {
58
+ var displayName: string;
59
+ }
60
+ //# sourceMappingURL=DeviceFrameSelector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DeviceFrameSelector.d.ts","sourceRoot":"","sources":["../../src/components/DeviceFrameSelector.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAAE,KAAK,GAAG,EAAgE,MAAM,OAAO,CAAA;AAO9F,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAA;AAI3D;;;;;GAKG;AACH,MAAM,WAAW,mCAAoC,SAAQ,wBAAwB;IACnF,gGAAgG;IAChG,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,kGAAkG;IAClG,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,gGAAgG;IAChG,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,wFAAwF;IACxF,cAAc,CAAC,EAAE,MAAM,IAAI,CAAA;CAC5B;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,OAAO,EACP,QAAQ,EACR,SAAS,EACT,SAAS,EACT,OAAO,EACP,QAAQ,EACR,cAAc,GACf,EAAE,mCAAmC,GAAG,GAAG,CAAC,OAAO,CA6SnD;yBArTe,mBAAmB"}
@@ -0,0 +1,230 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Device-frame selector for the preview panel — a themed dropdown menu.
4
+ *
5
+ * The trigger shows the CURRENT frame's icon plus a chevron-down affordance.
6
+ * Opening it lists every device frame (Responsive / Desktop / Tablet / Mobile)
7
+ * with its bonded icon + i18n label; choosing one calls `onChange`. This
8
+ * replaced the old single-button "cycle on click" affordance — selecting a
9
+ * frame directly is far less error-prone than blind-cycling through four.
10
+ *
11
+ * Below the frames (after a divider) the menu also hosts the preview's "Rotate"
12
+ * and "Open in new tab" actions (P4-04) that used to be separate toolbar
13
+ * buttons. "Rotate" is a TOGGLE — a `menuitemcheckbox` that shows a check when the
14
+ * preview is rotated to landscape — shown only "where relevant", i.e. when the
15
+ * current frame is rotatable (fixed-frame tablet/mobile) and an `onRotate` handler
16
+ * is wired;
17
+ * "Open in new tab" is shown whenever an `onOpenExternal` handler is wired.
18
+ * Both share the frame rows' icon + label styling and the same roving focus.
19
+ *
20
+ * The menu is a minimal, accessible, fully themed popover built from ClassMap
21
+ * tokens + bonded `<Icon>`s (keyboard navigable, outside-click + Escape close,
22
+ * `role="menu"` / `role="menuitemradio"` with roving focus). It deliberately
23
+ * does NOT use the framework `Dropdown`: that component imports
24
+ * `react-router-dom` via `useLocation`, and this feature package is kept free
25
+ * of that dependency (the same reason {@link Tooltip} is subpath-imported here,
26
+ * never the package barrel).
27
+ *
28
+ * The trigger carries `cm.touchTarget`, so its compact `xs` desktop size grows
29
+ * to a WCAG-compliant >=44×44px hit-area on touch (coarse-pointer) devices.
30
+ *
31
+ * @module
32
+ */
33
+ import { useCallback, useEffect, useRef, useState } from 'react';
34
+ import { t } from '@molecule/app-i18n';
35
+ import { getClassMap } from '@molecule/app-ui';
36
+ import { Tooltip } from '@molecule/app-ui-react/components/Tooltip.js';
37
+ import { DEVICE_FRAMES, DEVICE_META } from './device-cycle.js';
38
+ import { Icon } from './Icon.js';
39
+ /**
40
+ * A dropdown that selects the preview device frame and hosts the Rotate +
41
+ * Open-in-new-tab actions.
42
+ * @param props - Component props (see {@link DeviceFrameSelectorWithActionsProps}).
43
+ * @returns The rendered device-frame selector element.
44
+ */
45
+ export function DeviceFrameSelector({ current, onChange, className, canRotate, rotated, onRotate, onOpenExternal, }) {
46
+ const cm = getClassMap();
47
+ // Action rows shown below the device frames (P4-04), after a divider: Rotate
48
+ // (only "where relevant" — when the current frame is rotatable AND a handler
49
+ // is wired) and Open in new tab (whenever a handler is wired). Built as data so
50
+ // they share the frame rows' roving-focus model + menu-item styling.
51
+ const actionItems = [
52
+ ...(canRotate && onRotate
53
+ ? [
54
+ {
55
+ id: 'preview-device-rotate',
56
+ // `as const` keeps the literal type through the conditional spread
57
+ // (the spread otherwise widens it to string, off the IconName union).
58
+ icon: 'rotate',
59
+ label: t('ide.device.rotate', {}, { defaultValue: 'Rotate' }),
60
+ run: onRotate,
61
+ // Rotate is a TOGGLE (portrait ⇄ landscape) — show its on/off state with a
62
+ // check, so it reads as a toggle rather than a one-shot action.
63
+ checked: !!rotated,
64
+ },
65
+ ]
66
+ : []),
67
+ ...(onOpenExternal
68
+ ? [
69
+ {
70
+ id: 'preview-open-external',
71
+ icon: 'link-external',
72
+ label: t('ide.preview.openNewTab', {}, { defaultValue: 'Open in new tab' }),
73
+ run: onOpenExternal,
74
+ },
75
+ ]
76
+ : []),
77
+ ];
78
+ // Total roving-focus targets = device frames + the conditional action rows.
79
+ const itemCount = DEVICE_FRAMES.length + actionItems.length;
80
+ const [open, setOpen] = useState(false);
81
+ // Index of the menu item with roving focus while the menu is open.
82
+ const [activeIndex, setActiveIndex] = useState(0);
83
+ const rootRef = useRef(null);
84
+ const triggerRef = useRef(null);
85
+ const itemRefs = useRef([]);
86
+ const currentIndex = Math.max(0, DEVICE_FRAMES.indexOf(current));
87
+ const selectLabel = t('ide.device.select', {}, { defaultValue: 'Device frame' });
88
+ const closeMenu = useCallback((refocusTrigger = true) => {
89
+ setOpen(false);
90
+ if (refocusTrigger)
91
+ triggerRef.current?.focus();
92
+ }, []);
93
+ const openMenu = useCallback((index) => {
94
+ setActiveIndex(index);
95
+ setOpen(true);
96
+ }, []);
97
+ // Close on outside pointer-down.
98
+ useEffect(() => {
99
+ if (!open)
100
+ return undefined;
101
+ const onPointerDown = (event) => {
102
+ if (rootRef.current && !rootRef.current.contains(event.target)) {
103
+ setOpen(false);
104
+ }
105
+ };
106
+ document.addEventListener('mousedown', onPointerDown);
107
+ return () => document.removeEventListener('mousedown', onPointerDown);
108
+ }, [open]);
109
+ // Move real DOM focus to the active item whenever the menu opens / moves.
110
+ useEffect(() => {
111
+ if (open)
112
+ itemRefs.current[activeIndex]?.focus();
113
+ }, [open, activeIndex]);
114
+ const select = useCallback((frame) => {
115
+ onChange(frame);
116
+ closeMenu();
117
+ }, [onChange, closeMenu]);
118
+ // Run an action row (Rotate / Open in new tab) then close the menu.
119
+ const runAction = useCallback((run) => {
120
+ run();
121
+ closeMenu();
122
+ }, [closeMenu]);
123
+ const onTriggerKeyDown = useCallback((event) => {
124
+ if (event.key === 'ArrowDown' || event.key === 'Enter' || event.key === ' ') {
125
+ event.preventDefault();
126
+ openMenu(currentIndex);
127
+ }
128
+ else if (event.key === 'ArrowUp') {
129
+ event.preventDefault();
130
+ openMenu(itemCount - 1);
131
+ }
132
+ }, [openMenu, currentIndex, itemCount]);
133
+ const onMenuKeyDown = useCallback((event) => {
134
+ switch (event.key) {
135
+ case 'ArrowDown':
136
+ event.preventDefault();
137
+ setActiveIndex((index) => (index + 1) % itemCount);
138
+ break;
139
+ case 'ArrowUp':
140
+ event.preventDefault();
141
+ setActiveIndex((index) => (index - 1 + itemCount) % itemCount);
142
+ break;
143
+ case 'Home':
144
+ event.preventDefault();
145
+ setActiveIndex(0);
146
+ break;
147
+ case 'End':
148
+ event.preventDefault();
149
+ setActiveIndex(itemCount - 1);
150
+ break;
151
+ case 'Escape':
152
+ event.preventDefault();
153
+ closeMenu();
154
+ break;
155
+ case 'Tab':
156
+ // Let focus leave naturally, but collapse the menu.
157
+ setOpen(false);
158
+ break;
159
+ default:
160
+ break;
161
+ }
162
+ }, [closeMenu, itemCount]);
163
+ const currentLabel = t(DEVICE_META[current].labelKey, {}, { defaultValue: DEVICE_META[current].label });
164
+ return (_jsxs("div", { ref: rootRef, style: { position: 'relative', display: 'inline-flex' }, children: [_jsx(Tooltip, { content: selectLabel, placement: "bottom", children: _jsxs("button", { ref: triggerRef, type: "button", "data-mol-id": "preview-device-cycle", "aria-haspopup": "menu", "aria-expanded": open, "aria-label": `${selectLabel}: ${currentLabel}`, onClick: () => (open ? closeMenu(false) : openMenu(currentIndex)), onKeyDown: onTriggerKeyDown, className: cm.cn(cm.button({ variant: 'ghost', size: 'xs' }), cm.touchTarget, className), children: [_jsx(Icon, { name: DEVICE_META[current].icon, size: 16, "aria-hidden": "true" }), _jsx(Icon, { name: "chevron-down", size: 12, "aria-hidden": "true", style: { marginLeft: 1 } })] }) }), open && (_jsxs("div", { role: "menu", "aria-label": selectLabel, onKeyDown: onMenuKeyDown, className: cm.cn(cm.surface, cm.borderAll, cm.shadowLifted, cm.textSize('xs')), style: {
165
+ position: 'absolute',
166
+ top: '100%',
167
+ right: 0,
168
+ marginTop: 4,
169
+ minWidth: 168,
170
+ padding: 4,
171
+ borderRadius: 8,
172
+ display: 'flex',
173
+ flexDirection: 'column',
174
+ gap: 2,
175
+ zIndex: 30,
176
+ }, children: [DEVICE_FRAMES.map((frame, index) => {
177
+ const label = t(DEVICE_META[frame].labelKey, {}, { defaultValue: DEVICE_META[frame].label });
178
+ const isCurrent = frame === current;
179
+ const isActive = index === activeIndex;
180
+ return (_jsxs("button", { ref: (node) => {
181
+ itemRefs.current[index] = node;
182
+ }, type: "button", role: "menuitemradio", "aria-checked": isCurrent, tabIndex: isActive ? 0 : -1, "data-mol-id": `preview-device-option-${frame}`, onClick: () => select(frame), onMouseEnter: () => setActiveIndex(index), style: {
183
+ display: 'flex',
184
+ alignItems: 'center',
185
+ gap: 8,
186
+ width: '100%',
187
+ padding: '6px 8px',
188
+ textAlign: 'left',
189
+ border: 'none',
190
+ borderRadius: 6,
191
+ color: 'inherit',
192
+ cursor: 'pointer',
193
+ font: 'inherit',
194
+ // Highlight (hover + keyboard) via a theme token, never a hex —
195
+ // kept inline so it doesn't fight a ClassMap background class.
196
+ background: isActive
197
+ ? 'var(--mol-color-surface-secondary, transparent)'
198
+ : 'transparent',
199
+ }, children: [_jsx(Icon, { name: DEVICE_META[frame].icon, size: 16, "aria-hidden": "true" }), _jsx("span", { style: { flex: 1 }, children: label }), isCurrent && _jsx(Icon, { name: "check", size: 14, "aria-hidden": "true" })] }, frame));
200
+ }), actionItems.length > 0 && (_jsx("div", { role: "separator", "aria-hidden": "true", "data-mol-id": "preview-device-menu-separator", style: {
201
+ height: 1,
202
+ margin: '4px 0',
203
+ background: 'var(--mol-color-border, rgba(128,128,128,0.2))',
204
+ } })), actionItems.map((item, actionIdx) => {
205
+ const index = DEVICE_FRAMES.length + actionIdx;
206
+ const isActive = index === activeIndex;
207
+ return (_jsxs("button", { ref: (node) => {
208
+ itemRefs.current[index] = node;
209
+ }, type: "button", role: item.checked !== undefined ? 'menuitemcheckbox' : 'menuitem', "aria-checked": item.checked, tabIndex: isActive ? 0 : -1, "data-mol-id": item.id, onClick: () => runAction(item.run), onMouseEnter: () => setActiveIndex(index), style: {
210
+ display: 'flex',
211
+ alignItems: 'center',
212
+ gap: 8,
213
+ width: '100%',
214
+ padding: '6px 8px',
215
+ textAlign: 'left',
216
+ border: 'none',
217
+ borderRadius: 6,
218
+ color: 'inherit',
219
+ cursor: 'pointer',
220
+ font: 'inherit',
221
+ // Highlight (hover + keyboard) via a theme token, never a hex —
222
+ // matching the frame rows above.
223
+ background: isActive
224
+ ? 'var(--mol-color-surface-secondary, transparent)'
225
+ : 'transparent',
226
+ }, children: [_jsx(Icon, { name: item.icon, size: 16, "aria-hidden": "true" }), _jsx("span", { style: { flex: 1 }, children: item.label }), item.checked && _jsx(Icon, { name: "check", size: 14, "aria-hidden": "true" })] }, item.id));
227
+ })] }))] }));
228
+ }
229
+ DeviceFrameSelector.displayName = 'DeviceFrameSelector';
230
+ //# sourceMappingURL=DeviceFrameSelector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DeviceFrameSelector.js","sourceRoot":"","sources":["../../src/components/DeviceFrameSelector.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Code editor panel with tab bar and Monaco integration.
3
+ *
4
+ * @module
5
+ */
6
+ import type { JSX } from 'react';
7
+ import type { EditorPanelProps } from '../types.js';
8
+ /**
9
+ * Code editor panel with tab bar and Monaco integration.
10
+ * @param props - Component props.
11
+ * @returns The rendered editor panel element.
12
+ */
13
+ export declare function EditorPanel({ className, onActiveFileChange, onEditorReady, onTabsChange, fileStatuses, formattingFile, countdownFile, countdownKey, formatEstimate, onFixWithAI, onTabDoubleClick, }: EditorPanelProps): JSX.Element;
14
+ export declare namespace EditorPanel {
15
+ var displayName: string;
16
+ }
17
+ //# sourceMappingURL=EditorPanel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EditorPanel.d.ts","sourceRoot":"","sources":["../../src/components/EditorPanel.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,OAAO,CAAA;AAShC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAsDnD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EAC1B,SAAS,EACT,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,aAAa,EACb,YAAY,EACZ,cAAqB,EACrB,WAAW,EACX,gBAAgB,GACjB,EAAE,gBAAgB,GAAG,GAAG,CAAC,OAAO,CAiPhC;yBA7Pe,WAAW"}
@@ -0,0 +1,241 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useRef, useState } from 'react';
3
+ import { useCallback } from 'react';
4
+ import { useEditor, useEditorProvider, useThemeMode, useTranslation } from '@molecule/app-react';
5
+ import { getClassMap } from '@molecule/app-ui';
6
+ import { useNarrowViewport } from '../hooks/useViewport.js';
7
+ import { TabBar } from './TabBar.js';
8
+ /**
9
+ * Editor option overrides applied while the viewport is phone-width: no minimap
10
+ * (it eats a third of a 390px pane), readable 14px type, wrap instead of
11
+ * horizontal scrolling (touch pans the page, not the editor), no scroll past the
12
+ * last line (vertical space is scarce), and no folding controls (a narrower
13
+ * gutter — folding is a pointer-precision affordance anyway).
14
+ */
15
+ const MOBILE_EDITOR_CONFIG = {
16
+ fontSize: 14,
17
+ wordWrap: true,
18
+ minimap: false,
19
+ scrollBeyondLastLine: false,
20
+ folding: false,
21
+ };
22
+ /**
23
+ * Restore values for the {@link MOBILE_EDITOR_CONFIG} keys when the viewport
24
+ * widens and the provider cannot report its own config (`getConfig` is
25
+ * optional). Matches the stock provider defaults, so a host that didn't
26
+ * customize the editor gets its exact desktop behavior back.
27
+ */
28
+ const DESKTOP_EDITOR_CONFIG_FALLBACK = {
29
+ fontSize: 12,
30
+ wordWrap: false,
31
+ minimap: true,
32
+ scrollBeyondLastLine: true,
33
+ folding: true,
34
+ };
35
+ /** Inject the countdown keyframes once. */
36
+ let styleInjected = false;
37
+ /** Injects CSS keyframes for the format countdown and toast animations. */
38
+ function injectCountdownStyle() {
39
+ if (styleInjected)
40
+ return;
41
+ styleInjected = true;
42
+ const style = document.createElement('style');
43
+ style.textContent = `
44
+ @keyframes mol-format-countdown {
45
+ from { width: 0%; }
46
+ to { width: 100%; }
47
+ }
48
+ @keyframes mol-format-toast-grow {
49
+ from { width: 0%; }
50
+ to { width: 100%; }
51
+ }
52
+ `;
53
+ document.head.appendChild(style);
54
+ }
55
+ /**
56
+ * Code editor panel with tab bar and Monaco integration.
57
+ * @param props - Component props.
58
+ * @returns The rendered editor panel element.
59
+ */
60
+ export function EditorPanel({ className, onActiveFileChange, onEditorReady, onTabsChange, fileStatuses, formattingFile, countdownFile, countdownKey, formatEstimate = 2000, onFixWithAI, onTabDoubleClick, }) {
61
+ const cm = getClassMap();
62
+ const { t } = useTranslation();
63
+ const editorProvider = useEditorProvider();
64
+ const themeMode = useThemeMode();
65
+ const { tabs, activeFile, closeFile, setActiveTab, pinTab, mount, dispose } = useEditor();
66
+ const containerRef = useRef(null);
67
+ const mountedRef = useRef(false);
68
+ // Use a ref so onEditorReady never needs to be in the mount effect's dep array
69
+ const onEditorReadyRef = useRef(onEditorReady);
70
+ onEditorReadyRef.current = onEditorReady;
71
+ // Inject keyframes on first render
72
+ useEffect(() => {
73
+ injectCountdownStyle();
74
+ }, []);
75
+ // Sync Monaco editor theme with app theme mode — runs before mount too,
76
+ // so the stored config has the correct theme when the editor is created.
77
+ useEffect(() => {
78
+ editorProvider.updateConfig({ theme: themeMode === 'light' ? 'vs' : 'vs-dark' });
79
+ }, [themeMode, editorProvider]);
80
+ // Mobile-tuned editor options on phone-width viewports. Like the theme sync
81
+ // above, updateConfig persists into the provider's stored config, so this
82
+ // works whether it fires before or after mount (no remount, no layout in a
83
+ // zero-size container). A pure desktop session never enters either branch, so
84
+ // desktop behavior is untouched; on narrow→wide the captured baseline (the
85
+ // provider's own config when it can report it) is restored.
86
+ const isNarrow = useNarrowViewport();
87
+ const mobileBaselineRef = useRef(null);
88
+ useEffect(() => {
89
+ if (isNarrow) {
90
+ if (!mobileBaselineRef.current) {
91
+ const current = editorProvider.getConfig?.();
92
+ mobileBaselineRef.current = {
93
+ fontSize: current?.fontSize ?? DESKTOP_EDITOR_CONFIG_FALLBACK.fontSize,
94
+ wordWrap: current?.wordWrap ?? DESKTOP_EDITOR_CONFIG_FALLBACK.wordWrap,
95
+ minimap: current?.minimap ?? DESKTOP_EDITOR_CONFIG_FALLBACK.minimap,
96
+ scrollBeyondLastLine: current?.scrollBeyondLastLine ?? DESKTOP_EDITOR_CONFIG_FALLBACK.scrollBeyondLastLine,
97
+ folding: current?.folding ?? DESKTOP_EDITOR_CONFIG_FALLBACK.folding,
98
+ };
99
+ }
100
+ editorProvider.updateConfig(MOBILE_EDITOR_CONFIG);
101
+ }
102
+ else if (mobileBaselineRef.current) {
103
+ editorProvider.updateConfig(mobileBaselineRef.current);
104
+ mobileBaselineRef.current = null;
105
+ }
106
+ }, [isNarrow, editorProvider]);
107
+ // Formatting toast: visible tracks whether the toast is in the DOM,
108
+ // animatingIn controls the CSS transition state (false = slide down / fade out).
109
+ const isFormatting = !!(formattingFile && formattingFile === activeFile);
110
+ const [visible, setVisible] = useState(false);
111
+ const [animatingIn, setAnimatingIn] = useState(false);
112
+ const hideTimerRef = useRef(null);
113
+ useEffect(() => {
114
+ if (isFormatting) {
115
+ // Show toast
116
+ if (hideTimerRef.current) {
117
+ clearTimeout(hideTimerRef.current);
118
+ hideTimerRef.current = null;
119
+ }
120
+ setVisible(true);
121
+ // Trigger enter animation on next frame so the transition fires
122
+ requestAnimationFrame(() => {
123
+ requestAnimationFrame(() => {
124
+ setAnimatingIn(true);
125
+ });
126
+ });
127
+ }
128
+ else if (visible) {
129
+ // Start exit animation, then remove from DOM
130
+ setAnimatingIn(false);
131
+ hideTimerRef.current = setTimeout(() => {
132
+ setVisible(false);
133
+ hideTimerRef.current = null;
134
+ }, 300);
135
+ }
136
+ }, [isFormatting]);
137
+ // Countdown progress bar visible for the active file
138
+ const showCountdown = !!(countdownFile && countdownFile === activeFile);
139
+ // Wire Fix with AI callback — subscribe to the editor provider's onFixWithAI
140
+ const onFixWithAIRef = useRef(onFixWithAI);
141
+ onFixWithAIRef.current = onFixWithAI;
142
+ useEffect(() => {
143
+ if (!editorProvider.onFixWithAI)
144
+ return;
145
+ return editorProvider.onFixWithAI((request) => {
146
+ onFixWithAIRef.current?.(request);
147
+ });
148
+ }, [editorProvider]);
149
+ // Notify parent whenever the active file changes (tab switch, open, close)
150
+ useEffect(() => {
151
+ onActiveFileChange?.(activeFile);
152
+ }, [activeFile, onActiveFileChange]);
153
+ // Notify parent whenever the tab list changes (file opened or closed)
154
+ useEffect(() => {
155
+ onTabsChange?.(tabs.map((t) => t.path));
156
+ }, [tabs, onTabsChange]);
157
+ const handleTabSelect = useCallback((path) => {
158
+ setActiveTab(path);
159
+ onActiveFileChange?.(path);
160
+ }, [setActiveTab, onActiveFileChange]);
161
+ // Mount the editor when the container is ready
162
+ useEffect(() => {
163
+ const container = containerRef.current;
164
+ if (!container || mountedRef.current)
165
+ return;
166
+ mountedRef.current = true;
167
+ const result = mount(container);
168
+ // Handle both sync and async mount
169
+ if (result && typeof result.then === 'function') {
170
+ ;
171
+ result
172
+ .then(() => {
173
+ onEditorReadyRef.current?.();
174
+ })
175
+ .catch(() => {
176
+ mountedRef.current = false;
177
+ });
178
+ }
179
+ else {
180
+ onEditorReadyRef.current?.();
181
+ }
182
+ return () => {
183
+ mountedRef.current = false;
184
+ dispose();
185
+ };
186
+ }, [mount, dispose]);
187
+ return (_jsxs("div", { className: cm.cn(cm.flex({ direction: 'col' }), cm.h('full'), cm.surface, cm.borderR, className), children: [_jsx(TabBar, { tabs: tabs, activeFile: activeFile, onSelect: handleTabSelect, onClose: closeFile, onDoubleClick: (path) => {
188
+ if (onTabDoubleClick?.(path))
189
+ return;
190
+ pinTab(path);
191
+ }, fileStatuses: fileStatuses }), _jsxs("div", { style: {
192
+ flex: 1,
193
+ minHeight: 0,
194
+ position: 'relative',
195
+ overflow: 'hidden',
196
+ display: 'flex',
197
+ flexDirection: 'column',
198
+ }, children: [_jsx("div", { ref: containerRef, style: {
199
+ width: '100%',
200
+ height: '100%',
201
+ overflow: 'hidden',
202
+ visibility: tabs.length === 0 ? 'hidden' : 'visible',
203
+ } }), showCountdown && (_jsx("div", { style: {
204
+ position: 'absolute',
205
+ bottom: 0,
206
+ left: 0,
207
+ height: 2,
208
+ backgroundColor: 'var(--mol-color-primary, #4070e0)',
209
+ zIndex: 1000,
210
+ pointerEvents: 'none',
211
+ animation: 'mol-format-countdown 700ms linear forwards',
212
+ } }, countdownKey)), visible && (_jsxs("div", { style: {
213
+ position: 'absolute',
214
+ bottom: 12,
215
+ right: 16,
216
+ padding: '6px 12px',
217
+ fontSize: 13,
218
+ lineHeight: '22px',
219
+ backgroundColor: 'var(--color-surface, #151515)',
220
+ color: 'inherit',
221
+ border: '1px solid rgba(128, 128, 128, 0.15)',
222
+ borderRadius: 6,
223
+ overflow: 'hidden',
224
+ backdropFilter: 'blur(8px)',
225
+ boxShadow: '0 2px 8px rgba(0, 0, 0, 0.3)',
226
+ zIndex: 1000,
227
+ pointerEvents: 'none',
228
+ transform: animatingIn ? 'translateY(0)' : 'translateY(8px)',
229
+ opacity: animatingIn ? 1 : 0,
230
+ transition: 'transform 200ms ease-out, opacity 200ms ease-out',
231
+ }, children: [t('ide.formatting', undefined, { defaultValue: 'Formatting…' }), _jsx("div", { style: {
232
+ position: 'absolute',
233
+ bottom: 0,
234
+ left: 0,
235
+ height: 2,
236
+ backgroundColor: 'var(--mol-color-primary, #4070e0)',
237
+ animation: `mol-format-toast-grow ${formatEstimate}ms linear forwards`,
238
+ } })] }))] })] }));
239
+ }
240
+ EditorPanel.displayName = 'EditorPanel';
241
+ //# sourceMappingURL=EditorPanel.js.map