@mcp-b/react-components 0.17.0 → 0.18.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/dist/{AgentActivity-BNUNrP9O.d.ts → AgentActivity-D2SHSGFX.d.ts} +1 -1
- package/dist/{AgentChat-CRuMi1Pb.d.ts → AgentChat-ClxViuUM.d.ts} +1 -20
- package/dist/{Button-FX_hpqxm.d.ts → Button-DLGzHsjc.d.ts} +13 -7
- package/dist/{CodeBlock-Co1noLbC.js → CodeBlock-Bfp5PAg3.js} +9 -6
- package/dist/{CodeBlock-Dnpr88ZQ.d.ts → CodeBlock-gV4kZeCI.d.ts} +2 -9
- package/dist/{FileTree-B7bBsUyU.js → FileTree-3zYtrddG.js} +42 -16
- package/dist/{Message-C4eYRogZ.js → Message-BXtmfc9Q.js} +13 -20
- package/dist/{ModelSelector-BNrZlY_Y.js → ModelSelector-Cqw_qrUG.js} +15 -22
- package/dist/{PromptInput-OLfXPuHM.d.ts → PromptInput-Bdh9pqlo.d.ts} +4 -6
- package/dist/{Select-CwSqP07Y.d.ts → Select-B_13gg_J.d.ts} +2 -2
- package/dist/{Tabs-9zddXDQo.d.ts → Tabs-ColWK-XA.d.ts} +10 -2
- package/dist/{ThinkChat-DInElus-.js → ThinkChat-15EhfvYJ.js} +84 -61
- package/dist/{ThinkChatActivity-BgesTfZ4.d.ts → ThinkChatActivity-Bzot45kX.d.ts} +4 -7
- package/dist/{ThinkChatActivitySummary-BPmu9a-c.d.ts → ThinkChatActivitySummary-D1xOrNrq.d.ts} +1 -1
- package/dist/{Toggle-DaXpe1Lc.d.ts → Toggle-JKZ43D5S.d.ts} +1 -1
- package/dist/{ToggleGroup-yseEWkOC.d.ts → ToggleGroup-DNd6cDJZ.d.ts} +2 -2
- package/dist/Workspace-Jez8ffUj.d.ts +274 -0
- package/dist/{WorkspaceExplorer-sZewzT2O.d.ts → WorkspaceExplorer-B9dQWNVK.d.ts} +2 -2
- package/dist/{WorkspaceFile-C8UzuSH4.d.ts → WorkspaceFile-CzyHnESE.d.ts} +1 -1
- package/dist/{WorkspaceFileView-DNdGAEiX.js → WorkspaceFileView-C68Ub8S8.js} +120 -127
- package/dist/{WorkspaceLink-B1ykV1dS.js → WorkspaceLink-BYYeTQLf.js} +6 -27
- package/dist/{WorkspaceLink-DK1DWMHe.d.ts → WorkspaceLink-O-EBKUJ8.d.ts} +6 -9
- package/dist/{class-names-Bk-QLWWI.js → class-names-BiMDVpUo.js} +4 -1
- package/dist/components/Accordion.js +7 -2
- package/dist/components/Activity.js +2 -2
- package/dist/components/AgentActivity.d.ts +1 -1
- package/dist/components/AgentActivity.js +4 -5
- package/dist/components/AgentActivityPreview.d.ts +9 -9
- package/dist/components/AgentActivityPreview.js +8 -12
- package/dist/components/AgentAttention.d.ts +1 -1
- package/dist/components/AgentAttention.js +1 -1
- package/dist/components/AgentChat.d.ts +1 -1
- package/dist/components/AgentChat.js +94 -62
- package/dist/components/AgentChatMessageEditor.js +2 -2
- package/dist/components/AgentChatTurnError.js +2 -2
- package/dist/components/AgentConnection.js +3 -3
- package/dist/components/AgentMessageParts.js +1 -1
- package/dist/components/AgentToolDetail.d.ts +6 -4
- package/dist/components/AgentToolDetail.js +12 -10
- package/dist/components/AlertDialog.js +1 -1
- package/dist/components/AppTopBar.d.ts +24 -7
- package/dist/components/AppTopBar.js +32 -8
- package/dist/components/Approval.js +4 -4
- package/dist/components/Artifact.d.ts +1 -1
- package/dist/components/Artifact.js +6 -4
- package/dist/components/Autocomplete.js +1 -1
- package/dist/components/Avatar.d.ts +1 -1
- package/dist/components/Avatar.js +2 -2
- package/dist/components/Badge.js +3 -0
- package/dist/components/BrowserLiveView.js +27 -12
- package/dist/components/BrowserReplay.js +4 -3
- package/dist/components/BrowserRunPanel.js +5 -5
- package/dist/components/Button.d.ts +1 -1
- package/dist/components/Button.js +14 -11
- package/dist/components/Card.d.ts +7 -8
- package/dist/components/Card.js +19 -10
- package/dist/components/ChainOfThought.d.ts +1 -1
- package/dist/components/ChainOfThought.js +2 -2
- package/dist/components/ChatGptSignInScreen.js +8 -8
- package/dist/components/Checkbox.js +2 -2
- package/dist/components/CheckboxGroup.js +1 -1
- package/dist/components/Checkpoint.js +2 -2
- package/dist/components/CodeBlock.d.ts +1 -1
- package/dist/components/CodeBlock.js +1 -1
- package/dist/components/CodexModelSelector.js +2 -2
- package/dist/components/Collapsible.js +2 -2
- package/dist/components/Combobox.js +1 -1
- package/dist/components/Commit.d.ts +1 -1
- package/dist/components/Commit.js +3 -3
- package/dist/components/Context.js +1 -1
- package/dist/components/ContextMenu.js +2 -2
- package/dist/components/Conversation.d.ts +1 -1
- package/dist/components/Conversation.js +5 -4
- package/dist/components/DescriptionListParts.js +1 -1
- package/dist/components/Dialog.js +1 -1
- package/dist/components/EnvironmentVariables.d.ts +1 -1
- package/dist/components/EnvironmentVariables.js +3 -3
- package/dist/components/Field.js +5 -2
- package/dist/components/Fieldset.js +1 -1
- package/dist/components/FileTree.d.ts +6 -0
- package/dist/components/FileTree.js +1 -1
- package/dist/components/Form.js +1 -1
- package/dist/components/GithubMotionMark.js +1 -1
- package/dist/components/IconButton.d.ts +2 -3
- package/dist/components/IconButton.js +2 -1
- package/dist/components/InlineCitation.js +1 -1
- package/dist/components/Input.js +1 -1
- package/dist/components/Marker.js +1 -1
- package/dist/components/McpConnectionRequest.js +2 -2
- package/dist/components/McpServerPicker.js +2 -2
- package/dist/components/MediaPreviewCard.d.ts +103 -0
- package/dist/components/MediaPreviewCard.js +193 -0
- package/dist/components/Menu.js +1 -1
- package/dist/components/Menubar.js +2 -2
- package/dist/components/Message.d.ts +4 -1
- package/dist/components/Message.js +1 -1
- package/dist/components/MessageScroller.d.ts +1 -1
- package/dist/components/MessageScroller.js +5 -4
- package/dist/components/Meter.js +1 -1
- package/dist/components/ModelSelector.d.ts +4 -3
- package/dist/components/ModelSelector.js +1 -1
- package/dist/components/NavigationMenu.js +1 -1
- package/dist/components/NumberField.js +1 -1
- package/dist/components/Plan.js +2 -2
- package/dist/components/Popover.js +1 -1
- package/dist/components/PreviewCard.js +1 -1
- package/dist/components/PricingBillingToggle.js +1 -1
- package/dist/components/PricingFAQ.js +1 -1
- package/dist/components/PricingPage.js +2 -1
- package/dist/components/PricingPlanCard.js +4 -3
- package/dist/components/PricingUsageCalculator.js +2 -2
- package/dist/components/Progress.js +1 -1
- package/dist/components/PromptInput.d.ts +1 -1
- package/dist/components/PromptInput.js +33 -20
- package/dist/components/Queue.d.ts +1 -1
- package/dist/components/Queue.js +2 -3
- package/dist/components/RadioGroup.js +1 -1
- package/dist/components/Reasoning.js +3 -6
- package/dist/components/ResizableSidePanel.js +1 -1
- package/dist/components/ResponsiveSidebarShell.js +16 -10
- package/dist/components/ScrollArea.js +2 -1
- package/dist/components/SearchField.d.ts +43 -0
- package/dist/components/SearchField.js +52 -0
- package/dist/components/Select.d.ts +1 -1
- package/dist/components/Select.js +5 -6
- package/dist/components/Separator.js +1 -1
- package/dist/components/Slider.js +1 -1
- package/dist/components/Sources.js +2 -2
- package/dist/components/Stepper.js +16 -9
- package/dist/components/SubagentRunDetails.js +1 -1
- package/dist/components/SubagentRunIndicator.js +2 -2
- package/dist/components/SubagentRunList.d.ts +3 -5
- package/dist/components/SubagentRunList.js +3 -4
- package/dist/components/SubagentRunSelector.d.ts +8 -12
- package/dist/components/SubagentRunSelector.js +17 -20
- package/dist/components/Switch.d.ts +2 -2
- package/dist/components/Switch.js +4 -5
- package/dist/components/Tabs.d.ts +2 -2
- package/dist/components/Tabs.js +12 -3
- package/dist/components/Task.js +2 -2
- package/dist/components/Terminal.d.ts +2 -2
- package/dist/components/Terminal.js +5 -4
- package/dist/components/TestResults.js +10 -6
- package/dist/components/Textarea.js +1 -1
- package/dist/components/ThemeModeControl.js +1 -1
- package/dist/components/ThinkChat.d.ts +33 -20
- package/dist/components/ThinkChat.js +4 -4
- package/dist/components/ThinkChatActivity.d.ts +2 -2
- package/dist/components/ThinkChatActivity.js +8 -10
- package/dist/components/ThinkChatActivitySummary.d.ts +1 -1
- package/dist/components/ThinkChatActivitySummary.js +7 -3
- package/dist/components/ThinkChatToolPanels.js +26 -16
- package/dist/components/ThinkExecutionDetail.js +2 -2
- package/dist/components/ThinkExecutionStatus.js +2 -2
- package/dist/components/ThinkExecutionWorkbench.d.ts +30 -8
- package/dist/components/ThinkExecutionWorkbench.js +70 -134
- package/dist/components/ThinkFullApp.d.ts +36 -35
- package/dist/components/ThinkFullApp.js +156 -109
- package/dist/components/ThinkFullAppViewSwitch.d.ts +1 -1
- package/dist/components/ThinkFullAppViewSwitch.js +1 -1
- package/dist/components/ThinkRecoveryNotice.js +18 -12
- package/dist/components/ThinkRoutineBrowser.d.ts +17 -9
- package/dist/components/ThinkRoutineBrowser.js +33 -40
- package/dist/components/ThinkRoutineDetail.d.ts +5 -2
- package/dist/components/ThinkRoutineDetail.js +4 -4
- package/dist/components/ThinkRoutineForm.d.ts +3 -2
- package/dist/components/ThinkRoutineForm.js +11 -10
- package/dist/components/ThinkRoutineLibrary.d.ts +24 -31
- package/dist/components/ThinkRoutineLibrary.js +57 -80
- package/dist/components/ThinkRoutineRuns.d.ts +3 -2
- package/dist/components/ThinkRoutineRuns.js +31 -24
- package/dist/components/ThinkThreadPicker.d.ts +17 -11
- package/dist/components/ThinkThreadPicker.js +163 -39
- package/dist/components/Toast.js +1 -1
- package/dist/components/Toggle.d.ts +1 -1
- package/dist/components/Toggle.js +9 -7
- package/dist/components/ToggleGroup.d.ts +1 -1
- package/dist/components/ToggleGroup.js +15 -13
- package/dist/components/ToolInvocation.js +1 -1
- package/dist/components/Toolbar.js +1 -1
- package/dist/components/Tooltip.d.ts +8 -1
- package/dist/components/Tooltip.js +11 -2
- package/dist/components/TransitionSurface.d.ts +27 -0
- package/dist/components/TransitionSurface.js +20 -0
- package/dist/components/WebPreview.d.ts +5 -5
- package/dist/components/WebPreview.js +68 -54
- package/dist/components/Workspace.d.ts +2 -2
- package/dist/components/Workspace.js +358 -328
- package/dist/components/WorkspaceBrowser.d.ts +4 -7
- package/dist/components/WorkspaceBrowser.js +15 -25
- package/dist/components/WorkspaceExplorer.d.ts +5 -5
- package/dist/components/WorkspaceExplorer.js +19 -32
- package/dist/components/WorkspaceFile.d.ts +1 -1
- package/dist/components/WorkspaceFileView.d.ts +14 -2
- package/dist/components/WorkspaceFileView.js +1 -1
- package/dist/components/WorkspaceHeader.d.ts +0 -8
- package/dist/components/WorkspaceHeader.js +3 -15
- package/dist/components/WorkspaceLink.d.ts +1 -1
- package/dist/components/WorkspaceLink.js +1 -1
- package/dist/components/icons.d.ts +2 -2
- package/dist/components/icons.js +2 -2
- package/dist/{copy-button-CQx3LQAC.d.ts → copy-button-D7aAYA-2.d.ts} +1 -1
- package/dist/{copy-button-cYsJBauI.js → copy-button-DeK6KXEV.js} +3 -2
- package/dist/{icons--FqCZEc3.js → icons-CQJ-aMnI.js} +6 -3
- package/dist/{icons-DpUqY8Mp.d.ts → icons-CYXmvKlJ.d.ts} +17 -2
- package/dist/navigation-link-Cob7KBlM.js +19 -0
- package/dist/styles/accordion.css +4 -19
- package/dist/styles/activity.css +48 -56
- package/dist/styles/agent-activity-preview.css +2 -0
- package/dist/styles/agent-chat.css +14 -22
- package/dist/styles/alert-dialog.css +16 -18
- package/dist/styles/app-top-bar.css +17 -9
- package/dist/styles/approval.css +11 -6
- package/dist/styles/artifact.css +6 -6
- package/dist/styles/autocomplete.css +2 -1
- package/dist/styles/avatar.css +35 -30
- package/dist/styles/badge.css +10 -10
- package/dist/styles/base-ui-motion.css +94 -39
- package/dist/styles/browser-control-overlay.css +0 -1
- package/dist/styles/browser-live-view.css +39 -22
- package/dist/styles/browser-replay.css +1 -1
- package/dist/styles/browser-run-panel.css +8 -4
- package/dist/styles/button.css +36 -33
- package/dist/styles/card.css +5 -6
- package/dist/styles/chain-of-thought.css +22 -16
- package/dist/styles/checkbox.css +18 -5
- package/dist/styles/codex-model-selector.css +3 -7
- package/dist/styles/collapsible.css +10 -10
- package/dist/styles/combobox.css +3 -2
- package/dist/styles/commit.css +19 -18
- package/dist/styles/composition-motion.css +83 -0
- package/dist/styles/context-menu.css +3 -3
- package/dist/styles/context.css +11 -10
- package/dist/styles/conversation.css +18 -23
- package/dist/styles/dialog.css +15 -15
- package/dist/styles/field.css +32 -12
- package/dist/styles/file-tree.css +69 -9
- package/dist/styles/form.css +1 -1
- package/dist/styles/index.css +5 -0
- package/dist/styles/input.css +2 -2
- package/dist/styles/marker.css +14 -13
- package/dist/styles/mcp-server-picker.css +20 -17
- package/dist/styles/media-preview-card.css +160 -0
- package/dist/styles/menu.css +11 -11
- package/dist/styles/menubar.css +12 -11
- package/dist/styles/message.css +27 -21
- package/dist/styles/meter.css +2 -2
- package/dist/styles/model-selector.css +16 -14
- package/dist/styles/motion-reduction.css +29 -0
- package/dist/styles/navigation-menu.css +14 -30
- package/dist/styles/number-field.css +22 -4
- package/dist/styles/plan.css +5 -0
- package/dist/styles/popover.css +15 -16
- package/dist/styles/preview-card.css +2 -32
- package/dist/styles/progress.css +8 -3
- package/dist/styles/prompt-input.css +75 -75
- package/dist/styles/queue.css +48 -32
- package/dist/styles/radio.css +19 -12
- package/dist/styles/reasoning.css +2 -1
- package/dist/styles/recipes.css +109 -37
- package/dist/styles/resizable-side-panel.css +4 -10
- package/dist/styles/responsive-sidebar-shell.css +16 -15
- package/dist/styles/scroll-area.css +32 -15
- package/dist/styles/search-field.css +21 -0
- package/dist/styles/select.css +19 -12
- package/dist/styles/slider.css +25 -29
- package/dist/styles/sources.css +3 -3
- package/dist/styles/stepper.css +36 -26
- package/dist/styles/subagent-run.css +1 -1
- package/dist/styles/switch.css +32 -29
- package/dist/styles/tabs.css +79 -23
- package/dist/styles/task.css +13 -9
- package/dist/styles/test-results.css +29 -23
- package/dist/styles/theme-mode-control.css +8 -0
- package/dist/styles/think-execution-workbench.css +18 -15
- package/dist/styles/think-full-app.css +45 -68
- package/dist/styles/think-routine-library.css +43 -24
- package/dist/styles/think-thread-picker.css +193 -51
- package/dist/styles/toast.css +48 -27
- package/dist/styles/toggle-group.css +2 -36
- package/dist/styles/toggle.css +29 -5
- package/dist/styles/toolbar.css +25 -27
- package/dist/styles/tooltip.css +2 -32
- package/dist/styles/transition-surface.css +55 -0
- package/dist/styles/workspace-explorer.css +99 -44
- package/dist/summarize-tool-input-CqTcGRYy.js +15 -0
- package/dist/utils/mcp-server-catalog.d.ts +132 -0
- package/dist/utils/mcp-server-catalog.js +90 -0
- package/dist/utils/structured-code.d.ts +1 -1
- package/package.json +13 -7
- package/dist/Workspace-C9RFoxcu.d.ts +0 -62
- package/dist/summarize-tool-input-BUJsENSn.js +0 -12
|
@@ -1,18 +1,29 @@
|
|
|
1
1
|
import { n as isWorkspacePathWithinRoot, t as canonicalizeAbsoluteWorkspacePath } from "../workspace-path-L_KmjRjq.js";
|
|
2
2
|
import { getWorkspaceBasename, inferWorkspaceMedia } from "./WorkspaceFile.js";
|
|
3
3
|
import * as React from "react";
|
|
4
|
+
import { queryOptions, useQueries, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
4
5
|
//#region src/components/Workspace.ts
|
|
5
6
|
const DEFAULT_MAX_INLINE_ASSET_BYTES = 32 * 1024 * 1024;
|
|
6
7
|
const WORKSPACE_DIRECTORY_PAGE_SIZE = 1e3;
|
|
8
|
+
const ACTIVE_FILE_GC_TIME = 2e3;
|
|
7
9
|
const EMPTY_ENTRIES_BY_DIRECTORY = Object.freeze({});
|
|
8
10
|
const EMPTY_ENTRY_BY_PATH = /* @__PURE__ */ new Map();
|
|
9
11
|
const EMPTY_PATHS = /* @__PURE__ */ new Set();
|
|
12
|
+
const INACTIVE_FILE_INFO = {
|
|
13
|
+
path: "",
|
|
14
|
+
name: "",
|
|
15
|
+
type: "file",
|
|
16
|
+
mimeType: "text/plain",
|
|
17
|
+
size: 0,
|
|
18
|
+
createdAt: 0,
|
|
19
|
+
updatedAt: 0
|
|
20
|
+
};
|
|
21
|
+
const INACTIVE_FILE_ASSET_RESOLVER = () => null;
|
|
10
22
|
function toErrorMessage(error) {
|
|
11
23
|
return error instanceof Error ? error.message : String(error);
|
|
12
24
|
}
|
|
13
|
-
function
|
|
14
|
-
|
|
15
|
-
return separatorIndex <= 0 ? "/" : path.slice(0, separatorIndex);
|
|
25
|
+
function throwIfAborted(signal) {
|
|
26
|
+
if (signal.aborted) throw signal.reason ?? new DOMException("The query was cancelled.", "AbortError");
|
|
16
27
|
}
|
|
17
28
|
function getAssetDataSize(asset) {
|
|
18
29
|
if (asset.kind === "url") return void 0;
|
|
@@ -27,10 +38,7 @@ function normalizeWorkspaceAsset(asset, path, inferredMedia, info) {
|
|
|
27
38
|
size: asset.size ?? getAssetDataSize(asset) ?? info.size
|
|
28
39
|
};
|
|
29
40
|
return {
|
|
30
|
-
asset:
|
|
31
|
-
...asset,
|
|
32
|
-
...metadata
|
|
33
|
-
} : {
|
|
41
|
+
asset: {
|
|
34
42
|
...asset,
|
|
35
43
|
...metadata
|
|
36
44
|
},
|
|
@@ -50,360 +58,382 @@ async function readWorkspaceAssetBytes(source, path, info, maxInlineAssetBytes)
|
|
|
50
58
|
} : null;
|
|
51
59
|
}
|
|
52
60
|
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
61
|
+
* Source/root prefix for cache inspection, cancellation, or removal.
|
|
62
|
+
* Do not broadly invalidate this prefix: active file queries must refresh in
|
|
63
|
+
* stat → resolver → content order. Use a resource option key or `refresh()`.
|
|
56
64
|
*/
|
|
57
|
-
function
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
source,
|
|
82
|
-
root,
|
|
83
|
-
generation: 0
|
|
84
|
-
});
|
|
85
|
-
if (sourceIdentityRef.current.source !== source || sourceIdentityRef.current.root !== root) sourceIdentityRef.current = {
|
|
86
|
-
source,
|
|
87
|
-
root,
|
|
88
|
-
generation: sourceIdentityRef.current.generation + 1
|
|
65
|
+
function workspaceQueryKey(sourceKey, root) {
|
|
66
|
+
return [
|
|
67
|
+
"workspace",
|
|
68
|
+
sourceKey,
|
|
69
|
+
root
|
|
70
|
+
];
|
|
71
|
+
}
|
|
72
|
+
function workspaceFileQueryKey(sourceKey, root, requestedPath) {
|
|
73
|
+
return [
|
|
74
|
+
...workspaceQueryKey(sourceKey, root),
|
|
75
|
+
"file",
|
|
76
|
+
requestedPath
|
|
77
|
+
];
|
|
78
|
+
}
|
|
79
|
+
function workspaceFileVersion(target) {
|
|
80
|
+
return {
|
|
81
|
+
path: target.readPath,
|
|
82
|
+
name: target.info.name,
|
|
83
|
+
type: target.info.type,
|
|
84
|
+
mimeType: target.info.mimeType ?? null,
|
|
85
|
+
size: target.info.size,
|
|
86
|
+
createdAt: target.info.createdAt,
|
|
87
|
+
updatedAt: target.info.updatedAt,
|
|
88
|
+
target: target.info.target ?? null
|
|
89
89
|
};
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
return next;
|
|
101
|
-
});
|
|
102
|
-
}, []);
|
|
103
|
-
const setDirectoryError = React.useCallback((path, message) => {
|
|
104
|
-
setDirectoryErrorsByPath((current) => {
|
|
105
|
-
if (message !== null) {
|
|
106
|
-
if (current[path] === message) return current;
|
|
107
|
-
return {
|
|
108
|
-
...current,
|
|
109
|
-
[path]: message
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
if (!Object.hasOwn(current, path)) return current;
|
|
113
|
-
const next = { ...current };
|
|
114
|
-
delete next[path];
|
|
115
|
-
return next;
|
|
116
|
-
});
|
|
117
|
-
}, []);
|
|
118
|
-
const activateAssetFile = React.useCallback((file) => {
|
|
119
|
-
setFilesByPath((current) => {
|
|
120
|
-
if (!Object.hasOwn(current, file.path)) return current;
|
|
121
|
-
const next = { ...current };
|
|
122
|
-
delete next[file.path];
|
|
123
|
-
return next;
|
|
124
|
-
});
|
|
125
|
-
setActiveAssetFile(file);
|
|
126
|
-
}, []);
|
|
127
|
-
const cacheTextFile = React.useCallback((file) => {
|
|
128
|
-
setActiveAssetFile((current) => current?.path === file.path ? null : current);
|
|
129
|
-
setFilesByPath((current) => ({
|
|
130
|
-
...current,
|
|
131
|
-
[file.path]: file
|
|
132
|
-
}));
|
|
133
|
-
}, []);
|
|
134
|
-
const cancelFileLoad = React.useCallback(() => {
|
|
135
|
-
const request = fileRequestRef.current;
|
|
136
|
-
if (!request) return;
|
|
137
|
-
request.controller.abort();
|
|
138
|
-
fileRequestRef.current = null;
|
|
139
|
-
markLoading(request.path, false);
|
|
140
|
-
}, [markLoading]);
|
|
141
|
-
const loadDirectory = React.useCallback(async (path, { force = false } = {}) => {
|
|
142
|
-
if (!force && loadedDirectoriesRef.current.has(path)) return;
|
|
143
|
-
const identity = sourceIdentityRef.current;
|
|
144
|
-
if (identity.source !== source || identity.root !== root) return;
|
|
145
|
-
const generation = identity.generation;
|
|
146
|
-
const requestVersion = nextDirectoryRequestVersionRef.current + 1;
|
|
147
|
-
nextDirectoryRequestVersionRef.current = requestVersion;
|
|
148
|
-
directoryRequestVersionsRef.current.set(path, requestVersion);
|
|
149
|
-
const requestIsCurrent = () => generation === sourceIdentityRef.current.generation && directoryRequestVersionsRef.current.get(path) === requestVersion;
|
|
150
|
-
markLoading(path, true);
|
|
151
|
-
try {
|
|
90
|
+
}
|
|
91
|
+
/** Shared directory options for workspace components and route loaders. */
|
|
92
|
+
function workspaceDirectoryQueryOptions(source, sourceKey, root, path) {
|
|
93
|
+
return queryOptions({
|
|
94
|
+
queryKey: [
|
|
95
|
+
...workspaceQueryKey(sourceKey, root),
|
|
96
|
+
"directory",
|
|
97
|
+
path
|
|
98
|
+
],
|
|
99
|
+
queryFn: async ({ signal }) => {
|
|
152
100
|
const entries = [];
|
|
153
101
|
let offset = 0;
|
|
154
102
|
for (;;) {
|
|
103
|
+
throwIfAborted(signal);
|
|
155
104
|
const page = await source.readDir(path, {
|
|
156
105
|
limit: WORKSPACE_DIRECTORY_PAGE_SIZE,
|
|
157
106
|
offset
|
|
158
107
|
});
|
|
159
|
-
|
|
108
|
+
throwIfAborted(signal);
|
|
160
109
|
entries.push(...page);
|
|
161
|
-
if (page.length < WORKSPACE_DIRECTORY_PAGE_SIZE)
|
|
110
|
+
if (page.length < WORKSPACE_DIRECTORY_PAGE_SIZE) return entries;
|
|
162
111
|
offset += page.length;
|
|
163
112
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
cancelFileLoad();
|
|
192
|
-
const controller = new AbortController();
|
|
193
|
-
const request = {
|
|
194
|
-
path,
|
|
195
|
-
controller
|
|
196
|
-
};
|
|
197
|
-
fileRequestRef.current = request;
|
|
198
|
-
markLoading(path, true);
|
|
199
|
-
let fileInfo = listedFileInfo;
|
|
200
|
-
let media = inferWorkspaceMedia(path, fileInfo?.mimeType);
|
|
201
|
-
const requestIsCurrent = () => !controller.signal.aborted && generation === sourceIdentityRef.current.generation && selectedPathRef.current === path && (refreshRequestVersion === void 0 || refreshRequestVersionRef.current === refreshRequestVersion);
|
|
202
|
-
try {
|
|
203
|
-
if (!isWorkspacePathWithinRoot(path, root)) throw new Error("This file is outside the configured workspace root.");
|
|
204
|
-
const resolvedInfo = await source.stat(path);
|
|
205
|
-
if (!requestIsCurrent()) return;
|
|
206
|
-
if (!resolvedInfo) {
|
|
207
|
-
fileInfo = null;
|
|
208
|
-
throw new Error("File metadata is unavailable because source.stat() returned null.");
|
|
209
|
-
}
|
|
210
|
-
fileInfo = resolvedInfo;
|
|
211
|
-
const readPath = canonicalizeAbsoluteWorkspacePath(resolvedInfo.path);
|
|
113
|
+
},
|
|
114
|
+
staleTime: Infinity
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
/** Shared workspace-summary options for workspace components and route loaders. */
|
|
118
|
+
function workspaceInfoQueryOptions(source, sourceKey, root) {
|
|
119
|
+
return queryOptions({
|
|
120
|
+
queryKey: [...workspaceQueryKey(sourceKey, root), "info"],
|
|
121
|
+
queryFn: async ({ signal }) => {
|
|
122
|
+
if (!("getWorkspaceInfo" in source) || typeof source.getWorkspaceInfo !== "function") return null;
|
|
123
|
+
const info = await source.getWorkspaceInfo();
|
|
124
|
+
throwIfAborted(signal);
|
|
125
|
+
return info;
|
|
126
|
+
},
|
|
127
|
+
staleTime: Infinity
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
/** Re-stat and validate a requested path before any resolver or file read. */
|
|
131
|
+
function workspaceFileStatQueryOptions(source, sourceKey, root, requestedPath) {
|
|
132
|
+
return queryOptions({
|
|
133
|
+
queryKey: [...workspaceFileQueryKey(sourceKey, root, requestedPath), "stat"],
|
|
134
|
+
queryFn: async ({ signal }) => {
|
|
135
|
+
if (!isWorkspacePathWithinRoot(requestedPath, root)) throw new Error("This file is outside the configured workspace root.");
|
|
136
|
+
const info = await source.stat(requestedPath);
|
|
137
|
+
throwIfAborted(signal);
|
|
138
|
+
if (!info) throw new Error("File metadata is unavailable because source.stat() returned null.");
|
|
139
|
+
const readPath = canonicalizeAbsoluteWorkspacePath(info.path);
|
|
212
140
|
if (!readPath) throw new Error("The workspace source returned an invalid file path from stat().");
|
|
213
|
-
if (readPath !==
|
|
141
|
+
if (readPath !== info.path) throw new Error("The workspace source returned a non-canonical file path from stat().");
|
|
214
142
|
if (!isWorkspacePathWithinRoot(readPath, root)) throw new Error("This file's resolved path is outside the configured workspace root.");
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
143
|
+
return {
|
|
144
|
+
requestedPath,
|
|
145
|
+
readPath,
|
|
146
|
+
info,
|
|
147
|
+
media: inferWorkspaceMedia(readPath, info.mimeType)
|
|
148
|
+
};
|
|
149
|
+
},
|
|
150
|
+
staleTime: 0,
|
|
151
|
+
refetchOnMount: "always",
|
|
152
|
+
refetchOnWindowFocus: false,
|
|
153
|
+
refetchOnReconnect: false,
|
|
154
|
+
retry: false
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
/** Optional host asset resolution, scoped to the current stat result. */
|
|
158
|
+
function workspaceFileAssetQueryOptions(sourceKey, root, target, resolveFileAsset) {
|
|
159
|
+
return queryOptions({
|
|
160
|
+
queryKey: [
|
|
161
|
+
...workspaceFileQueryKey(sourceKey, root, target.requestedPath),
|
|
162
|
+
"asset",
|
|
163
|
+
workspaceFileVersion(target)
|
|
164
|
+
],
|
|
165
|
+
queryFn: async ({ signal }) => {
|
|
166
|
+
const resolvedAsset = await resolveFileAsset({
|
|
167
|
+
path: target.readPath,
|
|
168
|
+
requestedPath: target.requestedPath,
|
|
220
169
|
root: canonicalizeAbsoluteWorkspacePath(root) ?? root,
|
|
221
|
-
info:
|
|
222
|
-
signal
|
|
223
|
-
}) : null;
|
|
224
|
-
if (!requestIsCurrent()) return;
|
|
225
|
-
if (resolvedAsset) {
|
|
226
|
-
const normalized = normalizeWorkspaceAsset(resolvedAsset, readPath, media, resolvedInfo);
|
|
227
|
-
if (!normalized) throw new Error("The host returned an asset without a supported image, PDF, audio, or video MIME type.");
|
|
228
|
-
media = normalized.media;
|
|
229
|
-
activateAssetFile({
|
|
230
|
-
path,
|
|
231
|
-
name: getWorkspaceBasename(path),
|
|
232
|
-
content: null,
|
|
233
|
-
asset: normalized.asset,
|
|
234
|
-
info: resolvedInfo,
|
|
235
|
-
error: null
|
|
236
|
-
});
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
if (media) {
|
|
240
|
-
const byteAsset = await readWorkspaceAssetBytes(source, readPath, resolvedInfo, maxInlineAssetBytesRef.current);
|
|
241
|
-
if (!requestIsCurrent()) return;
|
|
242
|
-
const normalized = byteAsset ? normalizeWorkspaceAsset(byteAsset, readPath, media, resolvedInfo) : null;
|
|
243
|
-
activateAssetFile({
|
|
244
|
-
path,
|
|
245
|
-
name: getWorkspaceBasename(path),
|
|
246
|
-
content: null,
|
|
247
|
-
asset: normalized?.asset ?? null,
|
|
248
|
-
info: resolvedInfo,
|
|
249
|
-
error: null
|
|
250
|
-
});
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
|
-
const content = await source.readFile(readPath);
|
|
254
|
-
if (!requestIsCurrent()) return;
|
|
255
|
-
cacheTextFile({
|
|
256
|
-
path,
|
|
257
|
-
name: getWorkspaceBasename(path),
|
|
258
|
-
content,
|
|
259
|
-
asset: null,
|
|
260
|
-
info: resolvedInfo,
|
|
261
|
-
error: null
|
|
170
|
+
info: target.info,
|
|
171
|
+
signal
|
|
262
172
|
});
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
173
|
+
if (!resolvedAsset) return null;
|
|
174
|
+
const normalized = normalizeWorkspaceAsset(resolvedAsset, target.readPath, target.media, target.info);
|
|
175
|
+
if (!normalized) throw new Error("The host returned an asset without a supported image, PDF, audio, or video MIME type.");
|
|
176
|
+
return normalized;
|
|
177
|
+
},
|
|
178
|
+
staleTime: 0,
|
|
179
|
+
gcTime: ACTIVE_FILE_GC_TIME,
|
|
180
|
+
refetchOnMount: "always",
|
|
181
|
+
refetchOnWindowFocus: false,
|
|
182
|
+
refetchOnReconnect: false,
|
|
183
|
+
retry: false
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
/** Cached text read options, scoped to the validated stat result. */
|
|
187
|
+
function workspaceTextFileQueryOptions(source, sourceKey, root, target) {
|
|
188
|
+
return queryOptions({
|
|
189
|
+
queryKey: [
|
|
190
|
+
...workspaceFileQueryKey(sourceKey, root, target.requestedPath),
|
|
191
|
+
"text",
|
|
192
|
+
workspaceFileVersion(target)
|
|
193
|
+
],
|
|
194
|
+
queryFn: async ({ signal }) => {
|
|
195
|
+
const content = await source.readFile(target.readPath);
|
|
196
|
+
throwIfAborted(signal);
|
|
197
|
+
return content;
|
|
198
|
+
},
|
|
199
|
+
staleTime: Infinity
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
/** Active-file-only buffered asset options, scoped to the validated stat result. */
|
|
203
|
+
function workspaceBinaryFileQueryOptions(source, sourceKey, root, target, maxInlineAssetBytes) {
|
|
204
|
+
return queryOptions({
|
|
205
|
+
queryKey: [
|
|
206
|
+
...workspaceFileQueryKey(sourceKey, root, target.requestedPath),
|
|
207
|
+
"bytes",
|
|
208
|
+
workspaceFileVersion(target),
|
|
209
|
+
{ maxInlineAssetBytes }
|
|
210
|
+
],
|
|
211
|
+
queryFn: async ({ signal }) => {
|
|
212
|
+
const asset = await readWorkspaceAssetBytes(source, target.readPath, target.info, maxInlineAssetBytes);
|
|
213
|
+
throwIfAborted(signal);
|
|
214
|
+
return asset ? normalizeWorkspaceAsset(asset, target.readPath, target.media, target.info) : null;
|
|
215
|
+
},
|
|
216
|
+
staleTime: 0,
|
|
217
|
+
gcTime: ACTIVE_FILE_GC_TIME,
|
|
218
|
+
refetchOnMount: "always",
|
|
219
|
+
refetchOnWindowFocus: false,
|
|
220
|
+
refetchOnReconnect: false,
|
|
221
|
+
retry: false
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
async function refreshWorkspaceFile(queryClient, { source, sourceKey, root, selectedPath, resolveFileAsset, maxInlineAssetBytes }) {
|
|
225
|
+
if (!selectedPath) return;
|
|
226
|
+
const target = await queryClient.fetchQuery({
|
|
227
|
+
...workspaceFileStatQueryOptions(source, sourceKey, root, selectedPath),
|
|
228
|
+
staleTime: 0
|
|
229
|
+
});
|
|
230
|
+
if (resolveFileAsset) {
|
|
231
|
+
const options = workspaceFileAssetQueryOptions(sourceKey, root, target, resolveFileAsset);
|
|
232
|
+
if (!queryClient.getQueryState(options.queryKey)) return;
|
|
233
|
+
if (await queryClient.fetchQuery({
|
|
234
|
+
...options,
|
|
235
|
+
staleTime: 0
|
|
236
|
+
})) return;
|
|
237
|
+
}
|
|
238
|
+
if (target.media) {
|
|
239
|
+
const options = workspaceBinaryFileQueryOptions(source, sourceKey, root, target, maxInlineAssetBytes);
|
|
240
|
+
if (queryClient.getQueryState(options.queryKey)) await queryClient.fetchQuery({
|
|
241
|
+
...options,
|
|
242
|
+
staleTime: 0
|
|
328
243
|
});
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
const options = workspaceTextFileQueryOptions(source, sourceKey, root, target);
|
|
247
|
+
if (queryClient.getQueryState(options.queryKey)) await queryClient.fetchQuery({
|
|
248
|
+
...options,
|
|
249
|
+
staleTime: 0
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Headless state for browsing a Think workspace. TanStack Query owns remote
|
|
254
|
+
* directory, metadata, resolver, text, and byte lifecycles; the route owns
|
|
255
|
+
* selection, while this hook only keeps local tree expansion state.
|
|
256
|
+
*/
|
|
257
|
+
function useWorkspace({ source, sourceKey, root = "/", selectedPath, onSelectedPathChange, resolveFileAsset, maxInlineAssetBytes = DEFAULT_MAX_INLINE_ASSET_BYTES }) {
|
|
258
|
+
const queryClient = useQueryClient();
|
|
259
|
+
const scopeKey = JSON.stringify([sourceKey, root]);
|
|
260
|
+
const [expandedPathsByScope, setExpandedPathsByScope] = React.useState({});
|
|
261
|
+
const expandedPaths = expandedPathsByScope[scopeKey] ?? EMPTY_PATHS;
|
|
262
|
+
const latestExpansionRef = React.useRef({
|
|
263
|
+
scopeKey,
|
|
264
|
+
paths: expandedPaths
|
|
265
|
+
});
|
|
266
|
+
const normalizedMaxInlineAssetBytes = Number.isFinite(maxInlineAssetBytes) && maxInlineAssetBytes >= 0 ? maxInlineAssetBytes : DEFAULT_MAX_INLINE_ASSET_BYTES;
|
|
267
|
+
const directoryPaths = React.useMemo(() => [.../* @__PURE__ */ new Set([root, ...expandedPaths])].sort(), [expandedPaths, root]);
|
|
268
|
+
const directoryQueries = useQueries({ queries: directoryPaths.map((path) => workspaceDirectoryQueryOptions(source, sourceKey, root, path)) });
|
|
269
|
+
const infoQuery = useQuery({
|
|
270
|
+
...workspaceInfoQueryOptions(source, sourceKey, root),
|
|
271
|
+
enabled: "getWorkspaceInfo" in source && typeof source.getWorkspaceInfo === "function"
|
|
272
|
+
});
|
|
273
|
+
const statQuery = useQuery({
|
|
274
|
+
...workspaceFileStatQueryOptions(source, sourceKey, root, selectedPath ?? root),
|
|
275
|
+
enabled: selectedPath !== null
|
|
276
|
+
});
|
|
277
|
+
const target = selectedPath === null ? void 0 : statQuery.data;
|
|
278
|
+
const statSettled = target !== void 0 && statQuery.isSuccess && !statQuery.isFetching;
|
|
279
|
+
const queryTarget = target ?? {
|
|
280
|
+
requestedPath: selectedPath ?? root,
|
|
281
|
+
readPath: selectedPath ?? root,
|
|
282
|
+
info: INACTIVE_FILE_INFO,
|
|
283
|
+
media: null
|
|
284
|
+
};
|
|
285
|
+
const assetQuery = useQuery({
|
|
286
|
+
...workspaceFileAssetQueryOptions(sourceKey, root, queryTarget, resolveFileAsset ?? INACTIVE_FILE_ASSET_RESOLVER),
|
|
287
|
+
enabled: statSettled && resolveFileAsset !== void 0
|
|
288
|
+
});
|
|
289
|
+
const resolvedAsset = assetQuery.data ?? null;
|
|
290
|
+
const resolverSettled = !resolveFileAsset || assetQuery.isSuccess && !assetQuery.isFetching;
|
|
291
|
+
const textQuery = useQuery({
|
|
292
|
+
...workspaceTextFileQueryOptions(source, sourceKey, root, queryTarget),
|
|
293
|
+
enabled: statSettled && resolverSettled && !resolvedAsset && !target.media
|
|
294
|
+
});
|
|
295
|
+
const binaryQuery = useQuery({
|
|
296
|
+
...workspaceBinaryFileQueryOptions(source, sourceKey, root, queryTarget, normalizedMaxInlineAssetBytes),
|
|
297
|
+
enabled: statSettled && resolverSettled && !resolvedAsset && Boolean(target.media)
|
|
298
|
+
});
|
|
299
|
+
const entriesByDirectory = React.useMemo(() => {
|
|
300
|
+
const entries = {};
|
|
301
|
+
for (const [index, path] of directoryPaths.entries()) {
|
|
302
|
+
const data = directoryQueries[index]?.data;
|
|
303
|
+
if (data) entries[path] = data;
|
|
304
|
+
}
|
|
305
|
+
return entries;
|
|
306
|
+
}, [directoryPaths, directoryQueries]);
|
|
307
|
+
const entryByPath = React.useMemo(() => {
|
|
308
|
+
const entries = /* @__PURE__ */ new Map();
|
|
309
|
+
for (const directoryEntries of Object.values(entriesByDirectory)) for (const entry of directoryEntries) entries.set(entry.path, entry);
|
|
310
|
+
return entries;
|
|
311
|
+
}, [entriesByDirectory]);
|
|
312
|
+
const setExpandedPaths = React.useCallback((next) => {
|
|
313
|
+
latestExpansionRef.current = {
|
|
314
|
+
scopeKey,
|
|
315
|
+
paths: next
|
|
316
|
+
};
|
|
317
|
+
setExpandedPathsByScope((current) => ({
|
|
318
|
+
...current,
|
|
319
|
+
[scopeKey]: next
|
|
320
|
+
}));
|
|
321
|
+
}, [scopeKey]);
|
|
322
|
+
const selectFile = React.useCallback((path) => onSelectedPathChange?.(path), [onSelectedPathChange]);
|
|
323
|
+
const selectedError = statQuery.error ?? assetQuery.error ?? textQuery.error ?? binaryQuery.error;
|
|
324
|
+
let selectedFile = null;
|
|
325
|
+
if (selectedPath && selectedError) selectedFile = {
|
|
326
|
+
path: selectedPath,
|
|
327
|
+
name: getWorkspaceBasename(selectedPath),
|
|
328
|
+
content: null,
|
|
329
|
+
asset: null,
|
|
330
|
+
info: target?.info ?? null,
|
|
331
|
+
error: toErrorMessage(selectedError)
|
|
332
|
+
};
|
|
333
|
+
else if (selectedPath && statSettled && target && resolverSettled) {
|
|
334
|
+
if (resolvedAsset) selectedFile = {
|
|
335
|
+
path: selectedPath,
|
|
336
|
+
name: getWorkspaceBasename(selectedPath),
|
|
337
|
+
content: null,
|
|
338
|
+
asset: resolvedAsset.asset,
|
|
339
|
+
info: target.info,
|
|
340
|
+
error: null
|
|
341
|
+
};
|
|
342
|
+
else if (target.media && binaryQuery.isSuccess && !binaryQuery.isFetching) selectedFile = {
|
|
343
|
+
path: selectedPath,
|
|
344
|
+
name: getWorkspaceBasename(selectedPath),
|
|
345
|
+
content: null,
|
|
346
|
+
asset: binaryQuery.data?.asset ?? null,
|
|
347
|
+
info: target.info,
|
|
348
|
+
error: null
|
|
349
|
+
};
|
|
350
|
+
else if (!target.media && textQuery.isSuccess) selectedFile = {
|
|
351
|
+
path: selectedPath,
|
|
352
|
+
name: getWorkspaceBasename(selectedPath),
|
|
353
|
+
content: textQuery.data,
|
|
354
|
+
asset: null,
|
|
355
|
+
info: target.info,
|
|
356
|
+
error: null
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
const loadingPaths = React.useMemo(() => {
|
|
360
|
+
const paths = /* @__PURE__ */ new Set();
|
|
361
|
+
for (const [index, path] of directoryPaths.entries()) if (directoryQueries[index]?.isFetching) paths.add(path);
|
|
362
|
+
if (selectedPath && [
|
|
363
|
+
statQuery,
|
|
364
|
+
assetQuery,
|
|
365
|
+
textQuery,
|
|
366
|
+
binaryQuery
|
|
367
|
+
].some((query) => query.isFetching)) paths.add(selectedPath);
|
|
368
|
+
return paths;
|
|
329
369
|
}, [
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
370
|
+
assetQuery,
|
|
371
|
+
binaryQuery,
|
|
372
|
+
directoryPaths,
|
|
373
|
+
directoryQueries,
|
|
374
|
+
selectedPath,
|
|
375
|
+
statQuery,
|
|
376
|
+
textQuery
|
|
334
377
|
]);
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
const setExpandedPaths = React.useCallback((next) => {
|
|
339
|
-
setExpandedPathsState((current) => {
|
|
340
|
-
for (const path of next) if (!current.has(path)) loadDirectory(path);
|
|
341
|
-
return next;
|
|
342
|
-
});
|
|
343
|
-
}, [loadDirectory]);
|
|
344
|
-
const selectFile = React.useCallback((path) => {
|
|
345
|
-
cancelFileLoad();
|
|
346
|
-
selectedPathRef.current = path;
|
|
347
|
-
setActiveAssetFile(null);
|
|
348
|
-
setSelectedPath(path);
|
|
349
|
-
if (path !== null) loadFile(path);
|
|
350
|
-
}, [cancelFileLoad, loadFile]);
|
|
378
|
+
const rootDirectoryIndex = directoryPaths.indexOf(root);
|
|
379
|
+
const isLoading = Boolean(directoryQueries[rootDirectoryIndex]?.isFetching) || infoQuery.isFetching;
|
|
380
|
+
const error = [...new Set(directoryQueries.map((query) => query.error).filter((queryError) => queryError !== null).map(toErrorMessage))].join("; ") || null;
|
|
351
381
|
const refresh = React.useCallback(() => {
|
|
352
382
|
(async () => {
|
|
353
|
-
const
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
const
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
setIsLoading(true);
|
|
362
|
-
try {
|
|
363
|
-
const directoryPaths = [.../* @__PURE__ */ new Set([root, ...expandedPaths])];
|
|
364
|
-
const [refreshedListings] = await Promise.all([Promise.all(directoryPaths.map((path) => loadDirectory(path, { force: true }))), loadInfo()]);
|
|
365
|
-
if (!requestIsCurrent()) return;
|
|
366
|
-
const refreshedSelectedPath = selectedPathRef.current;
|
|
367
|
-
if (refreshedSelectedPath) {
|
|
368
|
-
const selectedDirectoryIndex = directoryPaths.indexOf(getWorkspaceParentPath(refreshedSelectedPath));
|
|
369
|
-
const selectedDirectoryListing = refreshedListings[selectedDirectoryIndex];
|
|
370
|
-
if (selectedDirectoryIndex >= 0 && (selectedDirectoryListing === null || selectedDirectoryListing === void 0)) return;
|
|
371
|
-
await loadFile(refreshedSelectedPath, {
|
|
372
|
-
force: true,
|
|
373
|
-
refreshRequestVersion
|
|
383
|
+
const latestExpandedPaths = latestExpansionRef.current.scopeKey === scopeKey ? latestExpansionRef.current.paths : expandedPaths;
|
|
384
|
+
const refreshedDirectoryPaths = [.../* @__PURE__ */ new Set([root, ...latestExpandedPaths])].sort();
|
|
385
|
+
await queryClient.cancelQueries({ queryKey: workspaceQueryKey(sourceKey, root) });
|
|
386
|
+
const refreshedDirectories = await Promise.all(refreshedDirectoryPaths.map(async (path) => {
|
|
387
|
+
try {
|
|
388
|
+
await queryClient.fetchQuery({
|
|
389
|
+
...workspaceDirectoryQueryOptions(source, sourceKey, root, path),
|
|
390
|
+
staleTime: 0
|
|
374
391
|
});
|
|
392
|
+
return true;
|
|
393
|
+
} catch {
|
|
394
|
+
return false;
|
|
375
395
|
}
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
396
|
+
}));
|
|
397
|
+
if ("getWorkspaceInfo" in source && typeof source.getWorkspaceInfo === "function") await queryClient.fetchQuery({
|
|
398
|
+
...workspaceInfoQueryOptions(source, sourceKey, root),
|
|
399
|
+
staleTime: 0
|
|
400
|
+
}).catch(() => void 0);
|
|
401
|
+
if (refreshedDirectories.includes(false)) return;
|
|
402
|
+
await refreshWorkspaceFile(queryClient, {
|
|
403
|
+
source,
|
|
404
|
+
sourceKey,
|
|
405
|
+
root,
|
|
406
|
+
selectedPath,
|
|
407
|
+
resolveFileAsset,
|
|
408
|
+
maxInlineAssetBytes: normalizedMaxInlineAssetBytes
|
|
409
|
+
});
|
|
410
|
+
})().catch(() => {});
|
|
380
411
|
}, [
|
|
381
412
|
expandedPaths,
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
413
|
+
normalizedMaxInlineAssetBytes,
|
|
414
|
+
queryClient,
|
|
415
|
+
resolveFileAsset,
|
|
385
416
|
root,
|
|
386
|
-
|
|
417
|
+
scopeKey,
|
|
418
|
+
selectedPath,
|
|
419
|
+
source,
|
|
420
|
+
sourceKey
|
|
387
421
|
]);
|
|
388
|
-
const stateIsCurrent = stateGeneration === sourceIdentityRef.current.generation;
|
|
389
|
-
const directoryError = [...new Set(Object.values(directoryErrorsByPath))].join("; ") || null;
|
|
390
|
-
const visibleSelectedPath = stateIsCurrent ? selectedPath : null;
|
|
391
|
-
const selectedFile = visibleSelectedPath ? activeAssetFile?.path === visibleSelectedPath ? activeAssetFile : filesByPath[visibleSelectedPath] ?? null : null;
|
|
392
422
|
return {
|
|
393
423
|
root,
|
|
394
|
-
entriesByDirectory:
|
|
395
|
-
entryByPath:
|
|
396
|
-
expandedPaths
|
|
424
|
+
entriesByDirectory: Object.keys(entriesByDirectory).length > 0 ? entriesByDirectory : EMPTY_ENTRIES_BY_DIRECTORY,
|
|
425
|
+
entryByPath: entryByPath.size > 0 ? entryByPath : EMPTY_ENTRY_BY_PATH,
|
|
426
|
+
expandedPaths,
|
|
397
427
|
setExpandedPaths,
|
|
398
|
-
selectedPath
|
|
428
|
+
selectedPath,
|
|
399
429
|
selectFile,
|
|
400
430
|
selectedFile,
|
|
401
|
-
isLoading
|
|
402
|
-
loadingPaths:
|
|
403
|
-
error
|
|
404
|
-
info:
|
|
431
|
+
isLoading,
|
|
432
|
+
loadingPaths: loadingPaths.size > 0 ? loadingPaths : EMPTY_PATHS,
|
|
433
|
+
error,
|
|
434
|
+
info: infoQuery.data ?? null,
|
|
405
435
|
refresh
|
|
406
436
|
};
|
|
407
437
|
}
|
|
408
438
|
//#endregion
|
|
409
|
-
export { useWorkspace };
|
|
439
|
+
export { useWorkspace, workspaceBinaryFileQueryOptions, workspaceDirectoryQueryOptions, workspaceFileAssetQueryOptions, workspaceFileStatQueryOptions, workspaceInfoQueryOptions, workspaceQueryKey, workspaceTextFileQueryOptions };
|