@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.
- package/LICENSE +115 -0
- package/dist/chatTimelineOrdering.d.ts +50 -0
- package/dist/chatTimelineOrdering.d.ts.map +1 -0
- package/dist/chatTimelineOrdering.js +46 -0
- package/dist/chatTimelineOrdering.js.map +1 -0
- package/dist/command-metadata.d.ts +78 -0
- package/dist/command-metadata.d.ts.map +1 -0
- package/dist/command-metadata.js +214 -0
- package/dist/command-metadata.js.map +1 -0
- package/dist/components/ActivityCard.d.ts +28 -0
- package/dist/components/ActivityCard.d.ts.map +1 -0
- package/dist/components/ActivityCard.js +62 -0
- package/dist/components/ActivityCard.js.map +1 -0
- package/dist/components/AutoCommitBadge.d.ts +58 -0
- package/dist/components/AutoCommitBadge.d.ts.map +1 -0
- package/dist/components/AutoCommitBadge.js +78 -0
- package/dist/components/AutoCommitBadge.js.map +1 -0
- package/dist/components/ChatPanel.d.ts +184 -0
- package/dist/components/ChatPanel.d.ts.map +1 -0
- package/dist/components/ChatPanel.js +6691 -0
- package/dist/components/ChatPanel.js.map +1 -0
- package/dist/components/CommandPalette.d.ts +18 -0
- package/dist/components/CommandPalette.d.ts.map +1 -0
- package/dist/components/CommandPalette.js +33 -0
- package/dist/components/CommandPalette.js.map +1 -0
- package/dist/components/DeviceFrameSelector.d.ts +60 -0
- package/dist/components/DeviceFrameSelector.d.ts.map +1 -0
- package/dist/components/DeviceFrameSelector.js +230 -0
- package/dist/components/DeviceFrameSelector.js.map +1 -0
- package/dist/components/EditorPanel.d.ts +17 -0
- package/dist/components/EditorPanel.d.ts.map +1 -0
- package/dist/components/EditorPanel.js +241 -0
- package/dist/components/EditorPanel.js.map +1 -0
- package/dist/components/FileExplorer.d.ts +64 -0
- package/dist/components/FileExplorer.d.ts.map +1 -0
- package/dist/components/FileExplorer.js +1111 -0
- package/dist/components/FileExplorer.js.map +1 -0
- package/dist/components/FileExplorerContextMenu.d.ts +37 -0
- package/dist/components/FileExplorerContextMenu.d.ts.map +1 -0
- package/dist/components/FileExplorerContextMenu.js +241 -0
- package/dist/components/FileExplorerContextMenu.js.map +1 -0
- package/dist/components/HelpCard.d.ts +34 -0
- package/dist/components/HelpCard.d.ts.map +1 -0
- package/dist/components/HelpCard.js +28 -0
- package/dist/components/HelpCard.js.map +1 -0
- package/dist/components/Icon.d.ts +50 -0
- package/dist/components/Icon.d.ts.map +1 -0
- package/dist/components/Icon.js +25 -0
- package/dist/components/Icon.js.map +1 -0
- package/dist/components/KeyboardShortcutsPanel.d.ts +19 -0
- package/dist/components/KeyboardShortcutsPanel.d.ts.map +1 -0
- package/dist/components/KeyboardShortcutsPanel.js +187 -0
- package/dist/components/KeyboardShortcutsPanel.js.map +1 -0
- package/dist/components/MarkdownContent.d.ts +52 -0
- package/dist/components/MarkdownContent.d.ts.map +1 -0
- package/dist/components/MarkdownContent.js +303 -0
- package/dist/components/MarkdownContent.js.map +1 -0
- package/dist/components/ModelsTable.d.ts +38 -0
- package/dist/components/ModelsTable.d.ts.map +1 -0
- package/dist/components/ModelsTable.js +96 -0
- package/dist/components/ModelsTable.js.map +1 -0
- package/dist/components/PreviewPanel.d.ts +66 -0
- package/dist/components/PreviewPanel.d.ts.map +1 -0
- package/dist/components/PreviewPanel.js +1752 -0
- package/dist/components/PreviewPanel.js.map +1 -0
- package/dist/components/QuickOpen.d.ts +20 -0
- package/dist/components/QuickOpen.d.ts.map +1 -0
- package/dist/components/QuickOpen.js +72 -0
- package/dist/components/QuickOpen.js.map +1 -0
- package/dist/components/QuickPicker.d.ts +21 -0
- package/dist/components/QuickPicker.d.ts.map +1 -0
- package/dist/components/QuickPicker.js +174 -0
- package/dist/components/QuickPicker.js.map +1 -0
- package/dist/components/RelevantSkillSuggestion.d.ts +41 -0
- package/dist/components/RelevantSkillSuggestion.d.ts.map +1 -0
- package/dist/components/RelevantSkillSuggestion.js +85 -0
- package/dist/components/RelevantSkillSuggestion.js.map +1 -0
- package/dist/components/ReportModal.d.ts +35 -0
- package/dist/components/ReportModal.d.ts.map +1 -0
- package/dist/components/ReportModal.js +112 -0
- package/dist/components/ReportModal.js.map +1 -0
- package/dist/components/ResizeHandle.d.ts +21 -0
- package/dist/components/ResizeHandle.d.ts.map +1 -0
- package/dist/components/ResizeHandle.js +109 -0
- package/dist/components/ResizeHandle.js.map +1 -0
- package/dist/components/ScriptsCard.d.ts +37 -0
- package/dist/components/ScriptsCard.d.ts.map +1 -0
- package/dist/components/ScriptsCard.js +177 -0
- package/dist/components/ScriptsCard.js.map +1 -0
- package/dist/components/SearchPanel.d.ts +18 -0
- package/dist/components/SearchPanel.d.ts.map +1 -0
- package/dist/components/SearchPanel.js +449 -0
- package/dist/components/SearchPanel.js.map +1 -0
- package/dist/components/SettingsCard.d.ts +49 -0
- package/dist/components/SettingsCard.d.ts.map +1 -0
- package/dist/components/SettingsCard.js +59 -0
- package/dist/components/SettingsCard.js.map +1 -0
- package/dist/components/ShareModal.d.ts +32 -0
- package/dist/components/ShareModal.d.ts.map +1 -0
- package/dist/components/ShareModal.js +126 -0
- package/dist/components/ShareModal.js.map +1 -0
- package/dist/components/SidebarTabs.d.ts +18 -0
- package/dist/components/SidebarTabs.d.ts.map +1 -0
- package/dist/components/SidebarTabs.js +20 -0
- package/dist/components/SidebarTabs.js.map +1 -0
- package/dist/components/SkillsCard.d.ts +86 -0
- package/dist/components/SkillsCard.d.ts.map +1 -0
- package/dist/components/SkillsCard.js +227 -0
- package/dist/components/SkillsCard.js.map +1 -0
- package/dist/components/StreamingIndicator.d.ts +50 -0
- package/dist/components/StreamingIndicator.d.ts.map +1 -0
- package/dist/components/StreamingIndicator.js +142 -0
- package/dist/components/StreamingIndicator.js.map +1 -0
- package/dist/components/TabBar.d.ts +37 -0
- package/dist/components/TabBar.d.ts.map +1 -0
- package/dist/components/TabBar.js +187 -0
- package/dist/components/TabBar.js.map +1 -0
- package/dist/components/TipCard.d.ts +22 -0
- package/dist/components/TipCard.d.ts.map +1 -0
- package/dist/components/TipCard.js +73 -0
- package/dist/components/TipCard.js.map +1 -0
- package/dist/components/ToolCallCard.d.ts +18 -0
- package/dist/components/ToolCallCard.d.ts.map +1 -0
- package/dist/components/ToolCallCard.js +716 -0
- package/dist/components/ToolCallCard.js.map +1 -0
- package/dist/components/UserAvatar.d.ts +45 -0
- package/dist/components/UserAvatar.d.ts.map +1 -0
- package/dist/components/UserAvatar.js +80 -0
- package/dist/components/UserAvatar.js.map +1 -0
- package/dist/components/WorkspaceLayout.d.ts +19 -0
- package/dist/components/WorkspaceLayout.d.ts.map +1 -0
- package/dist/components/WorkspaceLayout.js +56 -0
- package/dist/components/WorkspaceLayout.js.map +1 -0
- package/dist/components/activity-utilities.d.ts +107 -0
- package/dist/components/activity-utilities.d.ts.map +1 -0
- package/dist/components/activity-utilities.js +154 -0
- package/dist/components/activity-utilities.js.map +1 -0
- package/dist/components/chat-autocommit-utilities.d.ts +172 -0
- package/dist/components/chat-autocommit-utilities.d.ts.map +1 -0
- package/dist/components/chat-autocommit-utilities.js +185 -0
- package/dist/components/chat-autocommit-utilities.js.map +1 -0
- package/dist/components/chat-card-style.d.ts +64 -0
- package/dist/components/chat-card-style.d.ts.map +1 -0
- package/dist/components/chat-card-style.js +74 -0
- package/dist/components/chat-card-style.js.map +1 -0
- package/dist/components/chat-commands.d.ts +17 -0
- package/dist/components/chat-commands.d.ts.map +1 -0
- package/dist/components/chat-commands.js +17 -0
- package/dist/components/chat-commands.js.map +1 -0
- package/dist/components/chat-commit-utilities.d.ts +29 -0
- package/dist/components/chat-commit-utilities.d.ts.map +1 -0
- package/dist/components/chat-commit-utilities.js +31 -0
- package/dist/components/chat-commit-utilities.js.map +1 -0
- package/dist/components/chat-cost-utilities.d.ts +62 -0
- package/dist/components/chat-cost-utilities.d.ts.map +1 -0
- package/dist/components/chat-cost-utilities.js +66 -0
- package/dist/components/chat-cost-utilities.js.map +1 -0
- package/dist/components/chat-effort-utilities.d.ts +96 -0
- package/dist/components/chat-effort-utilities.d.ts.map +1 -0
- package/dist/components/chat-effort-utilities.js +97 -0
- package/dist/components/chat-effort-utilities.js.map +1 -0
- package/dist/components/chat-help-utilities.d.ts +89 -0
- package/dist/components/chat-help-utilities.d.ts.map +1 -0
- package/dist/components/chat-help-utilities.js +131 -0
- package/dist/components/chat-help-utilities.js.map +1 -0
- package/dist/components/chat-model-mode-utilities.d.ts +143 -0
- package/dist/components/chat-model-mode-utilities.d.ts.map +1 -0
- package/dist/components/chat-model-mode-utilities.js +191 -0
- package/dist/components/chat-model-mode-utilities.js.map +1 -0
- package/dist/components/chat-models-utilities.d.ts +98 -0
- package/dist/components/chat-models-utilities.d.ts.map +1 -0
- package/dist/components/chat-models-utilities.js +136 -0
- package/dist/components/chat-models-utilities.js.map +1 -0
- package/dist/components/chat-report-utilities.d.ts +149 -0
- package/dist/components/chat-report-utilities.d.ts.map +1 -0
- package/dist/components/chat-report-utilities.js +151 -0
- package/dist/components/chat-report-utilities.js.map +1 -0
- package/dist/components/chat-scripts-utilities.d.ts +140 -0
- package/dist/components/chat-scripts-utilities.d.ts.map +1 -0
- package/dist/components/chat-scripts-utilities.js +158 -0
- package/dist/components/chat-scripts-utilities.js.map +1 -0
- package/dist/components/chat-settings-utilities.d.ts +72 -0
- package/dist/components/chat-settings-utilities.d.ts.map +1 -0
- package/dist/components/chat-settings-utilities.js +56 -0
- package/dist/components/chat-settings-utilities.js.map +1 -0
- package/dist/components/chat-share-utilities.d.ts +104 -0
- package/dist/components/chat-share-utilities.d.ts.map +1 -0
- package/dist/components/chat-share-utilities.js +90 -0
- package/dist/components/chat-share-utilities.js.map +1 -0
- package/dist/components/chat-skills-utilities.d.ts +242 -0
- package/dist/components/chat-skills-utilities.d.ts.map +1 -0
- package/dist/components/chat-skills-utilities.js +433 -0
- package/dist/components/chat-skills-utilities.js.map +1 -0
- package/dist/components/chat-stream-utilities.d.ts +74 -0
- package/dist/components/chat-stream-utilities.d.ts.map +1 -0
- package/dist/components/chat-stream-utilities.js +103 -0
- package/dist/components/chat-stream-utilities.js.map +1 -0
- package/dist/components/chat-tips-utilities.d.ts +108 -0
- package/dist/components/chat-tips-utilities.d.ts.map +1 -0
- package/dist/components/chat-tips-utilities.js +137 -0
- package/dist/components/chat-tips-utilities.js.map +1 -0
- package/dist/components/device-cycle.d.ts +84 -0
- package/dist/components/device-cycle.d.ts.map +1 -0
- package/dist/components/device-cycle.js +77 -0
- package/dist/components/device-cycle.js.map +1 -0
- package/dist/components/index.d.ts +36 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +33 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/tool-call-utilities.d.ts +69 -0
- package/dist/components/tool-call-utilities.d.ts.map +1 -0
- package/dist/components/tool-call-utilities.js +368 -0
- package/dist/components/tool-call-utilities.js.map +1 -0
- package/dist/components/user-avatar-utilities.d.ts +41 -0
- package/dist/components/user-avatar-utilities.d.ts.map +1 -0
- package/dist/components/user-avatar-utilities.js +56 -0
- package/dist/components/user-avatar-utilities.js.map +1 -0
- package/dist/components/workspace-layout-utilities.d.ts +37 -0
- package/dist/components/workspace-layout-utilities.d.ts.map +1 -0
- package/dist/components/workspace-layout-utilities.js +58 -0
- package/dist/components/workspace-layout-utilities.js.map +1 -0
- package/dist/customEventCards.d.ts +117 -0
- package/dist/customEventCards.d.ts.map +1 -0
- package/dist/customEventCards.js +36 -0
- package/dist/customEventCards.js.map +1 -0
- package/dist/hooks/index.d.ts +8 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +8 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/useKeyboardShortcuts.d.ts +48 -0
- package/dist/hooks/useKeyboardShortcuts.d.ts.map +1 -0
- package/dist/hooks/useKeyboardShortcuts.js +112 -0
- package/dist/hooks/useKeyboardShortcuts.js.map +1 -0
- package/dist/hooks/useViewport.d.ts +42 -0
- package/dist/hooks/useViewport.d.ts.map +1 -0
- package/dist/hooks/useViewport.js +61 -0
- package/dist/hooks/useViewport.js.map +1 -0
- package/dist/index.d.ts +75 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +74 -0
- package/dist/index.js.map +1 -0
- package/dist/settings-metadata.d.ts +78 -0
- package/dist/settings-metadata.d.ts.map +1 -0
- package/dist/settings-metadata.js +131 -0
- package/dist/settings-metadata.js.map +1 -0
- package/dist/types.d.ts +751 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +25 -0
- package/dist/types.js.map +1 -0
- 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
|