dsh-plugin-core 0.2.14 → 0.3.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/README.md +52 -83
- package/dist/design/tokens-css.d.ts +1 -1
- package/dist/design/tokens-css.d.ts.map +1 -1
- package/dist/design/tokens-css.generated.d.ts +2 -0
- package/dist/design/tokens-css.generated.d.ts.map +1 -0
- package/dist/design/tokens-css.generated.js +4 -0
- package/dist/design/tokens-css.generated.js.map +1 -0
- package/dist/design/tokens-css.js +1 -252
- package/dist/design/tokens-css.js.map +1 -1
- package/dist/explorer/FileBrowser.d.ts +41 -0
- package/dist/explorer/FileBrowser.d.ts.map +1 -0
- package/dist/explorer/FileBrowser.js +659 -0
- package/dist/explorer/FileBrowser.js.map +1 -0
- package/dist/explorer/explorer-css.d.ts +1 -1
- package/dist/explorer/explorer-css.d.ts.map +1 -1
- package/dist/explorer/explorer-css.generated.d.ts +2 -0
- package/dist/explorer/explorer-css.generated.d.ts.map +1 -0
- package/dist/explorer/explorer-css.generated.js +4 -0
- package/dist/explorer/explorer-css.generated.js.map +1 -0
- package/dist/explorer/explorer-css.js +1 -31
- package/dist/explorer/explorer-css.js.map +1 -1
- package/dist/explorer/index.d.ts +4 -2
- package/dist/explorer/index.d.ts.map +1 -1
- package/dist/explorer/index.js +4 -2
- package/dist/explorer/index.js.map +1 -1
- package/dist/explorer/open.d.ts +103 -0
- package/dist/explorer/open.d.ts.map +1 -0
- package/dist/explorer/open.js +282 -0
- package/dist/explorer/open.js.map +1 -0
- package/dist/explorer/preview/BytesPreview.d.ts +13 -0
- package/dist/explorer/preview/BytesPreview.d.ts.map +1 -0
- package/dist/explorer/preview/BytesPreview.js +56 -0
- package/dist/explorer/preview/BytesPreview.js.map +1 -0
- package/dist/explorer/preview/HtmlPreview.d.ts +18 -0
- package/dist/explorer/preview/HtmlPreview.d.ts.map +1 -0
- package/dist/explorer/preview/HtmlPreview.js +112 -0
- package/dist/explorer/preview/HtmlPreview.js.map +1 -0
- package/dist/explorer/preview/OfficePreview.d.ts +11 -0
- package/dist/explorer/preview/OfficePreview.d.ts.map +1 -0
- package/dist/explorer/preview/OfficePreview.js +53 -0
- package/dist/explorer/preview/OfficePreview.js.map +1 -0
- package/dist/explorer/preview/TextPreview.d.ts +17 -0
- package/dist/explorer/preview/TextPreview.d.ts.map +1 -0
- package/dist/explorer/preview/TextPreview.js +95 -0
- package/dist/explorer/preview/TextPreview.js.map +1 -0
- package/dist/explorer/preview/index.d.ts +10 -3
- package/dist/explorer/preview/index.d.ts.map +1 -1
- package/dist/explorer/preview/index.js +52 -55
- package/dist/explorer/preview/index.js.map +1 -1
- package/dist/explorer/preview/md-labels.d.ts +3 -0
- package/dist/explorer/preview/md-labels.d.ts.map +1 -0
- package/dist/explorer/preview/md-labels.js +12 -0
- package/dist/explorer/preview/md-labels.js.map +1 -0
- package/dist/explorer/types.d.ts +186 -4
- package/dist/explorer/types.d.ts.map +1 -1
- package/dist/explorer/types.js +401 -14
- package/dist/explorer/types.js.map +1 -1
- package/dist/host/index.d.ts +66 -0
- package/dist/host/index.d.ts.map +1 -0
- package/dist/host/index.js +60 -0
- package/dist/host/index.js.map +1 -0
- package/dist/session/SessionViewModal.d.ts +42 -0
- package/dist/session/SessionViewModal.d.ts.map +1 -0
- package/dist/session/SessionViewModal.js +997 -0
- package/dist/session/SessionViewModal.js.map +1 -0
- package/dist/session/changes.d.ts +73 -0
- package/dist/session/changes.d.ts.map +1 -0
- package/dist/session/changes.js +134 -0
- package/dist/session/changes.js.map +1 -0
- package/dist/session/faces.d.ts +198 -0
- package/dist/session/faces.d.ts.map +1 -0
- package/dist/session/faces.js +17 -0
- package/dist/session/faces.js.map +1 -0
- package/dist/session/index.d.ts +5 -23
- package/dist/session/index.d.ts.map +1 -1
- package/dist/session/index.js +25 -70
- package/dist/session/index.js.map +1 -1
- package/dist/session/mirror/ChangedFileDiff.d.ts +22 -0
- package/dist/session/mirror/ChangedFileDiff.d.ts.map +1 -0
- package/dist/session/mirror/ChangedFileDiff.js +157 -0
- package/dist/session/mirror/ChangedFileDiff.js.map +1 -0
- package/dist/session/mirror/ChangedFilesCard.d.ts +18 -0
- package/dist/session/mirror/ChangedFilesCard.d.ts.map +1 -0
- package/dist/session/mirror/ChangedFilesCard.js +158 -0
- package/dist/session/mirror/ChangedFilesCard.js.map +1 -0
- package/dist/session/mirror/ChatGroupSeat.d.ts +24 -0
- package/dist/session/mirror/ChatGroupSeat.d.ts.map +1 -0
- package/dist/session/mirror/ChatGroupSeat.js +134 -0
- package/dist/session/mirror/ChatGroupSeat.js.map +1 -0
- package/dist/session/mirror/ChatNodeSeat.d.ts +43 -0
- package/dist/session/mirror/ChatNodeSeat.d.ts.map +1 -0
- package/dist/session/mirror/ChatNodeSeat.js +133 -0
- package/dist/session/mirror/ChatNodeSeat.js.map +1 -0
- package/dist/session/mirror/ChatView.d.ts +47 -0
- package/dist/session/mirror/ChatView.d.ts.map +1 -0
- package/dist/session/mirror/ChatView.js +127 -0
- package/dist/session/mirror/ChatView.js.map +1 -0
- package/dist/session/mirror/CommandCard.d.ts +20 -0
- package/dist/session/mirror/CommandCard.d.ts.map +1 -0
- package/dist/session/mirror/CommandCard.js +69 -0
- package/dist/session/mirror/CommandCard.js.map +1 -0
- package/dist/session/mirror/Deliverables.d.ts +24 -0
- package/dist/session/mirror/Deliverables.d.ts.map +1 -0
- package/dist/session/mirror/Deliverables.js +134 -0
- package/dist/session/mirror/Deliverables.js.map +1 -0
- package/dist/session/mirror/GenericCommandCard.d.ts +25 -0
- package/dist/session/mirror/GenericCommandCard.d.ts.map +1 -0
- package/dist/session/mirror/GenericCommandCard.js +567 -0
- package/dist/session/mirror/GenericCommandCard.js.map +1 -0
- package/dist/session/mirror/MessageIconActions.d.ts +16 -0
- package/dist/session/mirror/MessageIconActions.d.ts.map +1 -0
- package/dist/session/mirror/MessageIconActions.js +71 -0
- package/dist/session/mirror/MessageIconActions.js.map +1 -0
- package/dist/session/mirror/MessageItem.d.ts +66 -0
- package/dist/session/mirror/MessageItem.d.ts.map +1 -0
- package/dist/session/mirror/MessageItem.js +135 -0
- package/dist/session/mirror/MessageItem.js.map +1 -0
- package/dist/session/mirror/ReasoningRow.d.ts +11 -0
- package/dist/session/mirror/ReasoningRow.d.ts.map +1 -0
- package/dist/session/mirror/ReasoningRow.js +49 -0
- package/dist/session/mirror/ReasoningRow.js.map +1 -0
- package/dist/session/mirror/StatDialog.d.ts +20 -0
- package/dist/session/mirror/StatDialog.d.ts.map +1 -0
- package/dist/session/mirror/StatDialog.js +36 -0
- package/dist/session/mirror/StatDialog.js.map +1 -0
- package/dist/session/mirror/TurnProcessNodeView.d.ts +10 -0
- package/dist/session/mirror/TurnProcessNodeView.d.ts.map +1 -0
- package/dist/session/mirror/TurnProcessNodeView.js +68 -0
- package/dist/session/mirror/TurnProcessNodeView.js.map +1 -0
- package/dist/session/mirror/TurnTailNodeView.d.ts +32 -0
- package/dist/session/mirror/TurnTailNodeView.d.ts.map +1 -0
- package/dist/session/mirror/TurnTailNodeView.js +48 -0
- package/dist/session/mirror/TurnTailNodeView.js.map +1 -0
- package/dist/session/mirror/TurnTriggerNodeView.d.ts +14 -0
- package/dist/session/mirror/TurnTriggerNodeView.d.ts.map +1 -0
- package/dist/session/mirror/TurnTriggerNodeView.js +87 -0
- package/dist/session/mirror/TurnTriggerNodeView.js.map +1 -0
- package/dist/session/mirror/TurnUsagePanel.d.ts +21 -0
- package/dist/session/mirror/TurnUsagePanel.d.ts.map +1 -0
- package/dist/session/mirror/TurnUsagePanel.js +41 -0
- package/dist/session/mirror/TurnUsagePanel.js.map +1 -0
- package/dist/session/mirror/message-chrome.d.ts +33 -0
- package/dist/session/mirror/message-chrome.d.ts.map +1 -0
- package/dist/session/mirror/message-chrome.js +158 -0
- package/dist/session/mirror/message-chrome.js.map +1 -0
- package/dist/session/mirror/process-groups.d.ts +56 -0
- package/dist/session/mirror/process-groups.d.ts.map +1 -0
- package/dist/session/mirror/process-groups.js +202 -0
- package/dist/session/mirror/process-groups.js.map +1 -0
- package/dist/session/official-classes.d.ts +28 -0
- package/dist/session/official-classes.d.ts.map +1 -0
- package/dist/session/official-classes.js +146 -0
- package/dist/session/official-classes.js.map +1 -0
- package/dist/session/open-session-view.d.ts +17 -0
- package/dist/session/open-session-view.d.ts.map +1 -0
- package/dist/session/open-session-view.js +136 -0
- package/dist/session/open-session-view.js.map +1 -0
- package/dist/session/session-css.d.ts +1 -1
- package/dist/session/session-css.d.ts.map +1 -1
- package/dist/session/session-css.generated.d.ts +2 -0
- package/dist/session/session-css.generated.d.ts.map +1 -0
- package/dist/session/session-css.generated.js +4 -0
- package/dist/session/session-css.generated.js.map +1 -0
- package/dist/session/session-css.js +1 -17
- package/dist/session/session-css.js.map +1 -1
- package/dist/ui/AlertModal.d.ts +28 -0
- package/dist/ui/AlertModal.d.ts.map +1 -0
- package/dist/ui/AlertModal.js +29 -0
- package/dist/ui/AlertModal.js.map +1 -0
- package/dist/ui/Button.d.ts.map +1 -1
- package/dist/ui/Button.js +14 -1
- package/dist/ui/Button.js.map +1 -1
- package/dist/ui/Card.d.ts +4 -2
- package/dist/ui/Card.d.ts.map +1 -1
- package/dist/ui/Card.js +12 -6
- package/dist/ui/Card.js.map +1 -1
- package/dist/ui/Chip.d.ts +9 -1
- package/dist/ui/Chip.d.ts.map +1 -1
- package/dist/ui/Chip.js +8 -2
- package/dist/ui/Chip.js.map +1 -1
- package/dist/ui/CodeViewer.d.ts +36 -0
- package/dist/ui/CodeViewer.d.ts.map +1 -0
- package/dist/ui/CodeViewer.js +90 -0
- package/dist/ui/CodeViewer.js.map +1 -0
- package/dist/ui/ConfirmModal.d.ts +30 -0
- package/dist/ui/ConfirmModal.d.ts.map +1 -0
- package/dist/ui/ConfirmModal.js +23 -0
- package/dist/ui/ConfirmModal.js.map +1 -0
- package/dist/ui/DateTimePicker.d.ts +45 -0
- package/dist/ui/DateTimePicker.d.ts.map +1 -0
- package/dist/ui/DateTimePicker.js +447 -0
- package/dist/ui/DateTimePicker.js.map +1 -0
- package/dist/ui/Field.d.ts +11 -2
- package/dist/ui/Field.d.ts.map +1 -1
- package/dist/ui/Field.js +15 -5
- package/dist/ui/Field.js.map +1 -1
- package/dist/ui/MarqueeText.d.ts.map +1 -1
- package/dist/ui/MarqueeText.js +3 -1
- package/dist/ui/MarqueeText.js.map +1 -1
- package/dist/ui/Modal.d.ts +25 -3
- package/dist/ui/Modal.d.ts.map +1 -1
- package/dist/ui/Modal.js +41 -7
- package/dist/ui/Modal.js.map +1 -1
- package/dist/ui/ModelPicker.d.ts +25 -12
- package/dist/ui/ModelPicker.d.ts.map +1 -1
- package/dist/ui/ModelPicker.js +67 -19
- package/dist/ui/ModelPicker.js.map +1 -1
- package/dist/ui/PanelDock.d.ts +127 -0
- package/dist/ui/PanelDock.d.ts.map +1 -0
- package/dist/ui/PanelDock.js +297 -0
- package/dist/ui/PanelDock.js.map +1 -0
- package/dist/ui/Picker.d.ts +8 -2
- package/dist/ui/Picker.d.ts.map +1 -1
- package/dist/ui/Picker.js +86 -18
- package/dist/ui/Picker.js.map +1 -1
- package/dist/ui/SectionHead.d.ts +3 -3
- package/dist/ui/SectionHead.d.ts.map +1 -1
- package/dist/ui/SectionHead.js +4 -4
- package/dist/ui/SectionHead.js.map +1 -1
- package/dist/ui/Segmented.d.ts +2 -0
- package/dist/ui/Segmented.d.ts.map +1 -1
- package/dist/ui/Segmented.js +3 -1
- package/dist/ui/Segmented.js.map +1 -1
- package/dist/ui/Textarea.d.ts +2 -0
- package/dist/ui/Textarea.d.ts.map +1 -1
- package/dist/ui/Textarea.js +6 -1
- package/dist/ui/Textarea.js.map +1 -1
- package/dist/ui/Toast.d.ts +41 -8
- package/dist/ui/Toast.d.ts.map +1 -1
- package/dist/ui/Toast.js +216 -28
- package/dist/ui/Toast.js.map +1 -1
- package/dist/ui/WorkspacePicker.d.ts +15 -28
- package/dist/ui/WorkspacePicker.d.ts.map +1 -1
- package/dist/ui/WorkspacePicker.js +18 -27
- package/dist/ui/WorkspacePicker.js.map +1 -1
- package/dist/ui/card-css.d.ts +1 -1
- package/dist/ui/card-css.d.ts.map +1 -1
- package/dist/ui/card-css.generated.d.ts +2 -0
- package/dist/ui/card-css.generated.d.ts.map +1 -0
- package/dist/ui/card-css.generated.js +4 -0
- package/dist/ui/card-css.generated.js.map +1 -0
- package/dist/ui/card-css.js +1 -35
- package/dist/ui/card-css.js.map +1 -1
- package/dist/ui/chip-css.d.ts +1 -1
- package/dist/ui/chip-css.d.ts.map +1 -1
- package/dist/ui/chip-css.generated.d.ts +2 -0
- package/dist/ui/chip-css.generated.d.ts.map +1 -0
- package/dist/ui/chip-css.generated.js +4 -0
- package/dist/ui/chip-css.generated.js.map +1 -0
- package/dist/ui/chip-css.js +1 -17
- package/dist/ui/chip-css.js.map +1 -1
- package/dist/ui/code-viewer-css.d.ts +2 -0
- package/dist/ui/code-viewer-css.d.ts.map +1 -0
- package/dist/ui/code-viewer-css.generated.d.ts +2 -0
- package/dist/ui/code-viewer-css.generated.d.ts.map +1 -0
- package/dist/ui/code-viewer-css.generated.js +4 -0
- package/dist/ui/code-viewer-css.generated.js.map +1 -0
- package/dist/ui/code-viewer-css.js +3 -0
- package/dist/ui/code-viewer-css.js.map +1 -0
- package/dist/ui/code-viewer-lang.d.ts +15 -0
- package/dist/ui/code-viewer-lang.d.ts.map +1 -0
- package/dist/ui/code-viewer-lang.js +190 -0
- package/dist/ui/code-viewer-lang.js.map +1 -0
- package/dist/ui/code-viewer-theme.d.ts +5 -0
- package/dist/ui/code-viewer-theme.d.ts.map +1 -0
- package/dist/ui/code-viewer-theme.js +138 -0
- package/dist/ui/code-viewer-theme.js.map +1 -0
- package/dist/ui/controls/back-to-top-css.generated.d.ts +2 -0
- package/dist/ui/controls/back-to-top-css.generated.d.ts.map +1 -0
- package/dist/ui/controls/back-to-top-css.generated.js +4 -0
- package/dist/ui/controls/back-to-top-css.generated.js.map +1 -0
- package/dist/ui/controls/button-css.generated.d.ts +2 -0
- package/dist/ui/controls/button-css.generated.d.ts.map +1 -0
- package/dist/ui/controls/button-css.generated.js +4 -0
- package/dist/ui/controls/button-css.generated.js.map +1 -0
- package/dist/ui/controls/datetime-css.generated.d.ts +2 -0
- package/dist/ui/controls/datetime-css.generated.d.ts.map +1 -0
- package/dist/ui/controls/datetime-css.generated.js +4 -0
- package/dist/ui/controls/datetime-css.generated.js.map +1 -0
- package/dist/ui/controls/field-css.generated.d.ts +2 -0
- package/dist/ui/controls/field-css.generated.d.ts.map +1 -0
- package/dist/ui/controls/field-css.generated.js +4 -0
- package/dist/ui/controls/field-css.generated.js.map +1 -0
- package/dist/ui/controls/hint-css.generated.d.ts +2 -0
- package/dist/ui/controls/hint-css.generated.d.ts.map +1 -0
- package/dist/ui/controls/hint-css.generated.js +4 -0
- package/dist/ui/controls/hint-css.generated.js.map +1 -0
- package/dist/ui/controls/loading-css.generated.d.ts +2 -0
- package/dist/ui/controls/loading-css.generated.d.ts.map +1 -0
- package/dist/ui/controls/loading-css.generated.js +4 -0
- package/dist/ui/controls/loading-css.generated.js.map +1 -0
- package/dist/ui/controls/segmented-css.generated.d.ts +2 -0
- package/dist/ui/controls/segmented-css.generated.d.ts.map +1 -0
- package/dist/ui/controls/segmented-css.generated.js +4 -0
- package/dist/ui/controls/segmented-css.generated.js.map +1 -0
- package/dist/ui/controls-css.d.ts +7 -8
- package/dist/ui/controls-css.d.ts.map +1 -1
- package/dist/ui/controls-css.js +13 -11
- package/dist/ui/controls-css.js.map +1 -1
- package/dist/ui/css.d.ts +5 -0
- package/dist/ui/css.d.ts.map +1 -1
- package/dist/ui/css.js +13 -4
- package/dist/ui/css.js.map +1 -1
- package/dist/ui/datetime-logic.d.ts +100 -0
- package/dist/ui/datetime-logic.d.ts.map +1 -0
- package/dist/ui/datetime-logic.js +193 -0
- package/dist/ui/datetime-logic.js.map +1 -0
- package/dist/ui/feedback-css.d.ts +1 -1
- package/dist/ui/feedback-css.d.ts.map +1 -1
- package/dist/ui/feedback-css.generated.d.ts +2 -0
- package/dist/ui/feedback-css.generated.d.ts.map +1 -0
- package/dist/ui/feedback-css.generated.js +4 -0
- package/dist/ui/feedback-css.generated.js.map +1 -0
- package/dist/ui/feedback-css.js +1 -53
- package/dist/ui/feedback-css.js.map +1 -1
- package/dist/ui/host-data.d.ts +103 -0
- package/dist/ui/host-data.d.ts.map +1 -0
- package/dist/ui/host-data.js +106 -0
- package/dist/ui/host-data.js.map +1 -0
- package/dist/ui/host-toast.d.ts +23 -0
- package/dist/ui/host-toast.d.ts.map +1 -0
- package/dist/ui/host-toast.js +34 -0
- package/dist/ui/host-toast.js.map +1 -0
- package/dist/ui/index.d.ts +12 -6
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +10 -4
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/marquee-css.d.ts +2 -0
- package/dist/ui/marquee-css.d.ts.map +1 -0
- package/dist/ui/marquee-css.generated.d.ts +2 -0
- package/dist/ui/marquee-css.generated.d.ts.map +1 -0
- package/dist/ui/marquee-css.generated.js +4 -0
- package/dist/ui/marquee-css.generated.js.map +1 -0
- package/dist/ui/marquee-css.js +4 -0
- package/dist/ui/marquee-css.js.map +1 -0
- package/dist/ui/modal-host.d.ts +30 -0
- package/dist/ui/modal-host.d.ts.map +1 -0
- package/dist/ui/modal-host.js +101 -0
- package/dist/ui/modal-host.js.map +1 -0
- package/dist/ui/official.d.ts.map +1 -1
- package/dist/ui/official.js +45 -1
- package/dist/ui/official.js.map +1 -1
- package/dist/ui/panel-dock-css.d.ts +2 -0
- package/dist/ui/panel-dock-css.d.ts.map +1 -0
- package/dist/ui/panel-dock-css.generated.d.ts +2 -0
- package/dist/ui/panel-dock-css.generated.d.ts.map +1 -0
- package/dist/ui/panel-dock-css.generated.js +4 -0
- package/dist/ui/panel-dock-css.generated.js.map +1 -0
- package/dist/ui/panel-dock-css.js +4 -0
- package/dist/ui/panel-dock-css.js.map +1 -0
- package/dist/ui/panel-dock-logic.d.ts +50 -0
- package/dist/ui/panel-dock-logic.d.ts.map +1 -0
- package/dist/ui/panel-dock-logic.js +64 -0
- package/dist/ui/panel-dock-logic.js.map +1 -0
- package/dist/ui/picker-css.d.ts +1 -1
- package/dist/ui/picker-css.d.ts.map +1 -1
- package/dist/ui/picker-css.generated.d.ts +2 -0
- package/dist/ui/picker-css.generated.d.ts.map +1 -0
- package/dist/ui/picker-css.generated.js +4 -0
- package/dist/ui/picker-css.generated.js.map +1 -0
- package/dist/ui/picker-css.js +1 -48
- package/dist/ui/picker-css.js.map +1 -1
- package/dist/ui/picker-icons.d.ts +12 -0
- package/dist/ui/picker-icons.d.ts.map +1 -0
- package/dist/ui/picker-icons.js +29 -0
- package/dist/ui/picker-icons.js.map +1 -0
- package/dist/ui/picker-logic.d.ts +4 -1
- package/dist/ui/picker-logic.d.ts.map +1 -1
- package/dist/ui/picker-logic.js +1 -2
- package/dist/ui/picker-logic.js.map +1 -1
- package/dist/ui/resizer.d.ts +3 -1
- package/dist/ui/resizer.d.ts.map +1 -1
- package/dist/ui/resizer.js +1 -1
- package/dist/ui/resizer.js.map +1 -1
- package/dist/ui/toast-host.d.ts +24 -0
- package/dist/ui/toast-host.d.ts.map +1 -0
- package/dist/ui/toast-host.js +20 -0
- package/dist/ui/toast-host.js.map +1 -0
- package/dist/utils/date.d.ts +8 -0
- package/dist/utils/date.d.ts.map +1 -1
- package/dist/utils/date.js +16 -0
- package/dist/utils/date.js.map +1 -1
- package/dist/utils/i18n.d.ts +1 -1
- package/dist/utils/i18n.d.ts.map +1 -1
- package/dist/utils/i18n.js +437 -18
- package/dist/utils/i18n.js.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/path.d.ts.map +1 -1
- package/dist/utils/path.js +1 -0
- package/dist/utils/path.js.map +1 -1
- package/docs/README.md +12 -5
- package/docs/design/architecture.md +38 -8
- package/docs/design/copywriting.md +95 -0
- package/docs/design/doc-format.md +80 -0
- package/docs/design/doc-format.ts +39 -0
- package/docs/design/external/dsh-host.md +150 -1
- package/docs/design/features.md +4 -17
- package/docs/design/methods.md +13 -34
- package/docs/design/structure.md +25 -22
- package/docs/design/style.md +24 -50
- package/docs/help/design.doc.ts +112 -0
- package/docs/help/design.md +88 -0
- package/docs/help/explorer.doc.ts +136 -0
- package/docs/help/explorer.md +68 -0
- package/docs/help/index.doc.ts +79 -0
- package/docs/help/index.md +28 -0
- package/docs/help/integration.doc.ts +114 -0
- package/docs/help/integration.md +127 -0
- package/docs/help/methods.md +58 -0
- package/docs/help/session.doc.ts +154 -0
- package/docs/help/session.md +91 -0
- package/docs/help/style.md +80 -0
- package/docs/help/ui.doc.ts +404 -0
- package/docs/help/ui.md +342 -0
- package/docs/help/utils.doc.ts +133 -0
- package/docs/help/utils.md +96 -0
- package/docs/requirements.md +33 -12
- package/package.json +44 -3
- package/dist/ui/ErrorBoundary.d.ts +0 -22
- package/dist/ui/ErrorBoundary.d.ts.map +0 -1
- package/dist/ui/ErrorBoundary.js +0 -30
- package/dist/ui/ErrorBoundary.js.map +0 -1
- package/docs/design/features/design.md +0 -55
- package/docs/design/features/explorer.md +0 -73
- package/docs/design/features/session.md +0 -70
- package/docs/design/features/ui.md +0 -225
- package/docs/design/features/utils.md +0 -64
- package/docs/examples/integration.md +0 -103
|
@@ -0,0 +1,659 @@
|
|
|
1
|
+
// 工作区文件浏览器(U11):面包屑 + ▾ 选层 + 内联展开目录树 + 文件预览。
|
|
2
|
+
//
|
|
3
|
+
// 源实现 = 兄弟项目 dsh-task-dispatch-table/src/client/file-browser.tsx(868 行)——本文件是它的
|
|
4
|
+
// core 重实现("抄"不是"复制"):组件全部换 core 自己的体系,官方件只经 ui/official 消费;
|
|
5
|
+
// 修掉源实现的已知问题:根缓存改可注入(原模块级 Map 永不清除)、复制路径补 catch、
|
|
6
|
+
// 复制/选中路径口径统一为工作区相对、去掉死代码。
|
|
7
|
+
//
|
|
8
|
+
// 两个消费场景共用本组件(用户 2026-10-20 拍板:同一套内容,两种展现形式):
|
|
9
|
+
// · 浏览(dock):由 **核心 PanelDock** 承载(openFilePreview),拖宽与关闭都由它负责,
|
|
10
|
+
// 本组件不再自绘拖拽条 / 侧边栏外壳;
|
|
11
|
+
// · 选择(picker):`picker` + `onPick(path, isDir)`(path 为**工作区相对**路径),点文件即回调。
|
|
12
|
+
//
|
|
13
|
+
// 控件纪律:能用核心控件的一律核心(ghost 面包屑 / IconButton / Segmented / MarqueeText);
|
|
14
|
+
// 核心没有的那一项 = ▾ 选层浮层,按兄弟项目 dsh-task-dispatch-table 的 `.dsh-tdt-sv-crumbs-*`
|
|
15
|
+
// 逐像素照抄(间距 / 圆角 / 最大高度 / 空态),不自创度量。
|
|
16
|
+
import { createElement as h, Fragment, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore, } from 'react';
|
|
17
|
+
import { IconCheckOutlineRegular, IconChevronDownOutlineRegular, IconChevronRightOutlineRegular, IconCloseOutlineRegular, IconCopyOutlineRegular, IconFolderCloseRegular, IconFolderOpenOutlineRegular, IconRefreshOutlineRegular, writeClipboard, FileTypeIcon, } from '../ui/official.js';
|
|
18
|
+
import { ensureControls } from '../ui/css.js';
|
|
19
|
+
import { Button, IconButton } from '../ui/Button.js';
|
|
20
|
+
import { MarqueeText } from '../ui/MarqueeText.js';
|
|
21
|
+
import { Segmented } from '../ui/Segmented.js';
|
|
22
|
+
import { workspaceList } from '../ui/host-data.js';
|
|
23
|
+
import { t } from '../utils/i18n.js';
|
|
24
|
+
import { FilePreview } from './preview/index.js';
|
|
25
|
+
import { absolutizeDir, bareCode, baseNameOfPath, crumbsOfDir, createRootCache, dirnameOf, errView, findAnyFileAbs, isAbsoluteish, isFailed, joinPath, learnRoot, listingOf, listPathOf, matchesFilter, relativizeToRoot, sortEntries, workspaceAnchorForPath, } from './types.js';
|
|
26
|
+
/**
|
|
27
|
+
* ▾ 下拉条目构造(**picker 与 dock 共用同一段代码**,差别只在入参——源实现用户 2026-09-29
|
|
28
|
+
* 点名提炼,严禁两处各写一份):
|
|
29
|
+
* - 当前工作区排第一(打开文件夹图标 + 加粗),**其下路径紧跟**(每层一个右箭头位缩进),
|
|
30
|
+
* 再列其他工作区(关合图标 + 灰)——「打开哪个就跟在哪个下面」;点当前工作区 = 回到其根目录。
|
|
31
|
+
* - 当前工作区未知(rootName 空或不在清单)时路径段置顶、全部工作区按「其他」罗列;
|
|
32
|
+
* - workspaces 不传 = 不显示工作区段(根无箭头、其下逐层缩进)。
|
|
33
|
+
*
|
|
34
|
+
* 条目是**自写浮层**(核心没有「带缩进的选择层菜单」这类控件 ⇒ 按兄弟项目
|
|
35
|
+
* `dsh-task-dispatch-table` 的 `.dsh-tdt-sv-crumbs-menu*` 逐条照抄间距):
|
|
36
|
+
* backdrop + 菜单本体 + `-item`(4px 8px / 最大宽 280 / 超出滚动 / 空态)。
|
|
37
|
+
*/
|
|
38
|
+
function crumbsMenuEntries(params) {
|
|
39
|
+
const { workspaces, rootName, rootFallbackLabel, crumbs, onPickWorkspace, onLoadDir } = params;
|
|
40
|
+
const out = [];
|
|
41
|
+
// 内联样式照抄兄弟项目(wsEntry 的 gap 6px 覆盖类的 2px:图标与文字的间距以它为准)。
|
|
42
|
+
const wsEntry = (ws, current) => h('button', {
|
|
43
|
+
key: `ws:${ws}`,
|
|
44
|
+
type: 'button',
|
|
45
|
+
role: 'menuitem',
|
|
46
|
+
className: 'dshpc-fb__menu-item',
|
|
47
|
+
style: { paddingLeft: 8, display: 'flex', alignItems: 'center', gap: '6px' },
|
|
48
|
+
title: ws,
|
|
49
|
+
// 点当前工作区 = 回到该工作区根目录(浏览深处时唯一的「回根」入口,兄弟 2026-10-10);
|
|
50
|
+
// 点其他工作区才走 onPickWorkspace(换会话)。
|
|
51
|
+
onClick: () => {
|
|
52
|
+
if (current)
|
|
53
|
+
onLoadDir?.('');
|
|
54
|
+
else
|
|
55
|
+
onPickWorkspace?.(ws);
|
|
56
|
+
},
|
|
57
|
+
}, h('span', { className: 'dshpc-fb__menu-icon', style: { color: current ? 'var(--dshpc-fg)' : 'var(--dshpc-fg-2)' } }, h(current ? IconFolderOpenOutlineRegular : IconFolderCloseRegular, { size: 13 })), h('span', { className: 'dshpc-fb__menu-label', style: { fontWeight: current ? 600 : 400 } }, ws));
|
|
58
|
+
const pathEntry = (crumb, depth, withArrow = true) => h('button', {
|
|
59
|
+
key: `dir:${crumb.path}`,
|
|
60
|
+
type: 'button',
|
|
61
|
+
role: 'menuitem',
|
|
62
|
+
className: 'dshpc-fb__menu-item',
|
|
63
|
+
style: { paddingLeft: 8 },
|
|
64
|
+
title: crumb.path,
|
|
65
|
+
onClick: () => {
|
|
66
|
+
onLoadDir?.(crumb.path);
|
|
67
|
+
},
|
|
68
|
+
}, withArrow
|
|
69
|
+
? h(Fragment, null, Array.from({ length: depth }, (_, s) => h('span', { key: `s${s}`, className: 'dshpc-fb__menu-chev-slot', 'aria-hidden': true })), h('span', { className: 'dshpc-fb__menu-chev' }, h(IconChevronRightOutlineRegular, { size: 11 })))
|
|
70
|
+
: null, h('span', { className: 'dshpc-fb__menu-label' }, crumb.label));
|
|
71
|
+
if (workspaces === undefined) {
|
|
72
|
+
if (crumbs.length === 0)
|
|
73
|
+
return [h('div', { key: 'empty', className: 'dshpc-fb__menu-empty' }, rootFallbackLabel)];
|
|
74
|
+
return crumbs.map((crumb, index) => pathEntry(crumb, index === 0 ? 0 : index - 1, index !== 0));
|
|
75
|
+
}
|
|
76
|
+
// 路径段:根 crumb(path='')已由工作区段表达时剔除,深度从根下一层起算。
|
|
77
|
+
const segments = rootName !== '' && crumbs.length > 0 && crumbs[0]?.path === '' ? crumbs.slice(1) : crumbs;
|
|
78
|
+
if (rootName !== '' && workspaces.includes(rootName)) {
|
|
79
|
+
// 当前工作区排第一,其下路径紧跟,然后是其他工作区(保持原相对顺序)。
|
|
80
|
+
out.push(wsEntry(rootName, true));
|
|
81
|
+
segments.forEach((crumb, i) => {
|
|
82
|
+
out.push(pathEntry(crumb, i));
|
|
83
|
+
});
|
|
84
|
+
for (const ws of workspaces) {
|
|
85
|
+
if (ws !== rootName)
|
|
86
|
+
out.push(wsEntry(ws, false));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
segments.forEach((crumb, i) => {
|
|
91
|
+
out.push(pathEntry(crumb, i));
|
|
92
|
+
});
|
|
93
|
+
for (const ws of workspaces)
|
|
94
|
+
out.push(wsEntry(ws, false));
|
|
95
|
+
}
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
/** 目录浏览器面板。详见 {@link FileBrowserProps}。 */
|
|
99
|
+
export function FileBrowser(props) {
|
|
100
|
+
ensureControls();
|
|
101
|
+
const tt = props.translate ?? t;
|
|
102
|
+
const picker = props.picker === true;
|
|
103
|
+
// 根缓存:调用方可注入(多实例/清理策略归调用方);不传则内部自建、随组件废弃。
|
|
104
|
+
const cacheRef = useRef(null);
|
|
105
|
+
if (cacheRef.current === null)
|
|
106
|
+
cacheRef.current = props.rootCache ?? createRootCache();
|
|
107
|
+
const rootCache = props.rootCache ?? cacheRef.current;
|
|
108
|
+
// mode:加载/目录树/文件预览/列举错误。viewing 非空 ⇒ 在 dir 树内预览文件。
|
|
109
|
+
const [mode, setMode] = useState('loading');
|
|
110
|
+
const [dir, setDir] = useState('');
|
|
111
|
+
const [listing, setListing] = useState(null);
|
|
112
|
+
const [truncated, setTruncated] = useState(false);
|
|
113
|
+
const [viewing, setViewing] = useState(null);
|
|
114
|
+
const [listErr, setListErr] = useState(null);
|
|
115
|
+
// 面包屑折叠(超宽时只显示当前层名;行首 ▾ 选层常驻)。
|
|
116
|
+
const [crumbsOverflow, setCrumbsOverflow] = useState(false);
|
|
117
|
+
const [menuOpen, setMenuOpen] = useState(false);
|
|
118
|
+
const regionRef = useRef(null);
|
|
119
|
+
const measureRef = useRef(null);
|
|
120
|
+
// 预览态:md/html 渲染⇄源码 + 刷新自增(触发预览体重读)。
|
|
121
|
+
const [sourceView, setSourceView] = useState(false);
|
|
122
|
+
const [reloadNonce, setReloadNonce] = useState(0);
|
|
123
|
+
const [copied, setCopied] = useState(false);
|
|
124
|
+
// 内联展开:openDirs = 已展开目录路径集合;childCache = 各展开目录拉到的子项。
|
|
125
|
+
const [openDirs, setOpenDirs] = useState(new Set());
|
|
126
|
+
const [childCache, setChildCache] = useState({});
|
|
127
|
+
// 工作区根补学完成信号(缓存非响应式,学成后 bump 触发面包屑重算)。
|
|
128
|
+
const [, setRootNonce] = useState(0);
|
|
129
|
+
// 当前目录在会话工作区之外(官方 list 限工作区内,越界即报 outside-workspace)。
|
|
130
|
+
// 依据 = list 的**真实错误码**,不靠缓存猜(根学不出时会误判成"在区内")。
|
|
131
|
+
const [outside, setOutside] = useState(false);
|
|
132
|
+
// 路径 → 工作区匹配:props.path 是绝对路径时,找出它所属的工作区(最长前缀),
|
|
133
|
+
// list / 学根 / 相对化**全部**换用该工作区的会话与根——官方 list 按 sessionId 解析工作区根,
|
|
134
|
+
// 只换 list 的 sessionId 不换学根的键,面包屑会露出工作区之外的层级(2026-10-22 真机:
|
|
135
|
+
// 匹配生效但 rootCache 仍按 props.sessionId 学根 ⇒ 相对化失败 ⇒ 显示 `C.x > workspace > …`)。
|
|
136
|
+
// 匹配不到 ⇒ 整体回落 props.sessionId(旧行为)。
|
|
137
|
+
// 工作区清单来源(优先级从高到低):① props.workspaceAnchors(调用方显式给,可覆盖);
|
|
138
|
+
// ② 宿主 workspaces 快照(connectHost 注入的 workspaceList,官方 WorkspaceView 镜像)。
|
|
139
|
+
// 快照异步到达时会重算 + 重跑初始 effect(listSessionId 在其依赖里)⇒ 自愈,无感。
|
|
140
|
+
const hostWorkspaces = useSyncExternalStore(workspaceList.subscribe, workspaceList.getSnapshot);
|
|
141
|
+
const anchors = props.workspaceAnchors ?? hostWorkspaces;
|
|
142
|
+
const anchorMatch = useMemo(() => workspaceAnchorForPath(props.path, anchors), [props.path, anchors]);
|
|
143
|
+
const listSessionId = anchorMatch?.sessionId ?? props.sessionId;
|
|
144
|
+
/** 记一次 list 结果的"是否越界"(outside-workspace / not-found 均视为不可浏览)。 */
|
|
145
|
+
const noteOutside = (parsed) => {
|
|
146
|
+
if (parsed === null || parsed === undefined)
|
|
147
|
+
return;
|
|
148
|
+
if (!isFailed(parsed)) {
|
|
149
|
+
setOutside(false);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
const code = bareCode(parsed.failed);
|
|
153
|
+
if (code === 'outside-workspace' || code === 'not-found')
|
|
154
|
+
setOutside(true);
|
|
155
|
+
};
|
|
156
|
+
/** 列举某目录并展示(清空 viewing;收起下拉)。 */
|
|
157
|
+
const fetchDir = useCallback((targetDir) => {
|
|
158
|
+
setDir(targetDir);
|
|
159
|
+
setViewing(null);
|
|
160
|
+
setListErr(null);
|
|
161
|
+
setMenuOpen(false);
|
|
162
|
+
setMode('loading');
|
|
163
|
+
// 切换顶层目录:清空内联展开态(展开树只属于当前这一层)。
|
|
164
|
+
setOpenDirs(new Set());
|
|
165
|
+
setChildCache({});
|
|
166
|
+
props.workspaceFiles
|
|
167
|
+
.list(listSessionId, listPathOf(targetDir))
|
|
168
|
+
.then((result) => {
|
|
169
|
+
const parsed = listingOf(result);
|
|
170
|
+
noteOutside(parsed);
|
|
171
|
+
if (isFailed(parsed)) {
|
|
172
|
+
setListErr(errView(parsed.failed));
|
|
173
|
+
setMode('error');
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (parsed === null) {
|
|
177
|
+
setListErr({ key: 'previewBadPayload' });
|
|
178
|
+
setMode('error');
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
setListing(parsed.entries);
|
|
182
|
+
setTruncated(parsed.truncated);
|
|
183
|
+
setMode('dir');
|
|
184
|
+
})
|
|
185
|
+
.catch((error) => {
|
|
186
|
+
setListErr(errView(error));
|
|
187
|
+
setMode('error');
|
|
188
|
+
});
|
|
189
|
+
}, [listSessionId, props.workspaceFiles]);
|
|
190
|
+
/** 进入某目录;点到当前目录本身 = 重列本层(刷新)。 */
|
|
191
|
+
const loadDir = useCallback((targetDir) => {
|
|
192
|
+
if (targetDir === dir) {
|
|
193
|
+
fetchDir(targetDir);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
fetchDir(targetDir);
|
|
197
|
+
}, [dir, fetchDir]);
|
|
198
|
+
// 初次进入(path 可能是文件或目录):
|
|
199
|
+
// list(path) 成功 ⇒ 目录树(dir 尽量取宿主绝对路径 ⇒ 面包屑从工作区根往下列);
|
|
200
|
+
// 报错 ⇒ 文件预览态(dir = 父目录,面包屑保留可返回)。
|
|
201
|
+
// ⚠️ 切勿改用服务端 list 返回的 path 当面包屑:那是 workspacePathOf 的工作区**相对**形式,
|
|
202
|
+
// 会丢掉根以下的前导段,面包屑只剩最近一层(源实现 2026-09-29 踩过 ⇒ 已回退为入参路径)。
|
|
203
|
+
useEffect(() => {
|
|
204
|
+
let alive = true;
|
|
205
|
+
setMode('loading');
|
|
206
|
+
setListErr(null);
|
|
207
|
+
setViewing(null);
|
|
208
|
+
setSourceView(false);
|
|
209
|
+
setReloadNonce(0);
|
|
210
|
+
setMenuOpen(false);
|
|
211
|
+
setOpenDirs(new Set());
|
|
212
|
+
setChildCache({});
|
|
213
|
+
/** 补学工作区根(面包屑根名显示 + picker 相对化要用):列目录 → 有文件就 stat 反推,没有就走有界 BFS。 */
|
|
214
|
+
const ensureRootLearned = (currentDir) => {
|
|
215
|
+
if (rootCache.get(listSessionId) !== undefined)
|
|
216
|
+
return;
|
|
217
|
+
void (async () => {
|
|
218
|
+
try {
|
|
219
|
+
const parsed = listingOf(await props.workspaceFiles.list(listSessionId, listPathOf(currentDir)));
|
|
220
|
+
if (!alive || parsed === null || isFailed(parsed))
|
|
221
|
+
return;
|
|
222
|
+
const stat = props.workspaceFiles.stat;
|
|
223
|
+
if (stat === undefined)
|
|
224
|
+
return;
|
|
225
|
+
const rel = parsed.path;
|
|
226
|
+
const file = parsed.entries.find((entry) => entry.type === 'file');
|
|
227
|
+
if (file !== undefined) {
|
|
228
|
+
const sub = rel === '' ? file.name : `${rel}/${file.name}`;
|
|
229
|
+
const abs = (await stat(listSessionId, sub).catch(() => null));
|
|
230
|
+
const absPath = typeof abs?.absolutePath === 'string' ? abs.absolutePath : null;
|
|
231
|
+
if (absPath !== null && learnRoot(rootCache, listSessionId, sub, absPath) !== null) {
|
|
232
|
+
setRootNonce((n) => n + 1);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
const found = await findAnyFileAbs(props.workspaceFiles, listSessionId, rel);
|
|
237
|
+
if (found !== null && learnRoot(rootCache, listSessionId, found.relativePath, found.absolutePath) !== null) {
|
|
238
|
+
setRootNonce((n) => n + 1);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
/* 学不出就维持退化显示 */
|
|
243
|
+
}
|
|
244
|
+
})();
|
|
245
|
+
};
|
|
246
|
+
props.workspaceFiles
|
|
247
|
+
.list(listSessionId, listPathOf(props.path))
|
|
248
|
+
.then(async (result) => {
|
|
249
|
+
if (!alive)
|
|
250
|
+
return;
|
|
251
|
+
const parsed = listingOf(result);
|
|
252
|
+
if (!isFailed(parsed) && parsed !== null) {
|
|
253
|
+
setOutside(false);
|
|
254
|
+
// 入口可能是工作区相对名 ⇒ 用目录里任一文件子项的 stat 反推绝对路径;
|
|
255
|
+
// 解析不出(空目录/只有子目录/stat 缺失)就退回入参,不阻塞浏览。
|
|
256
|
+
const absDir = await absolutizeDir(rootCache, props.workspaceFiles, listSessionId, props.path, parsed.path, parsed.entries);
|
|
257
|
+
if (!alive)
|
|
258
|
+
return;
|
|
259
|
+
setDir(absDir);
|
|
260
|
+
setListing(parsed.entries);
|
|
261
|
+
setTruncated(parsed.truncated);
|
|
262
|
+
setMode('dir');
|
|
263
|
+
ensureRootLearned(absDir);
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
// 不是目录 ⇒ 当作文件预览,dir 取父目录,尽量把父树也列出来。
|
|
267
|
+
let parent = dirnameOf(props.path);
|
|
268
|
+
if (!isAbsoluteish(props.path)) {
|
|
269
|
+
const stat = props.workspaceFiles.stat;
|
|
270
|
+
if (stat !== undefined) {
|
|
271
|
+
try {
|
|
272
|
+
const res = await stat(listSessionId, props.path);
|
|
273
|
+
const abs = typeof res.absolutePath === 'string'
|
|
274
|
+
? res.absolutePath
|
|
275
|
+
: null;
|
|
276
|
+
if (abs !== null) {
|
|
277
|
+
parent = dirnameOf(abs);
|
|
278
|
+
// 记住工作区根 ⇒ 之后同会话里相对名也能解析
|
|
279
|
+
learnRoot(rootCache, listSessionId, props.path, abs);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
/* 解析不出就用入参父目录 */
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (!alive)
|
|
288
|
+
return;
|
|
289
|
+
setDir(parent);
|
|
290
|
+
setViewing(props.path);
|
|
291
|
+
setMode('file');
|
|
292
|
+
ensureRootLearned(parent);
|
|
293
|
+
props.workspaceFiles
|
|
294
|
+
.list(listSessionId, listPathOf(parent))
|
|
295
|
+
.then((pres) => {
|
|
296
|
+
if (!alive)
|
|
297
|
+
return;
|
|
298
|
+
const pl = listingOf(pres);
|
|
299
|
+
noteOutside(pl);
|
|
300
|
+
if (!isFailed(pl) && pl !== null) {
|
|
301
|
+
setListing(pl.entries);
|
|
302
|
+
setTruncated(pl.truncated);
|
|
303
|
+
}
|
|
304
|
+
})
|
|
305
|
+
.catch(() => {
|
|
306
|
+
/* 父树列不出不影响文件预览 */
|
|
307
|
+
});
|
|
308
|
+
})
|
|
309
|
+
.catch(() => {
|
|
310
|
+
if (!alive)
|
|
311
|
+
return;
|
|
312
|
+
// 传输层失败:仍尝试按文件预览(预览体内部会给出错误态)。
|
|
313
|
+
const parent = dirnameOf(props.path);
|
|
314
|
+
setDir(parent);
|
|
315
|
+
setViewing(props.path);
|
|
316
|
+
setMode('file');
|
|
317
|
+
ensureRootLearned(parent);
|
|
318
|
+
});
|
|
319
|
+
return () => {
|
|
320
|
+
alive = false;
|
|
321
|
+
};
|
|
322
|
+
}, [props.workspaceFiles, props.path, rootCache, listSessionId]);
|
|
323
|
+
// 面包屑溢出测量:隐藏测量条渲染与可见态完全相同的 crumb 按钮(含 padding/max-width),
|
|
324
|
+
// 使溢出判定与真实排版一致——不超长时必定还原完整路径。ResizeObserver 同时覆盖
|
|
325
|
+
// dock 宽度变化 / 初次布局。
|
|
326
|
+
useLayoutEffect(() => {
|
|
327
|
+
const region = regionRef.current;
|
|
328
|
+
const measure = measureRef.current;
|
|
329
|
+
if (region === null || measure === null)
|
|
330
|
+
return;
|
|
331
|
+
const recompute = () => {
|
|
332
|
+
setCrumbsOverflow(measure.scrollWidth > region.clientWidth + 1);
|
|
333
|
+
};
|
|
334
|
+
recompute();
|
|
335
|
+
const ro = new ResizeObserver(recompute);
|
|
336
|
+
ro.observe(region);
|
|
337
|
+
return () => ro.disconnect();
|
|
338
|
+
}, [dir, viewing, mode, listing]);
|
|
339
|
+
const reload = useCallback(() => {
|
|
340
|
+
if (viewing !== null) {
|
|
341
|
+
setReloadNonce((n) => n + 1);
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
loadDir(dir);
|
|
345
|
+
}, [viewing, dir, loadDir]);
|
|
346
|
+
const copyPath = useCallback(() => {
|
|
347
|
+
// 口径统一:复制**工作区相对**路径(与 picker onPick 一致;绝对形态对用户无意义)。
|
|
348
|
+
const target = viewing ?? dir;
|
|
349
|
+
if (target === '')
|
|
350
|
+
return;
|
|
351
|
+
const rel = relativizeToRoot(rootCache, listSessionId, target);
|
|
352
|
+
writeClipboard(rel)
|
|
353
|
+
.then((ok) => {
|
|
354
|
+
if (ok) {
|
|
355
|
+
setCopied(true);
|
|
356
|
+
window.setTimeout(() => setCopied(false), 1500);
|
|
357
|
+
}
|
|
358
|
+
})
|
|
359
|
+
.catch(() => {
|
|
360
|
+
/* 剪贴板不可用(非安全上下文等):静默,不弹错 */
|
|
361
|
+
});
|
|
362
|
+
}, [viewing, dir, rootCache, listSessionId]);
|
|
363
|
+
/** 内联展开/收起某目录(点 ▸):只切展开态,不导航;首次展开才拉子项。 */
|
|
364
|
+
const toggleDir = useCallback((path) => {
|
|
365
|
+
setOpenDirs((prev) => {
|
|
366
|
+
const next = new Set(prev);
|
|
367
|
+
if (next.has(path))
|
|
368
|
+
next.delete(path);
|
|
369
|
+
else
|
|
370
|
+
next.add(path);
|
|
371
|
+
return next;
|
|
372
|
+
});
|
|
373
|
+
if (!(path in childCache)) {
|
|
374
|
+
setChildCache((prev) => ({ ...prev, [path]: { status: 'loading' } }));
|
|
375
|
+
props.workspaceFiles
|
|
376
|
+
.list(listSessionId, listPathOf(path))
|
|
377
|
+
.then((result) => {
|
|
378
|
+
const parsed = listingOf(result);
|
|
379
|
+
setChildCache((prev) => ({
|
|
380
|
+
...prev,
|
|
381
|
+
[path]: isFailed(parsed) || parsed === null
|
|
382
|
+
? { status: 'error', error: errView(isFailed(parsed) ? parsed.failed : undefined) }
|
|
383
|
+
: { status: 'ready', entries: parsed.entries, truncated: parsed.truncated },
|
|
384
|
+
}));
|
|
385
|
+
})
|
|
386
|
+
.catch((error) => {
|
|
387
|
+
setChildCache((prev) => ({ ...prev, [path]: { status: 'error', error: errView(error) } }));
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
}, [childCache, props.workspaceFiles, listSessionId]);
|
|
391
|
+
// 过滤点名了扩展名 ⇒ 要的是**文件**:目录只作导航(点行就地展开),不给「选择」、也回传不了 isDir。
|
|
392
|
+
// (旧实现「目录永远保留且永远带选择钮」,于是「仅 JS/MD」下出现「文件夹旁边有选择、
|
|
393
|
+
// MD 文件旁边反而没有」,选中还回传一个文件夹——与过滤语义直接打架。)
|
|
394
|
+
const extsOnly = picker && props.filter !== undefined && props.filter.dirs !== true && (props.filter.exts?.length ?? 0) > 0;
|
|
395
|
+
/** 选择器里目录是否可选:不限 或 明说 dirs:true 才可选;扩展名过滤下不可选。 */
|
|
396
|
+
const dirPickable = picker && props.onPick !== undefined && !extsOnly;
|
|
397
|
+
/** 递归渲染目录树(内联展开)。file = 点开预览(picker = 选中);dir = ▸ 切展开、行尾钮分派。 */
|
|
398
|
+
const renderTree = useCallback((entries, baseDir) => sortEntries(entries).map((entry) => {
|
|
399
|
+
const childPath = joinPath(baseDir, entry.name);
|
|
400
|
+
const isDir = entry.type === 'directory';
|
|
401
|
+
// 选择器过滤:不匹配的文件不渲染(目录永远保留——里面可能有匹配的)。
|
|
402
|
+
if (!isDir && picker && !matchesFilter(childPath, false, props.filter))
|
|
403
|
+
return null;
|
|
404
|
+
if (!isDir) {
|
|
405
|
+
const pick = () => {
|
|
406
|
+
if (picker && props.onPick !== undefined) {
|
|
407
|
+
// 回传**工作区相对**路径(源实现真机 bug:绝对路径被宿主 zod 拒)。
|
|
408
|
+
props.onPick(relativizeToRoot(rootCache, listSessionId, childPath), false);
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
// 逐层展开后选中文件:面包屑要跟随到该文件所在目录。
|
|
412
|
+
const parent = dirnameOf(childPath);
|
|
413
|
+
if (parent !== dir)
|
|
414
|
+
loadDir(parent);
|
|
415
|
+
setViewing(childPath);
|
|
416
|
+
setReloadNonce(0);
|
|
417
|
+
setSourceView(false);
|
|
418
|
+
};
|
|
419
|
+
return h('div', {
|
|
420
|
+
key: childPath,
|
|
421
|
+
className: `dshpc-fb__row${picker ? ' dshpc-fb__row--pick' : ''}`,
|
|
422
|
+
role: 'button',
|
|
423
|
+
tabIndex: 0,
|
|
424
|
+
title: picker ? tt('explorerPickFile') : childPath,
|
|
425
|
+
onClick: pick,
|
|
426
|
+
onKeyDown: (event) => {
|
|
427
|
+
if (event.key === 'Enter' || event.key === ' ')
|
|
428
|
+
pick();
|
|
429
|
+
},
|
|
430
|
+
}, h('span', { className: 'dshpc-fb__row-icon' }, h(FileTypeIcon, { path: childPath, size: 18 })), h('span', { className: 'dshpc-fb__row-name' }, entry.name),
|
|
431
|
+
// 选择器里文件行也给一枚「选择」钮,与目录行的操作位对齐(目录没有时它仍在,
|
|
432
|
+
// 「点行 = 选中」的旧默契保持不变,外显入口不再缺)。
|
|
433
|
+
picker && props.onPick !== undefined
|
|
434
|
+
? h(Button, {
|
|
435
|
+
variant: 'outline',
|
|
436
|
+
size: 'sm',
|
|
437
|
+
className: 'dshpc-fb__row-act',
|
|
438
|
+
'aria-label': tt('explorerPickFile'),
|
|
439
|
+
onClick: (event) => {
|
|
440
|
+
event.stopPropagation();
|
|
441
|
+
pick();
|
|
442
|
+
},
|
|
443
|
+
}, tt('explorerPickDir'))
|
|
444
|
+
: null);
|
|
445
|
+
}
|
|
446
|
+
const cached = childCache[childPath];
|
|
447
|
+
const isOpen = openDirs.has(childPath);
|
|
448
|
+
// 目录行点击 = 就地展开/收起(两种形态统一,源实现用户 2026-10-10 拍板);
|
|
449
|
+
// 主动作挪到行尾小按钮:选择器 = 选择此文件夹,浏览 = 进入该目录。
|
|
450
|
+
// 目录行不渲染文件夹图标——行首已有展开小尖号,再摆一个是重复。
|
|
451
|
+
const rowAction = () => {
|
|
452
|
+
toggleDir(childPath);
|
|
453
|
+
};
|
|
454
|
+
const actThisDir = (event) => {
|
|
455
|
+
event.stopPropagation();
|
|
456
|
+
if (picker) {
|
|
457
|
+
// 扩展名过滤下这里不该被触发(目录行根本不渲染这枚钮);兜住是为了语义不被绕过。
|
|
458
|
+
if (!extsOnly && props.onPick !== undefined)
|
|
459
|
+
props.onPick(relativizeToRoot(rootCache, listSessionId, childPath), true);
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
loadDir(childPath);
|
|
463
|
+
};
|
|
464
|
+
return h(Fragment, { key: childPath }, h('div', {
|
|
465
|
+
className: 'dshpc-fb__row',
|
|
466
|
+
role: 'button',
|
|
467
|
+
tabIndex: 0,
|
|
468
|
+
title: childPath,
|
|
469
|
+
onClick: rowAction,
|
|
470
|
+
onKeyDown: (event) => {
|
|
471
|
+
if (event.key === 'Enter' || event.key === ' ')
|
|
472
|
+
rowAction();
|
|
473
|
+
},
|
|
474
|
+
},
|
|
475
|
+
// 展开 / 收起:核心 IconButton,20×20 命中位 + 展开时转 90°(与兄弟 .dsh-tdt-sv-tree-toggle 同款)。
|
|
476
|
+
h(IconButton, {
|
|
477
|
+
variant: 'plain',
|
|
478
|
+
size: 'sm',
|
|
479
|
+
className: `dshpc-fb__row-toggle${isOpen ? ' dshpc-fb__row-toggle--open' : ''}`,
|
|
480
|
+
icon: h(IconChevronRightOutlineRegular, { size: 16 }),
|
|
481
|
+
label: isOpen ? tt('explorerCollapse') : tt('explorerExpand'),
|
|
482
|
+
'aria-expanded': isOpen,
|
|
483
|
+
onClick: (event) => {
|
|
484
|
+
event.stopPropagation();
|
|
485
|
+
toggleDir(childPath);
|
|
486
|
+
},
|
|
487
|
+
}), h('span', { className: 'dshpc-fb__row-name' }, entry.name),
|
|
488
|
+
// picker:仅目录可选时才出「选择」(扩展名过滤下没有,目录只作导航);
|
|
489
|
+
// 浏览态:出「进入」。
|
|
490
|
+
picker
|
|
491
|
+
? dirPickable
|
|
492
|
+
? h(Button, {
|
|
493
|
+
variant: 'outline',
|
|
494
|
+
size: 'sm',
|
|
495
|
+
className: 'dshpc-fb__row-act',
|
|
496
|
+
'aria-label': tt('explorerPickDirAria'),
|
|
497
|
+
onClick: actThisDir,
|
|
498
|
+
}, tt('explorerPickDir'))
|
|
499
|
+
: null
|
|
500
|
+
: h(Button, {
|
|
501
|
+
variant: 'outline',
|
|
502
|
+
size: 'sm',
|
|
503
|
+
className: 'dshpc-fb__row-act',
|
|
504
|
+
'aria-label': tt('explorerEnter'),
|
|
505
|
+
onClick: actThisDir,
|
|
506
|
+
}, tt('explorerEnter'))), isOpen
|
|
507
|
+
? h('div', { className: 'dshpc-fb__row-children' },
|
|
508
|
+
// 硬性规定:不新建 Loading、不写加载文案 ⇒ 还在拉就空着(源实现用户 2026-10-05)。
|
|
509
|
+
cached === undefined || cached.status === 'loading'
|
|
510
|
+
? null
|
|
511
|
+
: cached.status === 'error'
|
|
512
|
+
? h('div', { className: 'dshpc-fb__row-err' }, tt(cached.error.key, cached.error.params))
|
|
513
|
+
: h(Fragment, null, renderTree(cached.entries, childPath), cached.truncated ? h('div', { className: 'dshpc-fb__row-note' }, tt('explorerTruncated')) : null))
|
|
514
|
+
: null);
|
|
515
|
+
}), [childCache, openDirs, picker, dirPickable, extsOnly, props.filter, props.onPick, listSessionId, rootCache, dir, loadDir, tt]);
|
|
516
|
+
// 面包屑 = 工作区根 + 其下相对层级。根名优先级:**匹配出的工作区名**(当前路径所属,
|
|
517
|
+
// 不写死)> 调用方 rootName > 缓存根末段。学根/相对化一律按 listSessionId——
|
|
518
|
+
// 用调用方原始 sessionId 学根会学出别的根 ⇒ 相对化失败 ⇒ 露出工作区外层级(真机 2026-10-22)。
|
|
519
|
+
const rootAbs = rootCache.get(listSessionId);
|
|
520
|
+
const rootLabel = anchorMatch?.title ?? props.rootName ?? (rootAbs !== undefined ? baseNameOfPath(rootAbs.replace(/\/+$/, '')) : '');
|
|
521
|
+
const crumbs = rootLabel !== '' ? [{ label: rootLabel, path: '' }, ...crumbsOfDir(relativizeToRoot(rootCache, listSessionId, dir))] : crumbsOfDir(dir);
|
|
522
|
+
// md / html 都出「渲染 ⇄ 源码」切换(默认渲染)。
|
|
523
|
+
const viewingIsMd = viewing !== null && /\.(md|markdown)$/i.test(viewing);
|
|
524
|
+
const viewingIsHtml = viewing !== null && /\.(html|htm)$/i.test(viewing);
|
|
525
|
+
const switchable = viewingIsMd || viewingIsHtml;
|
|
526
|
+
const menuItems = crumbsMenuEntries({
|
|
527
|
+
workspaces: props.workspaces,
|
|
528
|
+
rootName: rootLabel,
|
|
529
|
+
rootFallbackLabel: tt('explorerRootName'),
|
|
530
|
+
crumbs,
|
|
531
|
+
onPickWorkspace: props.onSelectWorkspace,
|
|
532
|
+
onLoadDir: loadDir,
|
|
533
|
+
});
|
|
534
|
+
/** 一段面包屑(核心 ghost 钮:无底无边;度量条与可见态必须逐字同构)。 */
|
|
535
|
+
const crumbButton = (label, onClick, current = false) => h(Button, {
|
|
536
|
+
variant: 'ghost',
|
|
537
|
+
size: 'sm',
|
|
538
|
+
className: `dshpc-fb__crumb${current ? ' dshpc-fb__crumb--current' : ''}`,
|
|
539
|
+
disabled: onClick === undefined,
|
|
540
|
+
tabIndex: onClick === undefined ? -1 : 0,
|
|
541
|
+
onClick,
|
|
542
|
+
}, h(MarqueeText, { text: label, className: 'dshpc-fb__crumb-mq' }));
|
|
543
|
+
const crumbBar = h('nav', { className: 'dshpc-fb__bar', 'aria-label': tt('explorerCrumbsAria') }, h('div', { className: 'dshpc-fb__menu-wrap' }, h(IconButton, {
|
|
544
|
+
variant: 'plain',
|
|
545
|
+
size: 'md',
|
|
546
|
+
icon: h(IconChevronDownOutlineRegular, { size: 14 }),
|
|
547
|
+
label: tt('explorerLevels'),
|
|
548
|
+
'aria-expanded': menuOpen,
|
|
549
|
+
onClick: () => setMenuOpen((v) => !v),
|
|
550
|
+
}),
|
|
551
|
+
// 选层下拉 = 自写浮层(抄兄弟 .dsh-tdt-sv-crumbs-*):透明遮罩点一下即收,
|
|
552
|
+
// 遮罩层序高于 picker 面板(1100)⇒ 菜单开着时点面板也算「点外部」。
|
|
553
|
+
menuOpen
|
|
554
|
+
? h(Fragment, null, h('div', {
|
|
555
|
+
className: 'dshpc-fb__crumbs-backdrop',
|
|
556
|
+
onClick: () => setMenuOpen(false),
|
|
557
|
+
}), h('div', { className: 'dshpc-fb__crumbs-menu', role: 'menu' }, menuItems))
|
|
558
|
+
: null), h('div', { ref: regionRef, className: 'dshpc-fb__crumbs' },
|
|
559
|
+
// 隐藏测量条:与可见态完全同构的 crumb 按钮,确保溢出判定精确。
|
|
560
|
+
h('span', { ref: measureRef, className: 'dshpc-fb__crumbs-measure', 'aria-hidden': true }, crumbs.length === 0
|
|
561
|
+
? crumbButton(tt('explorerRootName'))
|
|
562
|
+
: crumbs.map((crumb, index) => h(Fragment, { key: crumb.path }, index > 0 ? h(IconChevronRightOutlineRegular, { className: 'dshpc-fb__crumb-sep', size: 12 }) : null, crumbButton(crumb.label)))),
|
|
563
|
+
// 工作区根目录(crumbs 空)也要有可见的面包屑占位。
|
|
564
|
+
crumbsOverflow
|
|
565
|
+
? crumbButton(crumbs.length > 0 ? crumbs[crumbs.length - 1]?.label ?? tt('explorerRootName') : tt('explorerRootName'), undefined, true)
|
|
566
|
+
: crumbs.length === 0
|
|
567
|
+
? crumbButton(tt('explorerRootName'), undefined, true)
|
|
568
|
+
: crumbs.map((crumb, index) => h(Fragment, { key: crumb.path }, index > 0 ? h(IconChevronRightOutlineRegular, { className: 'dshpc-fb__crumb-sep', size: 12 }) : null, crumbButton(crumb.label, () => loadDir(crumb.path))))), h('div', { className: 'dshpc-fb__actions' }, props.onClose === undefined
|
|
569
|
+
? null
|
|
570
|
+
: h(IconButton, {
|
|
571
|
+
variant: 'plain',
|
|
572
|
+
size: 'md',
|
|
573
|
+
icon: h(IconCloseOutlineRegular, { size: 14 }),
|
|
574
|
+
label: tt('previewClose'),
|
|
575
|
+
onClick: props.onClose,
|
|
576
|
+
})));
|
|
577
|
+
// 越界(outside-workspace):第一排整条换成只读完整路径,不给任何会失败的入口。
|
|
578
|
+
const crumbBarPlain = h('nav', { className: 'dshpc-fb__bar', 'aria-label': tt('explorerCrumbsAria') }, h(MarqueeText, {
|
|
579
|
+
text: dir,
|
|
580
|
+
title: dir,
|
|
581
|
+
className: 'dshpc-fb__bar-plain',
|
|
582
|
+
style: { flex: '1 1 auto', minWidth: 0 },
|
|
583
|
+
}), h('div', { className: 'dshpc-fb__actions' }, props.onClose === undefined
|
|
584
|
+
? null
|
|
585
|
+
: h(IconButton, {
|
|
586
|
+
variant: 'plain',
|
|
587
|
+
size: 'md',
|
|
588
|
+
icon: h(IconCloseOutlineRegular, { size: 14 }),
|
|
589
|
+
label: tt('previewClose'),
|
|
590
|
+
onClick: props.onClose,
|
|
591
|
+
})));
|
|
592
|
+
// 第二排(仅预览态显示):文件名 + 渲染⇄源码切换 / 复制路径 / 刷新。
|
|
593
|
+
// 档位照兄弟项目调用点:Segmented `sm` + 图标钮 `md`(核心 Segmented 高度由 CSS 定,不随档变)。
|
|
594
|
+
const titleBar = viewing === null
|
|
595
|
+
? null
|
|
596
|
+
: h('div', { className: 'dshpc-fb__title' }, h(MarqueeText, {
|
|
597
|
+
text: baseNameOfPath(viewing),
|
|
598
|
+
title: viewing,
|
|
599
|
+
className: 'dshpc-fb__title-inner',
|
|
600
|
+
style: { flex: '1 1 auto', minWidth: 0 },
|
|
601
|
+
}), h('div', { className: 'dshpc-fb__actions' }, switchable
|
|
602
|
+
? h(Segmented, {
|
|
603
|
+
size: 'sm',
|
|
604
|
+
variant: 'default',
|
|
605
|
+
label: viewingIsMd ? tt('previewMdSwitchAria') : tt('previewHtmlSwitchAria'),
|
|
606
|
+
value: sourceView ? 'source' : 'render',
|
|
607
|
+
items: [
|
|
608
|
+
{ value: 'render', label: tt('previewRender') },
|
|
609
|
+
{ value: 'source', label: tt('previewSource') },
|
|
610
|
+
],
|
|
611
|
+
onChange: (next) => setSourceView(next === 'source'),
|
|
612
|
+
})
|
|
613
|
+
: null, h(IconButton, {
|
|
614
|
+
variant: 'plain',
|
|
615
|
+
size: 'md',
|
|
616
|
+
icon: copied ? h(IconCheckOutlineRegular, { size: 14 }) : h(IconCopyOutlineRegular, { size: 14 }),
|
|
617
|
+
label: tt('previewCopyPath'),
|
|
618
|
+
onClick: copyPath,
|
|
619
|
+
}), h(IconButton, {
|
|
620
|
+
variant: 'plain',
|
|
621
|
+
size: 'md',
|
|
622
|
+
icon: h(IconRefreshOutlineRegular, { size: 14 }),
|
|
623
|
+
label: tt('previewRefresh'),
|
|
624
|
+
onClick: reload,
|
|
625
|
+
})));
|
|
626
|
+
// ── 主体 ──
|
|
627
|
+
let body;
|
|
628
|
+
if (viewing !== null) {
|
|
629
|
+
body = h(FilePreview, {
|
|
630
|
+
key: `${viewing}:${reloadNonce}`,
|
|
631
|
+
workspaceFiles: props.workspaceFiles,
|
|
632
|
+
officeToPdf: props.officeToPdf ?? null,
|
|
633
|
+
sessionId: listSessionId,
|
|
634
|
+
path: viewing,
|
|
635
|
+
sourceView,
|
|
636
|
+
translate: tt,
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
else if (mode === 'error' && listErr !== null) {
|
|
640
|
+
// 列举失败(如 outside-workspace):只给错误文案;关闭(✕)即退出。
|
|
641
|
+
body = h('div', { className: 'dshpc-fb__body' }, h('div', { className: 'dshpc-fb__err' }, tt(listErr.key, listErr.params)));
|
|
642
|
+
}
|
|
643
|
+
else if (listing !== null) {
|
|
644
|
+
const visible = listing.filter((entry) => entry.type === 'directory' || matchesFilter(joinPath(dir, entry.name), false, picker ? props.filter : undefined));
|
|
645
|
+
body =
|
|
646
|
+
visible.length === 0
|
|
647
|
+
? h('div', { className: 'dshpc-fb__body' }, h('div', { className: 'dshpc-fb__note' }, tt('explorerEmptyDir')))
|
|
648
|
+
: h('div', { className: 'dshpc-fb__tree' }, renderTree(visible, dir), truncated ? h('div', { className: 'dshpc-fb__row-note' }, tt('explorerTruncated')) : null);
|
|
649
|
+
}
|
|
650
|
+
else {
|
|
651
|
+
// 不动 Loading、不写加载文案 ⇒ 空着(源实现硬性规定)。
|
|
652
|
+
body = h('div', { className: 'dshpc-fb__body' });
|
|
653
|
+
}
|
|
654
|
+
return h('div', {
|
|
655
|
+
className: 'dshpc-fb',
|
|
656
|
+
style: props.style ?? undefined,
|
|
657
|
+
}, outside ? crumbBarPlain : crumbBar, titleBar, body);
|
|
658
|
+
}
|
|
659
|
+
//# sourceMappingURL=FileBrowser.js.map
|