@deepseek-ai/dsh-client-ui-sidebar-right 0.1.5-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.i18n.yaml +6 -0
- package/README.md +133 -0
- package/README.zh.md +133 -0
- package/lib/client.js +3614 -0
- package/lib/index.js +6 -0
- package/lib/types/client/contract/params.d.ts +47 -0
- package/lib/types/client/contract/seed.d.ts +35 -0
- package/lib/types/client/contract/slots.d.ts +152 -0
- package/lib/types/client/index.d.ts +56 -0
- package/lib/types/client/labels.d.ts +19 -0
- package/lib/types/client/locales.d.ts +48 -0
- package/lib/types/client/service.d.ts +300 -0
- package/lib/types/client/shell/ExpandButton.d.ts +24 -0
- package/lib/types/client/shell/SidebarRight.d.ts +99 -0
- package/lib/types/client/stores.d.ts +100 -0
- package/lib/types/client/tab-domain.d.ts +95 -0
- package/lib/types/client/tab-info.d.ts +29 -0
- package/lib/types/client/tab-registry.d.ts +205 -0
- package/lib/types/client/tabs/guide/GuideBody.d.ts +30 -0
- package/lib/types/client/tabs/guide/definition.d.ts +18 -0
- package/lib/types/index.d.ts +4 -0
- package/package.json +79 -0
package/lib/client.js
ADDED
|
@@ -0,0 +1,3614 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "@deepseek-ai/dsh-client-ui-sidebar-right",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
//#region \0rolldown/runtime.js
|
|
8
|
+
var __create = Object.create;
|
|
9
|
+
var __defProp = Object.defineProperty;
|
|
10
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
11
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
12
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
13
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
|
+
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
15
|
+
var __copyProps = (to, from, except, desc) => {
|
|
16
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
17
|
+
key = keys[i];
|
|
18
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
19
|
+
get: ((k) => from[k]).bind(null, key),
|
|
20
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
return to;
|
|
24
|
+
};
|
|
25
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
26
|
+
value: mod,
|
|
27
|
+
enumerable: true
|
|
28
|
+
}) : target, mod));
|
|
29
|
+
//#endregion
|
|
30
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
31
|
+
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
32
|
+
let react = require("react");
|
|
33
|
+
let react_dom = require("react-dom");
|
|
34
|
+
let _deepseek_ai_dsh_client_ui_dockkit = require("@deepseek-ai/dsh-client-ui-dockkit");
|
|
35
|
+
let _deepseek_ai_dsh_client_store = require("@deepseek-ai/dsh-client-store");
|
|
36
|
+
//#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-sidebar-right/src/client/tabs/guide/GuideBody.module.css.mjs
|
|
37
|
+
const css$2 = ".geFEbW_guide{text-align:center;flex-direction:column;align-items:center;gap:8px;padding-top:24px;display:flex}.geFEbW_guideTitle{color:var(--dsw-alias-label-primary);font-size:var(--dsh-content-font-size,14px);margin:0;font-weight:500;line-height:1.6}.geFEbW_guideBody{color:var(--dsw-alias-label-secondary);font-size:var(--dsh-content-font-size-secondary,13px);margin:0;line-height:1.6}.geFEbW_entries{grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:8px;width:100%;max-width:480px;margin-top:16px;display:grid}.geFEbW_entry{color:inherit;font:inherit;text-align:left;border:.5px solid var(--dsw-alias-border-l1);cursor:pointer;background:0 0;border-radius:8px;align-items:flex-start;gap:8px;padding:10px 12px;display:flex}.geFEbW_entry:hover{background:var(--dsw-alias-interactive-bg-hover)}.geFEbW_entryIcon{color:var(--dsw-alias-label-secondary);flex:none;margin-top:1px;display:flex}.geFEbW_entryText{flex-direction:column;gap:2px;min-width:0;display:flex}.geFEbW_entryTitle{color:var(--dsw-alias-label-primary);font-size:var(--dsh-content-font-size,14px);line-height:1.4}.geFEbW_entryDescription{color:var(--dsw-alias-label-secondary);font-size:var(--dsh-content-font-size-secondary,13px);line-height:1.4}";
|
|
38
|
+
const tagId$2 = "@deepseek-ai/dsh-client-ui-sidebar-right/GuideBody.module.css";
|
|
39
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
|
|
40
|
+
const tag = document.createElement("style");
|
|
41
|
+
tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-sidebar-right";
|
|
42
|
+
tag.dataset.pluginCss = tagId$2;
|
|
43
|
+
tag.textContent = css$2;
|
|
44
|
+
document.head.appendChild(tag);
|
|
45
|
+
}
|
|
46
|
+
var GuideBody_module_css_default = {
|
|
47
|
+
"entries": "geFEbW_entries",
|
|
48
|
+
"entry": "geFEbW_entry",
|
|
49
|
+
"entryDescription": "geFEbW_entryDescription",
|
|
50
|
+
"entryIcon": "geFEbW_entryIcon",
|
|
51
|
+
"entryText": "geFEbW_entryText",
|
|
52
|
+
"entryTitle": "geFEbW_entryTitle",
|
|
53
|
+
"guide": "geFEbW_guide",
|
|
54
|
+
"guideBody": "geFEbW_guideBody",
|
|
55
|
+
"guideTitle": "geFEbW_guideTitle"
|
|
56
|
+
};
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region lib/types/client/tabs/guide/GuideBody.js
|
|
59
|
+
/** One entry box: the contributing type's glyph, heading, and line. */
|
|
60
|
+
function EntryBox({ entry, onPick }) {
|
|
61
|
+
const Icon = entry.icon;
|
|
62
|
+
return (0, react_jsx_runtime.jsxs)("button", {
|
|
63
|
+
type: "button",
|
|
64
|
+
className: GuideBody_module_css_default.entry,
|
|
65
|
+
"data-sidebar-right-guide-entry": entry.kind,
|
|
66
|
+
onClick: () => {
|
|
67
|
+
onPick(entry);
|
|
68
|
+
},
|
|
69
|
+
children: [Icon !== void 0 && (0, react_jsx_runtime.jsx)("span", {
|
|
70
|
+
className: GuideBody_module_css_default.entryIcon,
|
|
71
|
+
children: (0, react_jsx_runtime.jsx)(Icon, { size: 16 })
|
|
72
|
+
}), (0, react_jsx_runtime.jsxs)("span", {
|
|
73
|
+
className: GuideBody_module_css_default.entryText,
|
|
74
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
75
|
+
className: GuideBody_module_css_default.entryTitle,
|
|
76
|
+
children: entry.title()
|
|
77
|
+
}), (0, react_jsx_runtime.jsx)("span", {
|
|
78
|
+
className: GuideBody_module_css_default.entryDescription,
|
|
79
|
+
children: entry.description()
|
|
80
|
+
})]
|
|
81
|
+
})]
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/** The shipped guide: what the column is for, and the doors out of it. */
|
|
85
|
+
function ShippedGuide({ entries, onPick, t }) {
|
|
86
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
87
|
+
className: GuideBody_module_css_default.guide,
|
|
88
|
+
"data-sidebar-right-guide": true,
|
|
89
|
+
children: [
|
|
90
|
+
(0, react_jsx_runtime.jsx)("p", {
|
|
91
|
+
className: GuideBody_module_css_default.guideTitle,
|
|
92
|
+
children: t("guide.lead")
|
|
93
|
+
}),
|
|
94
|
+
(0, react_jsx_runtime.jsx)("p", {
|
|
95
|
+
className: GuideBody_module_css_default.guideBody,
|
|
96
|
+
children: t("guide.body")
|
|
97
|
+
}),
|
|
98
|
+
entries.length > 0 && (0, react_jsx_runtime.jsx)("div", {
|
|
99
|
+
className: GuideBody_module_css_default.entries,
|
|
100
|
+
children: entries.map((entry, index) => (0, react_jsx_runtime.jsx)(EntryBox, {
|
|
101
|
+
entry,
|
|
102
|
+
onPick
|
|
103
|
+
}, `${entry.kind}:${index}`))
|
|
104
|
+
})
|
|
105
|
+
]
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
/** The guide tab's body, replaceable through its chain child. */
|
|
109
|
+
function GuideBody({ useTabInfo, useGuideEntries, renderSlotChain, t }) {
|
|
110
|
+
const { tab } = useTabInfo();
|
|
111
|
+
return renderSlotChain("sidebar.right.tab.guide", {}, {
|
|
112
|
+
hookContext: useTabInfo,
|
|
113
|
+
fallback: (0, react_jsx_runtime.jsx)(ShippedGuide, {
|
|
114
|
+
entries: useGuideEntries((entries) => entries),
|
|
115
|
+
onPick: (entry) => {
|
|
116
|
+
tab.actions.openTab(entry.kind, { replaceTab: true });
|
|
117
|
+
},
|
|
118
|
+
t
|
|
119
|
+
})
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
//#endregion
|
|
123
|
+
//#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-sidebar-right/src/client/shell/ExpandButton.module.css.mjs
|
|
124
|
+
const css$1 = "._1kL45W_placeholder{flex:none;width:32px;height:32px;display:inline-block}._1kL45W_button{width:32px;height:32px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:8px;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}._1kL45W_button:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}._1kL45W_icon{transform:scaleX(-1)}";
|
|
125
|
+
const tagId$1 = "@deepseek-ai/dsh-client-ui-sidebar-right/ExpandButton.module.css";
|
|
126
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
|
|
127
|
+
const tag = document.createElement("style");
|
|
128
|
+
tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-sidebar-right";
|
|
129
|
+
tag.dataset.pluginCss = tagId$1;
|
|
130
|
+
tag.textContent = css$1;
|
|
131
|
+
document.head.appendChild(tag);
|
|
132
|
+
}
|
|
133
|
+
var ExpandButton_module_css_default = {
|
|
134
|
+
"button": "_1kL45W_button",
|
|
135
|
+
"icon": "_1kL45W_icon",
|
|
136
|
+
"placeholder": "_1kL45W_placeholder"
|
|
137
|
+
};
|
|
138
|
+
//#endregion
|
|
139
|
+
//#region lib/types/client/shell/ExpandButton.js
|
|
140
|
+
/** The expand control while the panel is collapsed; its footprint while it is shown. */
|
|
141
|
+
function ExpandButton({ sessionId, useStore, actions, t }) {
|
|
142
|
+
if (useStore((state) => state.bySession[sessionId]?.layout.expanded ?? false)) return (0, react_jsx_runtime.jsx)("span", {
|
|
143
|
+
className: ExpandButton_module_css_default.placeholder,
|
|
144
|
+
"aria-hidden": true,
|
|
145
|
+
"data-sidebar-right-expand-placeholder": true
|
|
146
|
+
});
|
|
147
|
+
return (0, react_jsx_runtime.jsx)("button", {
|
|
148
|
+
type: "button",
|
|
149
|
+
className: ExpandButton_module_css_default.button,
|
|
150
|
+
"aria-label": t("chrome.expand"),
|
|
151
|
+
title: t("chrome.expand"),
|
|
152
|
+
"data-sidebar-right-expand": true,
|
|
153
|
+
onClick: () => {
|
|
154
|
+
actions.setExpanded(sessionId, true);
|
|
155
|
+
},
|
|
156
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPanelLeftOutline16, { className: ExpandButton_module_css_default.icon })
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
//#endregion
|
|
160
|
+
//#region lib/types/client/contract/seed.js
|
|
161
|
+
/** The guide tab's kind. */
|
|
162
|
+
const GUIDE_KIND = "guide";
|
|
163
|
+
/**
|
|
164
|
+
* The address a page tab is recorded under: `sidebar://<kind>`. The scheme is
|
|
165
|
+
* this package's bookkeeping for `openTab`, spelled here and nowhere else; a
|
|
166
|
+
* caller names the kind and never sees or composes the address.
|
|
167
|
+
* @param kind - the page type's kind.
|
|
168
|
+
* @returns the page's address.
|
|
169
|
+
*/
|
|
170
|
+
function pageAddress(kind) {
|
|
171
|
+
return `sidebar://${kind}`;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Build the guide tab a new pane is seeded with.
|
|
175
|
+
*
|
|
176
|
+
* The title is captured at mint time because it goes into the surface's
|
|
177
|
+
* operation sequence, which records what happened and must not change meaning
|
|
178
|
+
* later. A language change relabels the type, not tabs already open.
|
|
179
|
+
* @param id - tab id minted by the caller.
|
|
180
|
+
* @param title - the guide type's display name at mint time.
|
|
181
|
+
* @returns the guide tab record.
|
|
182
|
+
*/
|
|
183
|
+
function makeGuideTab(id, title) {
|
|
184
|
+
return {
|
|
185
|
+
id,
|
|
186
|
+
kind: GUIDE_KIND,
|
|
187
|
+
contentId: pageAddress(GUIDE_KIND),
|
|
188
|
+
title
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
//#endregion
|
|
192
|
+
//#region lib/types/client/labels.js
|
|
193
|
+
/**
|
|
194
|
+
* Project the dictionary into the kit's label contract.
|
|
195
|
+
*
|
|
196
|
+
* Called during render, so a language change reaches the kit with the next one —
|
|
197
|
+
* the kit caches no copy to invalidate.
|
|
198
|
+
* @param t - namespace-bound translate.
|
|
199
|
+
* @returns every string the kit renders.
|
|
200
|
+
*/
|
|
201
|
+
function dockLabels(t) {
|
|
202
|
+
return {
|
|
203
|
+
emptyPane: t("dock.emptyPane"),
|
|
204
|
+
splitPane: t("dock.splitPane"),
|
|
205
|
+
splitPaneDisabled: t("dock.splitPaneDisabled"),
|
|
206
|
+
splitPaneNarrow: t("dock.splitPaneNarrow"),
|
|
207
|
+
closeTab: t("dock.closeTab"),
|
|
208
|
+
addTab: t("dock.addTab"),
|
|
209
|
+
dockFloat: t("dock.dockFloat"),
|
|
210
|
+
closeFloat: t("dock.closeFloat")
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
//#endregion
|
|
214
|
+
//#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-sidebar-right/src/client/shell/SidebarRight.module.css.mjs
|
|
215
|
+
const css = ".P3OORG_panel{z-index:10;background:var(--dsw-alias-bg-base);border-left:.5px solid var(--dsw-alias-border-l1);visibility:hidden;min-width:0;transition:transform var(--ds-transition-duration-slow) var(--ds-ease-in-out), visibility 0s linear var(--ds-transition-duration-slow);flex-direction:column;display:flex;position:absolute;top:0;bottom:0;right:0;transform:translate(100%)}.P3OORG_panel[data-sidebar-right-open]{visibility:visible;transition:transform var(--ds-transition-duration-slow) var(--ds-ease-in-out);transform:none}.P3OORG_panel[data-sidebar-right-panel=fullscreen]{z-index:40;border:none;position:fixed;inset:0}@media (prefers-reduced-motion:reduce){.P3OORG_panel,.P3OORG_panel[data-sidebar-right-open]{transition:none}}.P3OORG_iconButton{width:24px;height:24px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:4px;flex:none;justify-content:center;align-items:center;padding:0;font-size:14px;line-height:1;display:flex}.P3OORG_iconButton:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}.P3OORG_panelBody{flex:auto;min-height:0;display:flex}.P3OORG_unavailable{color:var(--dsw-alias-label-tertiary);font-size:var(--dsh-content-font-size-secondary,13px);margin:0}.P3OORG_floatHost{z-index:60;pointer-events:none;position:fixed;inset:0}";
|
|
216
|
+
const tagId = "@deepseek-ai/dsh-client-ui-sidebar-right/SidebarRight.module.css";
|
|
217
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
218
|
+
const tag = document.createElement("style");
|
|
219
|
+
tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-sidebar-right";
|
|
220
|
+
tag.dataset.pluginCss = tagId;
|
|
221
|
+
tag.textContent = css;
|
|
222
|
+
document.head.appendChild(tag);
|
|
223
|
+
}
|
|
224
|
+
var SidebarRight_module_css_default = {
|
|
225
|
+
"floatHost": "P3OORG_floatHost",
|
|
226
|
+
"iconButton": "P3OORG_iconButton",
|
|
227
|
+
"panel": "P3OORG_panel",
|
|
228
|
+
"panelBody": "P3OORG_panelBody",
|
|
229
|
+
"unavailable": "P3OORG_unavailable"
|
|
230
|
+
};
|
|
231
|
+
//#endregion
|
|
232
|
+
//#region lib/types/client/shell/SidebarRight.js
|
|
233
|
+
/**
|
|
234
|
+
* The Sidebar's seat in the frame, and the panel it draws.
|
|
235
|
+
*
|
|
236
|
+
* The frame owns the right column's geometry; this package owns one content
|
|
237
|
+
* tree at the column width or fixed across the viewport. A shown wide panel
|
|
238
|
+
* retains its track in fullscreen, preserving the conversation width. Below
|
|
239
|
+
* 768px fullscreen is derived from viewport width, without changing manual mode.
|
|
240
|
+
*
|
|
241
|
+
* The panel stays mounted while collapsed, translated off the frame's right
|
|
242
|
+
* edge, so opening and closing are one gesture in both presentations: a slide
|
|
243
|
+
* from and to that edge. Normal presentation moves the frame's tracks with
|
|
244
|
+
* the panel. A fullscreen opening reserves its underlying track only after
|
|
245
|
+
* the panel covers the frame, without animating those hidden columns.
|
|
246
|
+
*
|
|
247
|
+
* The panel has no header of its own: its two controls — presentation switch
|
|
248
|
+
* and collapse — ride the docking kit's chrome seat at the end of the top-right
|
|
249
|
+
* pane's tab strip, so the strip is the panel's whole top edge. The way back in
|
|
250
|
+
* while collapsed is not here either: it is one button in the conversation
|
|
251
|
+
* header (`ExpandButton.tsx`), because it exists only while this panel is
|
|
252
|
+
* hidden. Floating panels portal out because they must cross the column and the
|
|
253
|
+
* conversation, and the kit already positions them in viewport coordinates.
|
|
254
|
+
*
|
|
255
|
+
* Tab bodies do not live here. Each one is a registration under its type's kind,
|
|
256
|
+
* dispatched through the keyed `sidebar.right.pane.tab` seat (and a live chip
|
|
257
|
+
* title through `sidebar.right.pane.tab.title`), so a new tab type needs no edit
|
|
258
|
+
* to this file. What a body receives beyond the record — navigation, lifetime
|
|
259
|
+
* signal, actions — is read through the slot-owned useTabInfo hook. The Tab
|
|
260
|
+
* domain follows each session's store commits, including sessions off screen.
|
|
261
|
+
*/
|
|
262
|
+
/** The guide tab one pane holds, if any: a pane holds at most one. */
|
|
263
|
+
function guideIn(layout, paneId) {
|
|
264
|
+
return (0, _deepseek_ai_dsh_client_ui_dockkit.findPaneContentTab)(layout, paneId, pageAddress(GUIDE_KIND), GUIDE_KIND);
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Build the kit's intent face for one session out of the store's actions.
|
|
268
|
+
* @param sessionId - the session the seat draws; every action is bound to it.
|
|
269
|
+
* @param actions - the seat's bound store actions.
|
|
270
|
+
* @param openTab - the navigation face's `openTab`, which the strip's add control asks for a guide through.
|
|
271
|
+
* @returns the intents the kit reports gestures to.
|
|
272
|
+
*/
|
|
273
|
+
function intentsFor(sessionId, actions, openTab) {
|
|
274
|
+
return {
|
|
275
|
+
focusTab: (tabId) => {
|
|
276
|
+
actions.focusTab(sessionId, tabId);
|
|
277
|
+
},
|
|
278
|
+
focusPane: (paneId) => {
|
|
279
|
+
actions.focusPane(sessionId, paneId);
|
|
280
|
+
},
|
|
281
|
+
splitPane: (paneId) => {
|
|
282
|
+
actions.splitPane(sessionId, paneId);
|
|
283
|
+
},
|
|
284
|
+
addTab: (paneId) => {
|
|
285
|
+
openTab(GUIDE_KIND, {
|
|
286
|
+
paneId,
|
|
287
|
+
revealIfOpened: false
|
|
288
|
+
});
|
|
289
|
+
},
|
|
290
|
+
closeTab: (tabId) => {
|
|
291
|
+
actions.closeTab(sessionId, tabId);
|
|
292
|
+
},
|
|
293
|
+
duplicateTab: (tabId) => {
|
|
294
|
+
actions.duplicateTab(sessionId, tabId);
|
|
295
|
+
},
|
|
296
|
+
floatTab: (tabId, rect) => {
|
|
297
|
+
actions.floatTab(sessionId, tabId, rect);
|
|
298
|
+
},
|
|
299
|
+
unfloatPane: (paneId) => {
|
|
300
|
+
actions.unfloatPane(sessionId, paneId);
|
|
301
|
+
},
|
|
302
|
+
placeTab: (tabId, toPaneId, index) => {
|
|
303
|
+
actions.placeTab(sessionId, tabId, toPaneId, index);
|
|
304
|
+
},
|
|
305
|
+
dropTab: (tabId, paneId, zone) => {
|
|
306
|
+
actions.dropTab(sessionId, tabId, paneId, zone);
|
|
307
|
+
},
|
|
308
|
+
moveFloat: (paneId, x, y) => {
|
|
309
|
+
actions.moveFloat(sessionId, paneId, x, y);
|
|
310
|
+
},
|
|
311
|
+
resizeFloat: (paneId, rect) => {
|
|
312
|
+
actions.resizeFloat(sessionId, paneId, rect);
|
|
313
|
+
},
|
|
314
|
+
resizeSplit: (splitId, sizes) => {
|
|
315
|
+
actions.resizeSplit(sessionId, splitId, sizes);
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Dispatch one tab's body or title with stable framework hooks and record lifetime.
|
|
321
|
+
*/
|
|
322
|
+
function TabSlot({ renderSlot, occurrence, useTabTypes, useTabNavigation, useStore, fullscreen, tab, seat, fallback }) {
|
|
323
|
+
const { signal, tabActions } = occurrence(tab);
|
|
324
|
+
const definition = useTabTypes((types) => types.find((definition) => definition.kind === tab.kind));
|
|
325
|
+
const hookContext = (0, react.useMemo)(() => ({
|
|
326
|
+
tabId: tab.id,
|
|
327
|
+
title: seat === "sidebar.right.pane.tab.title",
|
|
328
|
+
fullscreen,
|
|
329
|
+
signal,
|
|
330
|
+
actions: tabActions,
|
|
331
|
+
useStore,
|
|
332
|
+
useTabNavigation
|
|
333
|
+
}), [
|
|
334
|
+
tab.id,
|
|
335
|
+
seat,
|
|
336
|
+
fullscreen,
|
|
337
|
+
signal,
|
|
338
|
+
tabActions,
|
|
339
|
+
useStore,
|
|
340
|
+
useTabNavigation
|
|
341
|
+
]);
|
|
342
|
+
return renderSlot(seat, {}, {
|
|
343
|
+
entryKey: definition?.id ?? tab.kind,
|
|
344
|
+
fallback,
|
|
345
|
+
hookContext
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Dispatch a tab's body to its registered type.
|
|
350
|
+
*
|
|
351
|
+
* A kind with no registrant is a real state, not a defect: a session log can
|
|
352
|
+
* carry a tab whose type shipped in a plugin that is no longer mounted. Saying so
|
|
353
|
+
* is better than an empty pane.
|
|
354
|
+
*/
|
|
355
|
+
function bodiesFor(panel) {
|
|
356
|
+
const { t, ...rest } = panel;
|
|
357
|
+
return (tab) => (0, react_jsx_runtime.jsx)(TabSlot, {
|
|
358
|
+
...rest,
|
|
359
|
+
tab,
|
|
360
|
+
seat: "sidebar.right.pane.tab",
|
|
361
|
+
fallback: (0, react_jsx_runtime.jsx)("p", {
|
|
362
|
+
className: SidebarRight_module_css_default.unavailable,
|
|
363
|
+
"data-sidebar-right-unavailable": true,
|
|
364
|
+
children: t("tab.unavailable")
|
|
365
|
+
})
|
|
366
|
+
}, tab.id);
|
|
367
|
+
}
|
|
368
|
+
/** Dispatch a tab's title to its registered type; without one the chip shows the title captured at open time. */
|
|
369
|
+
function titlesFor(panel) {
|
|
370
|
+
return (tab) => (0, react_jsx_runtime.jsx)(TabSlot, {
|
|
371
|
+
...panel,
|
|
372
|
+
tab,
|
|
373
|
+
seat: "sidebar.right.pane.tab.title",
|
|
374
|
+
fallback: tab.title
|
|
375
|
+
}, tab.id);
|
|
376
|
+
}
|
|
377
|
+
/** Expand-to-viewport glyph. */
|
|
378
|
+
function FullscreenGlyph() {
|
|
379
|
+
return (0, react_jsx_runtime.jsx)("svg", {
|
|
380
|
+
width: "14",
|
|
381
|
+
height: "14",
|
|
382
|
+
viewBox: "0 0 14 14",
|
|
383
|
+
fill: "none",
|
|
384
|
+
"aria-hidden": "true",
|
|
385
|
+
children: (0, react_jsx_runtime.jsx)("path", {
|
|
386
|
+
d: "M5 1.5H1.5V5M9 1.5h3.5V5M1.5 9v3.5H5M12.5 9v3.5H9",
|
|
387
|
+
stroke: "currentColor",
|
|
388
|
+
strokeLinecap: "round"
|
|
389
|
+
})
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
/** Restore-from-fullscreen glyph. */
|
|
393
|
+
function ExitFullscreenGlyph() {
|
|
394
|
+
return (0, react_jsx_runtime.jsx)("svg", {
|
|
395
|
+
width: "14",
|
|
396
|
+
height: "14",
|
|
397
|
+
viewBox: "0 0 14 14",
|
|
398
|
+
fill: "none",
|
|
399
|
+
"aria-hidden": "true",
|
|
400
|
+
children: (0, react_jsx_runtime.jsx)("path", {
|
|
401
|
+
d: "M1.5 5H5V1.5M9 1.5V5h3.5M1.5 9H5v3.5M9 12.5V9h3.5",
|
|
402
|
+
stroke: "currentColor",
|
|
403
|
+
strokeLinecap: "round"
|
|
404
|
+
})
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
/** The collapse glyph. */
|
|
408
|
+
function CloseGlyph() {
|
|
409
|
+
return (0, react_jsx_runtime.jsx)("svg", {
|
|
410
|
+
width: "12",
|
|
411
|
+
height: "12",
|
|
412
|
+
viewBox: "0 0 12 12",
|
|
413
|
+
fill: "none",
|
|
414
|
+
"aria-hidden": "true",
|
|
415
|
+
children: (0, react_jsx_runtime.jsx)("path", {
|
|
416
|
+
d: "M2 2l8 8M10 2l-8 8",
|
|
417
|
+
stroke: "currentColor",
|
|
418
|
+
strokeWidth: "1.3",
|
|
419
|
+
strokeLinecap: "round"
|
|
420
|
+
})
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
/** The panel's two controls, placed by the kit at the top-right pane's strip end. */
|
|
424
|
+
function PanelChrome({ sessionId, fullscreen, autoFullscreen, actions, t }) {
|
|
425
|
+
const next = fullscreen ? "push" : "fullscreen";
|
|
426
|
+
return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("button", {
|
|
427
|
+
type: "button",
|
|
428
|
+
className: SidebarRight_module_css_default.iconButton,
|
|
429
|
+
"aria-label": fullscreen ? t("chrome.exitFullscreen") : t("chrome.toFullscreen"),
|
|
430
|
+
title: fullscreen ? t("chrome.exitFullscreen") : t("chrome.toFullscreen"),
|
|
431
|
+
"data-sidebar-right-mode": next,
|
|
432
|
+
onClick: () => {
|
|
433
|
+
if (fullscreen && autoFullscreen) actions.setExpanded(sessionId, false);
|
|
434
|
+
actions.setMode(sessionId, next);
|
|
435
|
+
},
|
|
436
|
+
children: fullscreen ? (0, react_jsx_runtime.jsx)(ExitFullscreenGlyph, {}) : (0, react_jsx_runtime.jsx)(FullscreenGlyph, {})
|
|
437
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
438
|
+
type: "button",
|
|
439
|
+
className: SidebarRight_module_css_default.iconButton,
|
|
440
|
+
"aria-label": t("chrome.collapse"),
|
|
441
|
+
title: t("chrome.collapse"),
|
|
442
|
+
"data-sidebar-right-toggle": true,
|
|
443
|
+
onClick: () => {
|
|
444
|
+
actions.toggleExpanded(sessionId);
|
|
445
|
+
},
|
|
446
|
+
children: (0, react_jsx_runtime.jsx)(CloseGlyph, {})
|
|
447
|
+
})] });
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* The panel: the docked surface with the two controls in its top-right strip,
|
|
451
|
+
* anchored to the frame's right edge and slid off it while collapsed.
|
|
452
|
+
*/
|
|
453
|
+
function SidebarPanel(panel) {
|
|
454
|
+
const { sessionId, surface, actions, t, renderSlot, openTab, width, reportRoom, fullscreen, autoFullscreen, panelRef } = panel;
|
|
455
|
+
const { expanded } = surface.layout;
|
|
456
|
+
return (0, react_jsx_runtime.jsx)("div", {
|
|
457
|
+
ref: panelRef,
|
|
458
|
+
className: SidebarRight_module_css_default.panel,
|
|
459
|
+
style: { width: fullscreen ? "100%" : width },
|
|
460
|
+
"data-sidebar-right-panel": fullscreen ? "fullscreen" : "push",
|
|
461
|
+
"data-sidebar-right-open": expanded || void 0,
|
|
462
|
+
"aria-hidden": !expanded || void 0,
|
|
463
|
+
children: (0, react_jsx_runtime.jsx)("div", {
|
|
464
|
+
className: SidebarRight_module_css_default.panelBody,
|
|
465
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_dockkit.DockSurface, {
|
|
466
|
+
state: surface.layout,
|
|
467
|
+
canSplit: (0, _deepseek_ai_dsh_client_ui_dockkit.canSplit)(surface.layout) && (0, _deepseek_ai_dsh_client_ui_dockkit.dockPaneIds)(surface.layout).length < 2,
|
|
468
|
+
hideSplitAtCapacity: true,
|
|
469
|
+
dropZones: "horizontal",
|
|
470
|
+
minPaneFraction: .2,
|
|
471
|
+
canAddTab: (paneId) => guideIn(surface.layout, paneId) === void 0,
|
|
472
|
+
intents: intentsFor(sessionId, actions, openTab),
|
|
473
|
+
labels: dockLabels(t),
|
|
474
|
+
renderTab: bodiesFor(panel),
|
|
475
|
+
renderTabTitle: titlesFor(panel),
|
|
476
|
+
renderTabMenuItems: (tab, dismiss) => renderSlot("sidebar.right.tab.menu.item", {
|
|
477
|
+
tab,
|
|
478
|
+
dismiss
|
|
479
|
+
}),
|
|
480
|
+
chrome: (0, react_jsx_runtime.jsx)(PanelChrome, {
|
|
481
|
+
sessionId,
|
|
482
|
+
fullscreen,
|
|
483
|
+
autoFullscreen,
|
|
484
|
+
actions,
|
|
485
|
+
t
|
|
486
|
+
}),
|
|
487
|
+
onRoom: reportRoom
|
|
488
|
+
})
|
|
489
|
+
})
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
/** Portal the floating layer out of whichever seat rendered it. */
|
|
493
|
+
function Floats(panel) {
|
|
494
|
+
const { sessionId, surface, actions, t, openTab } = panel;
|
|
495
|
+
if (surface.layout.floats.length === 0) return null;
|
|
496
|
+
return (0, react_dom.createPortal)((0, react_jsx_runtime.jsx)("div", {
|
|
497
|
+
className: SidebarRight_module_css_default.floatHost,
|
|
498
|
+
"data-sidebar-right-float-host": true,
|
|
499
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_dockkit.FloatLayer, {
|
|
500
|
+
state: surface.layout,
|
|
501
|
+
intents: intentsFor(sessionId, actions, openTab),
|
|
502
|
+
labels: dockLabels(t),
|
|
503
|
+
renderTab: bodiesFor(panel),
|
|
504
|
+
renderTabTitle: titlesFor(panel)
|
|
505
|
+
})
|
|
506
|
+
}), document.body);
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* The right column's occupant: the panel, anchored to the column's edge and
|
|
510
|
+
* shown or hidden by sliding, plus the floating layer. It is also where the
|
|
511
|
+
* frame learns the panel's presentation, and where `ctx.sidebarRight` learns
|
|
512
|
+
* which session it is acting on, because this is the seat that knows both.
|
|
513
|
+
*/
|
|
514
|
+
function RightbarSeat({ sessionId, width, viewportWidth, canShow, useStore, actions, t, renderSlot, syncPresentation, bindService, openTab, useTabTypes, useTabNavigation, occurrence }) {
|
|
515
|
+
const surfaces = useStore((state) => state.bySession);
|
|
516
|
+
const surface = surfaces[sessionId];
|
|
517
|
+
const shown = surface !== void 0 && surface.layout.expanded;
|
|
518
|
+
const autoFullscreen = viewportWidth < 768;
|
|
519
|
+
const fullscreen = autoFullscreen || surface?.layout.mode === "fullscreen";
|
|
520
|
+
const panelRef = (0, react.useRef)(null);
|
|
521
|
+
const room = (0, react.useRef)(/* @__PURE__ */ new Map());
|
|
522
|
+
const reportRoom = (0, react.useCallback)((fits) => {
|
|
523
|
+
room.current = fits;
|
|
524
|
+
}, []);
|
|
525
|
+
const track = shown && !autoFullscreen;
|
|
526
|
+
(0, react.useEffect)(() => {
|
|
527
|
+
if (surface === void 0) actions.open(sessionId);
|
|
528
|
+
}, [
|
|
529
|
+
actions,
|
|
530
|
+
sessionId,
|
|
531
|
+
surface
|
|
532
|
+
]);
|
|
533
|
+
(0, react.useLayoutEffect)(() => {
|
|
534
|
+
if (shown && !fullscreen && !canShow) actions.setExpanded(sessionId, false);
|
|
535
|
+
}, [
|
|
536
|
+
actions,
|
|
537
|
+
sessionId,
|
|
538
|
+
shown,
|
|
539
|
+
fullscreen,
|
|
540
|
+
canShow
|
|
541
|
+
]);
|
|
542
|
+
(0, react.useLayoutEffect)(() => {
|
|
543
|
+
let disposed = false;
|
|
544
|
+
const reportWhenCovered = () => {
|
|
545
|
+
if (disposed) return;
|
|
546
|
+
const entering = shown && fullscreen ? panelRef.current.getAnimations().filter((animation) => "transitionProperty" in animation && animation.transitionProperty === "transform" && animation.playState !== "finished" && animation.playState !== "idle") : [];
|
|
547
|
+
if (entering.length === 0) {
|
|
548
|
+
syncPresentation({
|
|
549
|
+
shown,
|
|
550
|
+
track,
|
|
551
|
+
fullscreen
|
|
552
|
+
});
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
Promise.allSettled(entering.map((animation) => animation.finished)).then(reportWhenCovered);
|
|
556
|
+
};
|
|
557
|
+
reportWhenCovered();
|
|
558
|
+
return () => {
|
|
559
|
+
disposed = true;
|
|
560
|
+
};
|
|
561
|
+
}, [
|
|
562
|
+
sessionId,
|
|
563
|
+
shown,
|
|
564
|
+
track,
|
|
565
|
+
fullscreen,
|
|
566
|
+
syncPresentation
|
|
567
|
+
]);
|
|
568
|
+
(0, react.useLayoutEffect)(() => () => {
|
|
569
|
+
syncPresentation({
|
|
570
|
+
shown: false,
|
|
571
|
+
track: false,
|
|
572
|
+
fullscreen: false
|
|
573
|
+
});
|
|
574
|
+
}, [syncPresentation]);
|
|
575
|
+
(0, react.useEffect)(() => bindService({
|
|
576
|
+
sessionId,
|
|
577
|
+
actions,
|
|
578
|
+
surfaces,
|
|
579
|
+
canSplitPane: (paneId) => room.current.get(paneId)?.row !== false
|
|
580
|
+
}), [
|
|
581
|
+
bindService,
|
|
582
|
+
sessionId,
|
|
583
|
+
actions,
|
|
584
|
+
surfaces
|
|
585
|
+
]);
|
|
586
|
+
if (surface === void 0) return null;
|
|
587
|
+
const panel = {
|
|
588
|
+
sessionId,
|
|
589
|
+
actions,
|
|
590
|
+
t,
|
|
591
|
+
renderSlot,
|
|
592
|
+
surface,
|
|
593
|
+
openTab,
|
|
594
|
+
useTabTypes,
|
|
595
|
+
useTabNavigation,
|
|
596
|
+
useStore,
|
|
597
|
+
occurrence,
|
|
598
|
+
fullscreen,
|
|
599
|
+
autoFullscreen,
|
|
600
|
+
reportRoom
|
|
601
|
+
};
|
|
602
|
+
return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(SidebarPanel, {
|
|
603
|
+
...panel,
|
|
604
|
+
width,
|
|
605
|
+
panelRef
|
|
606
|
+
}), (0, react_jsx_runtime.jsx)(Floats, { ...panel })] });
|
|
607
|
+
}
|
|
608
|
+
//#endregion
|
|
609
|
+
//#region lib/types/client/tab-domain.js
|
|
610
|
+
/** Every session's occurrences. */
|
|
611
|
+
var TabDomain = class {
|
|
612
|
+
navigator;
|
|
613
|
+
pin;
|
|
614
|
+
bySession = /* @__PURE__ */ new Map();
|
|
615
|
+
/**
|
|
616
|
+
* @param navigator - where tab actions go, aimed at the tab's session; the navigation controller.
|
|
617
|
+
* @param pin - `ctx.resources.pin`, called once per occurrence at its first sync.
|
|
618
|
+
*/
|
|
619
|
+
constructor(navigator, pin) {
|
|
620
|
+
this.navigator = navigator;
|
|
621
|
+
this.pin = pin;
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Reconcile one session's occurrences with its committed layout.
|
|
625
|
+
*
|
|
626
|
+
* Called by the seat after every commit, and only then: aborting a vanished
|
|
627
|
+
* record runs the types' cleanup, which writes their stores.
|
|
628
|
+
* @param sessionId - the session whose layout committed.
|
|
629
|
+
* @param layout - that session's layout as committed.
|
|
630
|
+
*/
|
|
631
|
+
sync(sessionId, layout) {
|
|
632
|
+
const held = this.session(sessionId);
|
|
633
|
+
for (const [tabId, occurrence] of held) {
|
|
634
|
+
if (layout.tabs[tabId] !== void 0) continue;
|
|
635
|
+
held.delete(tabId);
|
|
636
|
+
occurrence.controller.abort();
|
|
637
|
+
}
|
|
638
|
+
for (const tab of Object.values(layout.tabs)) {
|
|
639
|
+
const occurrence = held.get(tab.id) ?? this.hold(sessionId, tab.id, {
|
|
640
|
+
address: tab.contentId,
|
|
641
|
+
params: void 0,
|
|
642
|
+
revision: 0
|
|
643
|
+
});
|
|
644
|
+
const pane = (0, _deepseek_ai_dsh_client_ui_dockkit.findTabPane)(layout, tab.id);
|
|
645
|
+
occurrence.paneId = pane.host === "dock" ? pane.id : void 0;
|
|
646
|
+
if (occurrence.pinned) continue;
|
|
647
|
+
occurrence.pinned = true;
|
|
648
|
+
this.pin(occurrence.navigation.getSnapshot().address, occurrence.signal);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Read an occurrence created by navigation or committed-store reconciliation.
|
|
653
|
+
* @param sessionId - the session the record is in.
|
|
654
|
+
* @param tab - the record being drawn.
|
|
655
|
+
* @returns its occurrence.
|
|
656
|
+
* @throws when the record has not been reconciled or has disappeared.
|
|
657
|
+
*/
|
|
658
|
+
occurrence(sessionId, tab) {
|
|
659
|
+
const occurrence = this.bySession.get(sessionId)?.get(tab.id);
|
|
660
|
+
if (occurrence === void 0) throw new Error(`sidebarRight: tab "${tab.id}" has no committed occurrence in session "${sessionId}"`);
|
|
661
|
+
return occurrence;
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Record that an `open` settled on a tab.
|
|
665
|
+
*
|
|
666
|
+
* A record the layout has not yet shown the seat gets its occurrence here, so
|
|
667
|
+
* the body's first render already carries the opener's `params`.
|
|
668
|
+
* @param sessionId - the session opened into.
|
|
669
|
+
* @param tabId - the tab the open settled on.
|
|
670
|
+
* @param target - the address and the opener's params.
|
|
671
|
+
*/
|
|
672
|
+
navigate(sessionId, tabId, target) {
|
|
673
|
+
const existing = this.session(sessionId).get(tabId);
|
|
674
|
+
if (existing === void 0) {
|
|
675
|
+
this.hold(sessionId, tabId, {
|
|
676
|
+
...target,
|
|
677
|
+
revision: 1
|
|
678
|
+
});
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
existing.navigation.set({
|
|
682
|
+
...target,
|
|
683
|
+
revision: existing.navigation.getSnapshot().revision + 1
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
/** Abort every occurrence of every session; the package is unloading. */
|
|
687
|
+
dispose() {
|
|
688
|
+
for (const held of this.bySession.values()) for (const occurrence of held.values()) occurrence.controller.abort();
|
|
689
|
+
this.bySession.clear();
|
|
690
|
+
}
|
|
691
|
+
session(sessionId) {
|
|
692
|
+
let held = this.bySession.get(sessionId);
|
|
693
|
+
if (held === void 0) {
|
|
694
|
+
held = /* @__PURE__ */ new Map();
|
|
695
|
+
this.bySession.set(sessionId, held);
|
|
696
|
+
}
|
|
697
|
+
return held;
|
|
698
|
+
}
|
|
699
|
+
hold(sessionId, tabId, navigation) {
|
|
700
|
+
const controller = new AbortController();
|
|
701
|
+
const { navigator } = this;
|
|
702
|
+
const place = (placement) => ({
|
|
703
|
+
...placement.replaceTab === true ? { replaceTab: tabId } : held.paneId === void 0 ? {} : { paneId: held.paneId },
|
|
704
|
+
...placement.paneId === void 0 ? {} : { paneId: placement.paneId },
|
|
705
|
+
...placement.revealIfOpened === void 0 ? {} : { revealIfOpened: placement.revealIfOpened }
|
|
706
|
+
});
|
|
707
|
+
const held = {
|
|
708
|
+
sessionId,
|
|
709
|
+
tabId,
|
|
710
|
+
controller,
|
|
711
|
+
signal: controller.signal,
|
|
712
|
+
navigation: (0, _deepseek_ai_dsh_client_store.createSnapshotStore)(navigation),
|
|
713
|
+
paneId: void 0,
|
|
714
|
+
pinned: false,
|
|
715
|
+
tabActions: {
|
|
716
|
+
openResource: (address, options = {}) => {
|
|
717
|
+
navigator.openResourceIn(sessionId, address, {
|
|
718
|
+
...place(options),
|
|
719
|
+
params: options.params
|
|
720
|
+
});
|
|
721
|
+
},
|
|
722
|
+
openTab: (kind, options = {}) => {
|
|
723
|
+
navigator.openTabIn(sessionId, kind, {
|
|
724
|
+
...place(options),
|
|
725
|
+
params: options.params
|
|
726
|
+
});
|
|
727
|
+
},
|
|
728
|
+
close: () => {
|
|
729
|
+
navigator.closeIn(sessionId, tabId);
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
};
|
|
733
|
+
this.session(sessionId).set(tabId, held);
|
|
734
|
+
return held;
|
|
735
|
+
}
|
|
736
|
+
};
|
|
737
|
+
//#endregion
|
|
738
|
+
//#region lib/types/client/service.js
|
|
739
|
+
/**
|
|
740
|
+
* Create the public controller and the plugin-private store adoption callback.
|
|
741
|
+
* Adoption subscribes without reconciling; the first store commit creates occurrences.
|
|
742
|
+
* @param tabs - registered tab types.
|
|
743
|
+
* @param pin - resource retention for an occurrence's lifetime.
|
|
744
|
+
* @returns the controller and a callback releasing exactly its own adoption.
|
|
745
|
+
*/
|
|
746
|
+
function createSidebarRightController(tabs, pin) {
|
|
747
|
+
const adopted = /* @__PURE__ */ new Map();
|
|
748
|
+
const controller = new SidebarRightController(tabs, pin, adopted);
|
|
749
|
+
return {
|
|
750
|
+
controller,
|
|
751
|
+
adopt(sessionId, store) {
|
|
752
|
+
adopted.get(sessionId)?.unsubscribe();
|
|
753
|
+
const sync = () => {
|
|
754
|
+
const surface = store.getSnapshot().bySession[sessionId];
|
|
755
|
+
if (surface !== void 0) controller.tabDomain.sync(sessionId, surface.layout);
|
|
756
|
+
};
|
|
757
|
+
const adoption = {
|
|
758
|
+
store,
|
|
759
|
+
unsubscribe: store.subscribe(sync)
|
|
760
|
+
};
|
|
761
|
+
adopted.set(sessionId, adoption);
|
|
762
|
+
return () => {
|
|
763
|
+
adoption.unsubscribe();
|
|
764
|
+
if (adopted.get(sessionId) === adoption) adopted.delete(sessionId);
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
/** The scheme every resource address carries; anything else is not a resource this face opens. */
|
|
770
|
+
const RESOURCE_SCHEME = "dsh-resource://";
|
|
771
|
+
/** Cross-plugin right-Sidebar face (ctx.sidebarRight). */
|
|
772
|
+
var SidebarRightController = class {
|
|
773
|
+
tabs;
|
|
774
|
+
adopted;
|
|
775
|
+
binding;
|
|
776
|
+
/**
|
|
777
|
+
* The Tab domain this controller navigates into; synced from each adopted
|
|
778
|
+
* store's commits, read by the seat for each body's owner share.
|
|
779
|
+
*/
|
|
780
|
+
tabDomain;
|
|
781
|
+
/**
|
|
782
|
+
* @param tabs - the tab-type registry consulted to claim an address.
|
|
783
|
+
* @param pin - `ctx.resources.pin`, which the Tab domain holds addresses with.
|
|
784
|
+
* @param adopted - plugin-owned session stores used by occurrence actions.
|
|
785
|
+
*/
|
|
786
|
+
constructor(tabs, pin, adopted = /* @__PURE__ */ new Map()) {
|
|
787
|
+
this.tabs = tabs;
|
|
788
|
+
this.adopted = adopted;
|
|
789
|
+
this.tabDomain = new TabDomain(this, pin);
|
|
790
|
+
}
|
|
791
|
+
/**
|
|
792
|
+
* Adopt the mounted seat's binding, replacing any previous one.
|
|
793
|
+
*
|
|
794
|
+
* Called from the seat while it is mounted, and released when it leaves.
|
|
795
|
+
* @param binding - the mounted seat's session, actions, and the store's surfaces.
|
|
796
|
+
* @returns a release callback that clears exactly this binding.
|
|
797
|
+
*/
|
|
798
|
+
bind(binding) {
|
|
799
|
+
this.binding = binding;
|
|
800
|
+
return () => {
|
|
801
|
+
if (this.binding === binding) this.binding = void 0;
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
805
|
+
* Open a resource: claim it, place it, reveal the column, record the navigation.
|
|
806
|
+
* @param address - a `dsh-resource://<type>/…` address.
|
|
807
|
+
* @param options - placement, the opening type, and navigation parameters.
|
|
808
|
+
*/
|
|
809
|
+
openResource(address, options = {}) {
|
|
810
|
+
const { sessionId, actions } = this.require();
|
|
811
|
+
this.placeResource(sessionId, actions, address, options);
|
|
812
|
+
}
|
|
813
|
+
/**
|
|
814
|
+
* Open a page type by kind at the address this package records pages under.
|
|
815
|
+
* @param kind - the page type's kind.
|
|
816
|
+
* @param options - placement and that kind's navigation parameters.
|
|
817
|
+
*/
|
|
818
|
+
openTab(kind, options = {}) {
|
|
819
|
+
const { sessionId, actions } = this.require();
|
|
820
|
+
this.placeTab(sessionId, actions, kind, options);
|
|
821
|
+
}
|
|
822
|
+
/**
|
|
823
|
+
* Open a resource in one session, for a tab's own action; nothing happens
|
|
824
|
+
* for a session whose store was never adopted or whose adoption was released.
|
|
825
|
+
* Not part of `ISidebarRight`: the Tab domain's path.
|
|
826
|
+
* @param sessionId - the session the acting tab is in.
|
|
827
|
+
* @param address - a `dsh-resource://<type>/…` address.
|
|
828
|
+
* @param options - placement, the opening type, and navigation parameters.
|
|
829
|
+
*/
|
|
830
|
+
openResourceIn(sessionId, address, options = {}) {
|
|
831
|
+
const actions = this.actionsFor(sessionId);
|
|
832
|
+
if (actions !== void 0) this.placeResource(sessionId, actions, address, options);
|
|
833
|
+
}
|
|
834
|
+
/**
|
|
835
|
+
* Open a page type in one session, for a tab's own action; nothing happens
|
|
836
|
+
* for a session whose store was never adopted or whose adoption was released.
|
|
837
|
+
* Not part of `ISidebarRight`: the Tab domain's path.
|
|
838
|
+
* @param sessionId - the session the acting tab is in.
|
|
839
|
+
* @param kind - the page type's kind.
|
|
840
|
+
* @param options - placement and that kind's navigation parameters.
|
|
841
|
+
*/
|
|
842
|
+
openTabIn(sessionId, kind, options = {}) {
|
|
843
|
+
const actions = this.actionsFor(sessionId);
|
|
844
|
+
if (actions !== void 0) this.placeTab(sessionId, actions, kind, options);
|
|
845
|
+
}
|
|
846
|
+
/**
|
|
847
|
+
* Close a tab of one session, for the tab's own action; nothing happens
|
|
848
|
+
* for a session whose store was never adopted or whose adoption was released.
|
|
849
|
+
* Not part of `ISidebarRight`: the Tab domain's path.
|
|
850
|
+
* @param sessionId - the session the tab is in.
|
|
851
|
+
* @param tabId - the tab to close.
|
|
852
|
+
*/
|
|
853
|
+
closeIn(sessionId, tabId) {
|
|
854
|
+
const actions = this.actionsFor(sessionId);
|
|
855
|
+
if (actions !== void 0) actions.closeTab(sessionId, tabId);
|
|
856
|
+
}
|
|
857
|
+
/** Claim a resource and place it in one session; an address outside the scheme or one no type claims throws. */
|
|
858
|
+
placeResource(sessionId, actions, address, options) {
|
|
859
|
+
if (!address.startsWith(RESOURCE_SCHEME)) throw new Error(`sidebarRight: no registered tab type claims "${address}"`);
|
|
860
|
+
this.place(sessionId, actions, this.tabs.claim(address, options.kind), address, options, options.params);
|
|
861
|
+
}
|
|
862
|
+
/** Place a page type in one session at the address pages are recorded under; an unregistered kind throws. */
|
|
863
|
+
placeTab(sessionId, actions, kind, options) {
|
|
864
|
+
const definition = this.tabs.get(kind);
|
|
865
|
+
if (definition === void 0) throw new Error(`sidebarRight: no tab type is registered as "${kind}"`);
|
|
866
|
+
const address = pageAddress(kind);
|
|
867
|
+
this.place(sessionId, actions, {
|
|
868
|
+
kind,
|
|
869
|
+
contentId: address,
|
|
870
|
+
title: definition.title(address)
|
|
871
|
+
}, address, options, options.params);
|
|
872
|
+
}
|
|
873
|
+
/** The steps both opens share: one store intent, and the navigation record for the tab it settles on. */
|
|
874
|
+
place(sessionId, actions, claim, address, placement, params) {
|
|
875
|
+
actions.openContent(sessionId, {
|
|
876
|
+
kind: claim.kind,
|
|
877
|
+
contentId: claim.contentId,
|
|
878
|
+
title: claim.title,
|
|
879
|
+
...placement.paneId === void 0 ? {} : { paneId: placement.paneId },
|
|
880
|
+
...placement.replaceTab === void 0 ? {} : { replaceTab: placement.replaceTab },
|
|
881
|
+
...placement.revealIfOpened === void 0 ? {} : { revealIfOpened: placement.revealIfOpened }
|
|
882
|
+
}, (tabId) => {
|
|
883
|
+
this.tabDomain.navigate(sessionId, tabId, {
|
|
884
|
+
address,
|
|
885
|
+
params
|
|
886
|
+
});
|
|
887
|
+
});
|
|
888
|
+
}
|
|
889
|
+
/**
|
|
890
|
+
* Close one tab of the mounted session.
|
|
891
|
+
* @param tabId - the tab to close.
|
|
892
|
+
*/
|
|
893
|
+
close(tabId) {
|
|
894
|
+
const { sessionId, actions } = this.require();
|
|
895
|
+
actions.closeTab(sessionId, tabId);
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* The active tab of the active pane.
|
|
899
|
+
* @returns the record, or `undefined` with no mounted surface.
|
|
900
|
+
*/
|
|
901
|
+
active() {
|
|
902
|
+
const layout = this.mounted()?.layout;
|
|
903
|
+
if (layout === void 0) return void 0;
|
|
904
|
+
const { activeTabId } = (0, _deepseek_ai_dsh_client_ui_dockkit.getPane)(layout, layout.activePaneId);
|
|
905
|
+
return Object.values(layout.tabs).find((tab) => tab.id === activeTabId);
|
|
906
|
+
}
|
|
907
|
+
/**
|
|
908
|
+
* Whether the column is currently showing its panel.
|
|
909
|
+
* @returns `true` while expanded; `false` while collapsed or with no mounted surface.
|
|
910
|
+
*/
|
|
911
|
+
isExpanded() {
|
|
912
|
+
return this.mounted()?.layout.expanded ?? false;
|
|
913
|
+
}
|
|
914
|
+
/** Collapse an expanded column, or expand a collapsed one. */
|
|
915
|
+
toggleExpanded() {
|
|
916
|
+
const { sessionId, actions } = this.require();
|
|
917
|
+
actions.toggleExpanded(sessionId);
|
|
918
|
+
}
|
|
919
|
+
/**
|
|
920
|
+
* Focus a tab and the pane holding it; a missing tab is left alone.
|
|
921
|
+
* @param tabId - the tab to focus.
|
|
922
|
+
*/
|
|
923
|
+
focus(tabId) {
|
|
924
|
+
const { sessionId, actions } = this.require();
|
|
925
|
+
if (this.mounted()?.layout.tabs[tabId] === void 0) return;
|
|
926
|
+
actions.focusTab(sessionId, tabId);
|
|
927
|
+
}
|
|
928
|
+
/**
|
|
929
|
+
* Split a docked pane to its right when the budget and the room rule allow.
|
|
930
|
+
* @param paneId - the pane to split; defaults to the active docked pane.
|
|
931
|
+
* @returns the new pane's id, or `undefined` when nothing was split.
|
|
932
|
+
*/
|
|
933
|
+
split(paneId) {
|
|
934
|
+
const { sessionId, actions, canSplitPane } = this.require();
|
|
935
|
+
const layout = this.mounted()?.layout;
|
|
936
|
+
if (layout === void 0) return void 0;
|
|
937
|
+
const target = paneId ?? (0, _deepseek_ai_dsh_client_ui_dockkit.activeDockPaneId)(layout);
|
|
938
|
+
const node = layout.nodes[target];
|
|
939
|
+
if (node === void 0 || node.kind !== "pane" || node.host !== "dock") return void 0;
|
|
940
|
+
if (!(0, _deepseek_ai_dsh_client_ui_dockkit.canSplit)(layout) || (0, _deepseek_ai_dsh_client_ui_dockkit.dockPaneIds)(layout).length >= 2 || !canSplitPane(target)) return void 0;
|
|
941
|
+
let created;
|
|
942
|
+
actions.splitPane(sessionId, target, (id) => {
|
|
943
|
+
created = id;
|
|
944
|
+
});
|
|
945
|
+
return created;
|
|
946
|
+
}
|
|
947
|
+
/**
|
|
948
|
+
* Take a docked tab out into a floating panel; a missing or floating tab is left alone.
|
|
949
|
+
* @param tabId - the tab to float.
|
|
950
|
+
* @param rect - the panel's rectangle; defaults to the cascade from the last panel.
|
|
951
|
+
*/
|
|
952
|
+
float(tabId, rect) {
|
|
953
|
+
const { sessionId, actions } = this.require();
|
|
954
|
+
const layout = this.mounted()?.layout;
|
|
955
|
+
if (layout === void 0 || layout.tabs[tabId] === void 0) return;
|
|
956
|
+
if ((0, _deepseek_ai_dsh_client_ui_dockkit.findTabPane)(layout, tabId).host !== "dock") return;
|
|
957
|
+
actions.floatTab(sessionId, tabId, rect);
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* Return a floating panel's tab to the active docked pane; a missing or docked pane is left alone.
|
|
961
|
+
* @param paneId - the floating pane.
|
|
962
|
+
*/
|
|
963
|
+
dock(paneId) {
|
|
964
|
+
const { sessionId, actions } = this.require();
|
|
965
|
+
const node = this.mounted()?.layout.nodes[paneId];
|
|
966
|
+
if (node === void 0 || node.kind !== "pane" || node.host !== "float") return;
|
|
967
|
+
actions.unfloatPane(sessionId, paneId);
|
|
968
|
+
}
|
|
969
|
+
/**
|
|
970
|
+
* Step the mounted session's surface back one intent.
|
|
971
|
+
*
|
|
972
|
+
* @internal Not part of the product: the sequence is an architectural fact
|
|
973
|
+
* with no user-facing control yet. Kept reachable for tests.
|
|
974
|
+
*/
|
|
975
|
+
_undo() {
|
|
976
|
+
const { sessionId, actions } = this.require();
|
|
977
|
+
actions.undo(sessionId);
|
|
978
|
+
}
|
|
979
|
+
/**
|
|
980
|
+
* Step the mounted session's surface forward one intent.
|
|
981
|
+
*
|
|
982
|
+
* @internal See `_undo`.
|
|
983
|
+
*/
|
|
984
|
+
_redo() {
|
|
985
|
+
const { sessionId, actions } = this.require();
|
|
986
|
+
actions.redo(sessionId);
|
|
987
|
+
}
|
|
988
|
+
/** The mounted session's surface; `undefined` without a seat or before its first open. */
|
|
989
|
+
mounted() {
|
|
990
|
+
const { binding } = this;
|
|
991
|
+
return binding === void 0 ? void 0 : binding.surfaces[binding.sessionId];
|
|
992
|
+
}
|
|
993
|
+
/**
|
|
994
|
+
* The store actions a tab's own action on `sessionId` runs through: that
|
|
995
|
+
* session's adopted store. `undefined` — nothing to act on — for a session
|
|
996
|
+
* whose store was never minted or whose adoption was released.
|
|
997
|
+
*/
|
|
998
|
+
actionsFor(sessionId) {
|
|
999
|
+
return this.adopted.get(sessionId)?.store.actions;
|
|
1000
|
+
}
|
|
1001
|
+
require() {
|
|
1002
|
+
if (this.binding === void 0) throw new Error("sidebarRight: no session surface is mounted");
|
|
1003
|
+
return this.binding;
|
|
1004
|
+
}
|
|
1005
|
+
};
|
|
1006
|
+
//#endregion
|
|
1007
|
+
//#region ../../../node_modules/.pnpm/picomatch@4.0.4/node_modules/picomatch/lib/constants.js
|
|
1008
|
+
var require_constants = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
1009
|
+
const WIN_SLASH = "\\\\/";
|
|
1010
|
+
const WIN_NO_SLASH = `[^${WIN_SLASH}]`;
|
|
1011
|
+
const DEFAULT_MAX_EXTGLOB_RECURSION = 0;
|
|
1012
|
+
/**
|
|
1013
|
+
* Posix glob regex
|
|
1014
|
+
*/
|
|
1015
|
+
const DOT_LITERAL = "\\.";
|
|
1016
|
+
const PLUS_LITERAL = "\\+";
|
|
1017
|
+
const QMARK_LITERAL = "\\?";
|
|
1018
|
+
const SLASH_LITERAL = "\\/";
|
|
1019
|
+
const ONE_CHAR = "(?=.)";
|
|
1020
|
+
const QMARK = "[^/]";
|
|
1021
|
+
const END_ANCHOR = `(?:${SLASH_LITERAL}|$)`;
|
|
1022
|
+
const START_ANCHOR = `(?:^|${SLASH_LITERAL})`;
|
|
1023
|
+
const DOTS_SLASH = `${DOT_LITERAL}{1,2}${END_ANCHOR}`;
|
|
1024
|
+
const POSIX_CHARS = {
|
|
1025
|
+
DOT_LITERAL,
|
|
1026
|
+
PLUS_LITERAL,
|
|
1027
|
+
QMARK_LITERAL,
|
|
1028
|
+
SLASH_LITERAL,
|
|
1029
|
+
ONE_CHAR,
|
|
1030
|
+
QMARK,
|
|
1031
|
+
END_ANCHOR,
|
|
1032
|
+
DOTS_SLASH,
|
|
1033
|
+
NO_DOT: `(?!${DOT_LITERAL})`,
|
|
1034
|
+
NO_DOTS: `(?!${START_ANCHOR}${DOTS_SLASH})`,
|
|
1035
|
+
NO_DOT_SLASH: `(?!${DOT_LITERAL}{0,1}${END_ANCHOR})`,
|
|
1036
|
+
NO_DOTS_SLASH: `(?!${DOTS_SLASH})`,
|
|
1037
|
+
QMARK_NO_DOT: `[^.${SLASH_LITERAL}]`,
|
|
1038
|
+
STAR: `${QMARK}*?`,
|
|
1039
|
+
START_ANCHOR,
|
|
1040
|
+
SEP: "/"
|
|
1041
|
+
};
|
|
1042
|
+
/**
|
|
1043
|
+
* Windows glob regex
|
|
1044
|
+
*/
|
|
1045
|
+
const WINDOWS_CHARS = {
|
|
1046
|
+
...POSIX_CHARS,
|
|
1047
|
+
SLASH_LITERAL: `[${WIN_SLASH}]`,
|
|
1048
|
+
QMARK: WIN_NO_SLASH,
|
|
1049
|
+
STAR: `${WIN_NO_SLASH}*?`,
|
|
1050
|
+
DOTS_SLASH: `${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$)`,
|
|
1051
|
+
NO_DOT: `(?!${DOT_LITERAL})`,
|
|
1052
|
+
NO_DOTS: `(?!(?:^|[${WIN_SLASH}])${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`,
|
|
1053
|
+
NO_DOT_SLASH: `(?!${DOT_LITERAL}{0,1}(?:[${WIN_SLASH}]|$))`,
|
|
1054
|
+
NO_DOTS_SLASH: `(?!${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`,
|
|
1055
|
+
QMARK_NO_DOT: `[^.${WIN_SLASH}]`,
|
|
1056
|
+
START_ANCHOR: `(?:^|[${WIN_SLASH}])`,
|
|
1057
|
+
END_ANCHOR: `(?:[${WIN_SLASH}]|$)`,
|
|
1058
|
+
SEP: "\\"
|
|
1059
|
+
};
|
|
1060
|
+
module.exports = {
|
|
1061
|
+
DEFAULT_MAX_EXTGLOB_RECURSION,
|
|
1062
|
+
MAX_LENGTH: 1024 * 64,
|
|
1063
|
+
POSIX_REGEX_SOURCE: {
|
|
1064
|
+
__proto__: null,
|
|
1065
|
+
alnum: "a-zA-Z0-9",
|
|
1066
|
+
alpha: "a-zA-Z",
|
|
1067
|
+
ascii: "\\x00-\\x7F",
|
|
1068
|
+
blank: " \\t",
|
|
1069
|
+
cntrl: "\\x00-\\x1F\\x7F",
|
|
1070
|
+
digit: "0-9",
|
|
1071
|
+
graph: "\\x21-\\x7E",
|
|
1072
|
+
lower: "a-z",
|
|
1073
|
+
print: "\\x20-\\x7E ",
|
|
1074
|
+
punct: "\\-!\"#$%&'()\\*+,./:;<=>?@[\\]^_`{|}~",
|
|
1075
|
+
space: " \\t\\r\\n\\v\\f",
|
|
1076
|
+
upper: "A-Z",
|
|
1077
|
+
word: "A-Za-z0-9_",
|
|
1078
|
+
xdigit: "A-Fa-f0-9"
|
|
1079
|
+
},
|
|
1080
|
+
REGEX_BACKSLASH: /\\(?![*+?^${}(|)[\]])/g,
|
|
1081
|
+
REGEX_NON_SPECIAL_CHARS: /^[^@![\].,$*+?^{}()|\\/]+/,
|
|
1082
|
+
REGEX_SPECIAL_CHARS: /[-*+?.^${}(|)[\]]/,
|
|
1083
|
+
REGEX_SPECIAL_CHARS_BACKREF: /(\\?)((\W)(\3*))/g,
|
|
1084
|
+
REGEX_SPECIAL_CHARS_GLOBAL: /([-*+?.^${}(|)[\]])/g,
|
|
1085
|
+
REGEX_REMOVE_BACKSLASH: /(?:\[.*?[^\\]\]|\\(?=.))/g,
|
|
1086
|
+
REPLACEMENTS: {
|
|
1087
|
+
__proto__: null,
|
|
1088
|
+
"***": "*",
|
|
1089
|
+
"**/**": "**",
|
|
1090
|
+
"**/**/**": "**"
|
|
1091
|
+
},
|
|
1092
|
+
CHAR_0: 48,
|
|
1093
|
+
CHAR_9: 57,
|
|
1094
|
+
CHAR_UPPERCASE_A: 65,
|
|
1095
|
+
CHAR_LOWERCASE_A: 97,
|
|
1096
|
+
CHAR_UPPERCASE_Z: 90,
|
|
1097
|
+
CHAR_LOWERCASE_Z: 122,
|
|
1098
|
+
CHAR_LEFT_PARENTHESES: 40,
|
|
1099
|
+
CHAR_RIGHT_PARENTHESES: 41,
|
|
1100
|
+
CHAR_ASTERISK: 42,
|
|
1101
|
+
CHAR_AMPERSAND: 38,
|
|
1102
|
+
CHAR_AT: 64,
|
|
1103
|
+
CHAR_BACKWARD_SLASH: 92,
|
|
1104
|
+
CHAR_CARRIAGE_RETURN: 13,
|
|
1105
|
+
CHAR_CIRCUMFLEX_ACCENT: 94,
|
|
1106
|
+
CHAR_COLON: 58,
|
|
1107
|
+
CHAR_COMMA: 44,
|
|
1108
|
+
CHAR_DOT: 46,
|
|
1109
|
+
CHAR_DOUBLE_QUOTE: 34,
|
|
1110
|
+
CHAR_EQUAL: 61,
|
|
1111
|
+
CHAR_EXCLAMATION_MARK: 33,
|
|
1112
|
+
CHAR_FORM_FEED: 12,
|
|
1113
|
+
CHAR_FORWARD_SLASH: 47,
|
|
1114
|
+
CHAR_GRAVE_ACCENT: 96,
|
|
1115
|
+
CHAR_HASH: 35,
|
|
1116
|
+
CHAR_HYPHEN_MINUS: 45,
|
|
1117
|
+
CHAR_LEFT_ANGLE_BRACKET: 60,
|
|
1118
|
+
CHAR_LEFT_CURLY_BRACE: 123,
|
|
1119
|
+
CHAR_LEFT_SQUARE_BRACKET: 91,
|
|
1120
|
+
CHAR_LINE_FEED: 10,
|
|
1121
|
+
CHAR_NO_BREAK_SPACE: 160,
|
|
1122
|
+
CHAR_PERCENT: 37,
|
|
1123
|
+
CHAR_PLUS: 43,
|
|
1124
|
+
CHAR_QUESTION_MARK: 63,
|
|
1125
|
+
CHAR_RIGHT_ANGLE_BRACKET: 62,
|
|
1126
|
+
CHAR_RIGHT_CURLY_BRACE: 125,
|
|
1127
|
+
CHAR_RIGHT_SQUARE_BRACKET: 93,
|
|
1128
|
+
CHAR_SEMICOLON: 59,
|
|
1129
|
+
CHAR_SINGLE_QUOTE: 39,
|
|
1130
|
+
CHAR_SPACE: 32,
|
|
1131
|
+
CHAR_TAB: 9,
|
|
1132
|
+
CHAR_UNDERSCORE: 95,
|
|
1133
|
+
CHAR_VERTICAL_LINE: 124,
|
|
1134
|
+
CHAR_ZERO_WIDTH_NOBREAK_SPACE: 65279,
|
|
1135
|
+
/**
|
|
1136
|
+
* Create EXTGLOB_CHARS
|
|
1137
|
+
*/
|
|
1138
|
+
extglobChars(chars) {
|
|
1139
|
+
return {
|
|
1140
|
+
"!": {
|
|
1141
|
+
type: "negate",
|
|
1142
|
+
open: "(?:(?!(?:",
|
|
1143
|
+
close: `))${chars.STAR})`
|
|
1144
|
+
},
|
|
1145
|
+
"?": {
|
|
1146
|
+
type: "qmark",
|
|
1147
|
+
open: "(?:",
|
|
1148
|
+
close: ")?"
|
|
1149
|
+
},
|
|
1150
|
+
"+": {
|
|
1151
|
+
type: "plus",
|
|
1152
|
+
open: "(?:",
|
|
1153
|
+
close: ")+"
|
|
1154
|
+
},
|
|
1155
|
+
"*": {
|
|
1156
|
+
type: "star",
|
|
1157
|
+
open: "(?:",
|
|
1158
|
+
close: ")*"
|
|
1159
|
+
},
|
|
1160
|
+
"@": {
|
|
1161
|
+
type: "at",
|
|
1162
|
+
open: "(?:",
|
|
1163
|
+
close: ")"
|
|
1164
|
+
}
|
|
1165
|
+
};
|
|
1166
|
+
},
|
|
1167
|
+
/**
|
|
1168
|
+
* Create GLOB_CHARS
|
|
1169
|
+
*/
|
|
1170
|
+
globChars(win32) {
|
|
1171
|
+
return win32 === true ? WINDOWS_CHARS : POSIX_CHARS;
|
|
1172
|
+
}
|
|
1173
|
+
};
|
|
1174
|
+
}));
|
|
1175
|
+
//#endregion
|
|
1176
|
+
//#region ../../../node_modules/.pnpm/picomatch@4.0.4/node_modules/picomatch/lib/utils.js
|
|
1177
|
+
var require_utils = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
1178
|
+
const { REGEX_BACKSLASH, REGEX_REMOVE_BACKSLASH, REGEX_SPECIAL_CHARS, REGEX_SPECIAL_CHARS_GLOBAL } = require_constants();
|
|
1179
|
+
exports.isObject = (val) => val !== null && typeof val === "object" && !Array.isArray(val);
|
|
1180
|
+
exports.hasRegexChars = (str) => REGEX_SPECIAL_CHARS.test(str);
|
|
1181
|
+
exports.isRegexChar = (str) => str.length === 1 && exports.hasRegexChars(str);
|
|
1182
|
+
exports.escapeRegex = (str) => str.replace(REGEX_SPECIAL_CHARS_GLOBAL, "\\$1");
|
|
1183
|
+
exports.toPosixSlashes = (str) => str.replace(REGEX_BACKSLASH, "/");
|
|
1184
|
+
exports.isWindows = () => {
|
|
1185
|
+
if (typeof navigator !== "undefined" && navigator.platform) {
|
|
1186
|
+
const platform = navigator.platform.toLowerCase();
|
|
1187
|
+
return platform === "win32" || platform === "windows";
|
|
1188
|
+
}
|
|
1189
|
+
if (typeof process !== "undefined" && process.platform) return process.platform === "win32";
|
|
1190
|
+
return false;
|
|
1191
|
+
};
|
|
1192
|
+
exports.removeBackslashes = (str) => {
|
|
1193
|
+
return str.replace(REGEX_REMOVE_BACKSLASH, (match) => {
|
|
1194
|
+
return match === "\\" ? "" : match;
|
|
1195
|
+
});
|
|
1196
|
+
};
|
|
1197
|
+
exports.escapeLast = (input, char, lastIdx) => {
|
|
1198
|
+
const idx = input.lastIndexOf(char, lastIdx);
|
|
1199
|
+
if (idx === -1) return input;
|
|
1200
|
+
if (input[idx - 1] === "\\") return exports.escapeLast(input, char, idx - 1);
|
|
1201
|
+
return `${input.slice(0, idx)}\\${input.slice(idx)}`;
|
|
1202
|
+
};
|
|
1203
|
+
exports.removePrefix = (input, state = {}) => {
|
|
1204
|
+
let output = input;
|
|
1205
|
+
if (output.startsWith("./")) {
|
|
1206
|
+
output = output.slice(2);
|
|
1207
|
+
state.prefix = "./";
|
|
1208
|
+
}
|
|
1209
|
+
return output;
|
|
1210
|
+
};
|
|
1211
|
+
exports.wrapOutput = (input, state = {}, options = {}) => {
|
|
1212
|
+
let output = `${options.contains ? "" : "^"}(?:${input})${options.contains ? "" : "$"}`;
|
|
1213
|
+
if (state.negated === true) output = `(?:^(?!${output}).*$)`;
|
|
1214
|
+
return output;
|
|
1215
|
+
};
|
|
1216
|
+
exports.basename = (path, { windows } = {}) => {
|
|
1217
|
+
const segs = path.split(windows ? /[\\/]/ : "/");
|
|
1218
|
+
const last = segs[segs.length - 1];
|
|
1219
|
+
if (last === "") return segs[segs.length - 2];
|
|
1220
|
+
return last;
|
|
1221
|
+
};
|
|
1222
|
+
}));
|
|
1223
|
+
//#endregion
|
|
1224
|
+
//#region ../../../node_modules/.pnpm/picomatch@4.0.4/node_modules/picomatch/lib/scan.js
|
|
1225
|
+
var require_scan = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
1226
|
+
const utils = require_utils();
|
|
1227
|
+
const { CHAR_ASTERISK, CHAR_AT, CHAR_BACKWARD_SLASH, CHAR_COMMA, CHAR_DOT, CHAR_EXCLAMATION_MARK, CHAR_FORWARD_SLASH, CHAR_LEFT_CURLY_BRACE, CHAR_LEFT_PARENTHESES, CHAR_LEFT_SQUARE_BRACKET, CHAR_PLUS, CHAR_QUESTION_MARK, CHAR_RIGHT_CURLY_BRACE, CHAR_RIGHT_PARENTHESES, CHAR_RIGHT_SQUARE_BRACKET } = require_constants();
|
|
1228
|
+
const isPathSeparator = (code) => {
|
|
1229
|
+
return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH;
|
|
1230
|
+
};
|
|
1231
|
+
const depth = (token) => {
|
|
1232
|
+
if (token.isPrefix !== true) token.depth = token.isGlobstar ? Infinity : 1;
|
|
1233
|
+
};
|
|
1234
|
+
/**
|
|
1235
|
+
* Quickly scans a glob pattern and returns an object with a handful of
|
|
1236
|
+
* useful properties, like `isGlob`, `path` (the leading non-glob, if it exists),
|
|
1237
|
+
* `glob` (the actual pattern), `negated` (true if the path starts with `!` but not
|
|
1238
|
+
* with `!(`) and `negatedExtglob` (true if the path starts with `!(`).
|
|
1239
|
+
*
|
|
1240
|
+
* ```js
|
|
1241
|
+
* const pm = require('picomatch');
|
|
1242
|
+
* console.log(pm.scan('foo/bar/*.js'));
|
|
1243
|
+
* { isGlob: true, input: 'foo/bar/*.js', base: 'foo/bar', glob: '*.js' }
|
|
1244
|
+
* ```
|
|
1245
|
+
* @param {String} `str`
|
|
1246
|
+
* @param {Object} `options`
|
|
1247
|
+
* @return {Object} Returns an object with tokens and regex source string.
|
|
1248
|
+
* @api public
|
|
1249
|
+
*/
|
|
1250
|
+
const scan = (input, options) => {
|
|
1251
|
+
const opts = options || {};
|
|
1252
|
+
const length = input.length - 1;
|
|
1253
|
+
const scanToEnd = opts.parts === true || opts.scanToEnd === true;
|
|
1254
|
+
const slashes = [];
|
|
1255
|
+
const tokens = [];
|
|
1256
|
+
const parts = [];
|
|
1257
|
+
let str = input;
|
|
1258
|
+
let index = -1;
|
|
1259
|
+
let start = 0;
|
|
1260
|
+
let lastIndex = 0;
|
|
1261
|
+
let isBrace = false;
|
|
1262
|
+
let isBracket = false;
|
|
1263
|
+
let isGlob = false;
|
|
1264
|
+
let isExtglob = false;
|
|
1265
|
+
let isGlobstar = false;
|
|
1266
|
+
let braceEscaped = false;
|
|
1267
|
+
let backslashes = false;
|
|
1268
|
+
let negated = false;
|
|
1269
|
+
let negatedExtglob = false;
|
|
1270
|
+
let finished = false;
|
|
1271
|
+
let braces = 0;
|
|
1272
|
+
let prev;
|
|
1273
|
+
let code;
|
|
1274
|
+
let token = {
|
|
1275
|
+
value: "",
|
|
1276
|
+
depth: 0,
|
|
1277
|
+
isGlob: false
|
|
1278
|
+
};
|
|
1279
|
+
const eos = () => index >= length;
|
|
1280
|
+
const peek = () => str.charCodeAt(index + 1);
|
|
1281
|
+
const advance = () => {
|
|
1282
|
+
prev = code;
|
|
1283
|
+
return str.charCodeAt(++index);
|
|
1284
|
+
};
|
|
1285
|
+
while (index < length) {
|
|
1286
|
+
code = advance();
|
|
1287
|
+
let next;
|
|
1288
|
+
if (code === CHAR_BACKWARD_SLASH) {
|
|
1289
|
+
backslashes = token.backslashes = true;
|
|
1290
|
+
code = advance();
|
|
1291
|
+
if (code === CHAR_LEFT_CURLY_BRACE) braceEscaped = true;
|
|
1292
|
+
continue;
|
|
1293
|
+
}
|
|
1294
|
+
if (braceEscaped === true || code === CHAR_LEFT_CURLY_BRACE) {
|
|
1295
|
+
braces++;
|
|
1296
|
+
while (eos() !== true && (code = advance())) {
|
|
1297
|
+
if (code === CHAR_BACKWARD_SLASH) {
|
|
1298
|
+
backslashes = token.backslashes = true;
|
|
1299
|
+
advance();
|
|
1300
|
+
continue;
|
|
1301
|
+
}
|
|
1302
|
+
if (code === CHAR_LEFT_CURLY_BRACE) {
|
|
1303
|
+
braces++;
|
|
1304
|
+
continue;
|
|
1305
|
+
}
|
|
1306
|
+
if (braceEscaped !== true && code === CHAR_DOT && (code = advance()) === CHAR_DOT) {
|
|
1307
|
+
isBrace = token.isBrace = true;
|
|
1308
|
+
isGlob = token.isGlob = true;
|
|
1309
|
+
finished = true;
|
|
1310
|
+
if (scanToEnd === true) continue;
|
|
1311
|
+
break;
|
|
1312
|
+
}
|
|
1313
|
+
if (braceEscaped !== true && code === CHAR_COMMA) {
|
|
1314
|
+
isBrace = token.isBrace = true;
|
|
1315
|
+
isGlob = token.isGlob = true;
|
|
1316
|
+
finished = true;
|
|
1317
|
+
if (scanToEnd === true) continue;
|
|
1318
|
+
break;
|
|
1319
|
+
}
|
|
1320
|
+
if (code === CHAR_RIGHT_CURLY_BRACE) {
|
|
1321
|
+
braces--;
|
|
1322
|
+
if (braces === 0) {
|
|
1323
|
+
braceEscaped = false;
|
|
1324
|
+
isBrace = token.isBrace = true;
|
|
1325
|
+
finished = true;
|
|
1326
|
+
break;
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
if (scanToEnd === true) continue;
|
|
1331
|
+
break;
|
|
1332
|
+
}
|
|
1333
|
+
if (code === CHAR_FORWARD_SLASH) {
|
|
1334
|
+
slashes.push(index);
|
|
1335
|
+
tokens.push(token);
|
|
1336
|
+
token = {
|
|
1337
|
+
value: "",
|
|
1338
|
+
depth: 0,
|
|
1339
|
+
isGlob: false
|
|
1340
|
+
};
|
|
1341
|
+
if (finished === true) continue;
|
|
1342
|
+
if (prev === CHAR_DOT && index === start + 1) {
|
|
1343
|
+
start += 2;
|
|
1344
|
+
continue;
|
|
1345
|
+
}
|
|
1346
|
+
lastIndex = index + 1;
|
|
1347
|
+
continue;
|
|
1348
|
+
}
|
|
1349
|
+
if (opts.noext !== true) {
|
|
1350
|
+
if ((code === CHAR_PLUS || code === CHAR_AT || code === CHAR_ASTERISK || code === CHAR_QUESTION_MARK || code === CHAR_EXCLAMATION_MARK) === true && peek() === CHAR_LEFT_PARENTHESES) {
|
|
1351
|
+
isGlob = token.isGlob = true;
|
|
1352
|
+
isExtglob = token.isExtglob = true;
|
|
1353
|
+
finished = true;
|
|
1354
|
+
if (code === CHAR_EXCLAMATION_MARK && index === start) negatedExtglob = true;
|
|
1355
|
+
if (scanToEnd === true) {
|
|
1356
|
+
while (eos() !== true && (code = advance())) {
|
|
1357
|
+
if (code === CHAR_BACKWARD_SLASH) {
|
|
1358
|
+
backslashes = token.backslashes = true;
|
|
1359
|
+
code = advance();
|
|
1360
|
+
continue;
|
|
1361
|
+
}
|
|
1362
|
+
if (code === CHAR_RIGHT_PARENTHESES) {
|
|
1363
|
+
isGlob = token.isGlob = true;
|
|
1364
|
+
finished = true;
|
|
1365
|
+
break;
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
continue;
|
|
1369
|
+
}
|
|
1370
|
+
break;
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
if (code === CHAR_ASTERISK) {
|
|
1374
|
+
if (prev === CHAR_ASTERISK) isGlobstar = token.isGlobstar = true;
|
|
1375
|
+
isGlob = token.isGlob = true;
|
|
1376
|
+
finished = true;
|
|
1377
|
+
if (scanToEnd === true) continue;
|
|
1378
|
+
break;
|
|
1379
|
+
}
|
|
1380
|
+
if (code === CHAR_QUESTION_MARK) {
|
|
1381
|
+
isGlob = token.isGlob = true;
|
|
1382
|
+
finished = true;
|
|
1383
|
+
if (scanToEnd === true) continue;
|
|
1384
|
+
break;
|
|
1385
|
+
}
|
|
1386
|
+
if (code === CHAR_LEFT_SQUARE_BRACKET) {
|
|
1387
|
+
while (eos() !== true && (next = advance())) {
|
|
1388
|
+
if (next === CHAR_BACKWARD_SLASH) {
|
|
1389
|
+
backslashes = token.backslashes = true;
|
|
1390
|
+
advance();
|
|
1391
|
+
continue;
|
|
1392
|
+
}
|
|
1393
|
+
if (next === CHAR_RIGHT_SQUARE_BRACKET) {
|
|
1394
|
+
isBracket = token.isBracket = true;
|
|
1395
|
+
isGlob = token.isGlob = true;
|
|
1396
|
+
finished = true;
|
|
1397
|
+
break;
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
if (scanToEnd === true) continue;
|
|
1401
|
+
break;
|
|
1402
|
+
}
|
|
1403
|
+
if (opts.nonegate !== true && code === CHAR_EXCLAMATION_MARK && index === start) {
|
|
1404
|
+
negated = token.negated = true;
|
|
1405
|
+
start++;
|
|
1406
|
+
continue;
|
|
1407
|
+
}
|
|
1408
|
+
if (opts.noparen !== true && code === CHAR_LEFT_PARENTHESES) {
|
|
1409
|
+
isGlob = token.isGlob = true;
|
|
1410
|
+
if (scanToEnd === true) {
|
|
1411
|
+
while (eos() !== true && (code = advance())) {
|
|
1412
|
+
if (code === CHAR_LEFT_PARENTHESES) {
|
|
1413
|
+
backslashes = token.backslashes = true;
|
|
1414
|
+
code = advance();
|
|
1415
|
+
continue;
|
|
1416
|
+
}
|
|
1417
|
+
if (code === CHAR_RIGHT_PARENTHESES) {
|
|
1418
|
+
finished = true;
|
|
1419
|
+
break;
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
continue;
|
|
1423
|
+
}
|
|
1424
|
+
break;
|
|
1425
|
+
}
|
|
1426
|
+
if (isGlob === true) {
|
|
1427
|
+
finished = true;
|
|
1428
|
+
if (scanToEnd === true) continue;
|
|
1429
|
+
break;
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
if (opts.noext === true) {
|
|
1433
|
+
isExtglob = false;
|
|
1434
|
+
isGlob = false;
|
|
1435
|
+
}
|
|
1436
|
+
let base = str;
|
|
1437
|
+
let prefix = "";
|
|
1438
|
+
let glob = "";
|
|
1439
|
+
if (start > 0) {
|
|
1440
|
+
prefix = str.slice(0, start);
|
|
1441
|
+
str = str.slice(start);
|
|
1442
|
+
lastIndex -= start;
|
|
1443
|
+
}
|
|
1444
|
+
if (base && isGlob === true && lastIndex > 0) {
|
|
1445
|
+
base = str.slice(0, lastIndex);
|
|
1446
|
+
glob = str.slice(lastIndex);
|
|
1447
|
+
} else if (isGlob === true) {
|
|
1448
|
+
base = "";
|
|
1449
|
+
glob = str;
|
|
1450
|
+
} else base = str;
|
|
1451
|
+
if (base && base !== "" && base !== "/" && base !== str) {
|
|
1452
|
+
if (isPathSeparator(base.charCodeAt(base.length - 1))) base = base.slice(0, -1);
|
|
1453
|
+
}
|
|
1454
|
+
if (opts.unescape === true) {
|
|
1455
|
+
if (glob) glob = utils.removeBackslashes(glob);
|
|
1456
|
+
if (base && backslashes === true) base = utils.removeBackslashes(base);
|
|
1457
|
+
}
|
|
1458
|
+
const state = {
|
|
1459
|
+
prefix,
|
|
1460
|
+
input,
|
|
1461
|
+
start,
|
|
1462
|
+
base,
|
|
1463
|
+
glob,
|
|
1464
|
+
isBrace,
|
|
1465
|
+
isBracket,
|
|
1466
|
+
isGlob,
|
|
1467
|
+
isExtglob,
|
|
1468
|
+
isGlobstar,
|
|
1469
|
+
negated,
|
|
1470
|
+
negatedExtglob
|
|
1471
|
+
};
|
|
1472
|
+
if (opts.tokens === true) {
|
|
1473
|
+
state.maxDepth = 0;
|
|
1474
|
+
if (!isPathSeparator(code)) tokens.push(token);
|
|
1475
|
+
state.tokens = tokens;
|
|
1476
|
+
}
|
|
1477
|
+
if (opts.parts === true || opts.tokens === true) {
|
|
1478
|
+
let prevIndex;
|
|
1479
|
+
for (let idx = 0; idx < slashes.length; idx++) {
|
|
1480
|
+
const n = prevIndex ? prevIndex + 1 : start;
|
|
1481
|
+
const i = slashes[idx];
|
|
1482
|
+
const value = input.slice(n, i);
|
|
1483
|
+
if (opts.tokens) {
|
|
1484
|
+
if (idx === 0 && start !== 0) {
|
|
1485
|
+
tokens[idx].isPrefix = true;
|
|
1486
|
+
tokens[idx].value = prefix;
|
|
1487
|
+
} else tokens[idx].value = value;
|
|
1488
|
+
depth(tokens[idx]);
|
|
1489
|
+
state.maxDepth += tokens[idx].depth;
|
|
1490
|
+
}
|
|
1491
|
+
if (idx !== 0 || value !== "") parts.push(value);
|
|
1492
|
+
prevIndex = i;
|
|
1493
|
+
}
|
|
1494
|
+
if (prevIndex && prevIndex + 1 < input.length) {
|
|
1495
|
+
const value = input.slice(prevIndex + 1);
|
|
1496
|
+
parts.push(value);
|
|
1497
|
+
if (opts.tokens) {
|
|
1498
|
+
tokens[tokens.length - 1].value = value;
|
|
1499
|
+
depth(tokens[tokens.length - 1]);
|
|
1500
|
+
state.maxDepth += tokens[tokens.length - 1].depth;
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
state.slashes = slashes;
|
|
1504
|
+
state.parts = parts;
|
|
1505
|
+
}
|
|
1506
|
+
return state;
|
|
1507
|
+
};
|
|
1508
|
+
module.exports = scan;
|
|
1509
|
+
}));
|
|
1510
|
+
//#endregion
|
|
1511
|
+
//#region ../../../node_modules/.pnpm/picomatch@4.0.4/node_modules/picomatch/lib/parse.js
|
|
1512
|
+
var require_parse = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
1513
|
+
const constants = require_constants();
|
|
1514
|
+
const utils = require_utils();
|
|
1515
|
+
/**
|
|
1516
|
+
* Constants
|
|
1517
|
+
*/
|
|
1518
|
+
const { MAX_LENGTH, POSIX_REGEX_SOURCE, REGEX_NON_SPECIAL_CHARS, REGEX_SPECIAL_CHARS_BACKREF, REPLACEMENTS } = constants;
|
|
1519
|
+
/**
|
|
1520
|
+
* Helpers
|
|
1521
|
+
*/
|
|
1522
|
+
const expandRange = (args, options) => {
|
|
1523
|
+
if (typeof options.expandRange === "function") return options.expandRange(...args, options);
|
|
1524
|
+
args.sort();
|
|
1525
|
+
const value = `[${args.join("-")}]`;
|
|
1526
|
+
try {
|
|
1527
|
+
new RegExp(value);
|
|
1528
|
+
} catch (ex) {
|
|
1529
|
+
return args.map((v) => utils.escapeRegex(v)).join("..");
|
|
1530
|
+
}
|
|
1531
|
+
return value;
|
|
1532
|
+
};
|
|
1533
|
+
/**
|
|
1534
|
+
* Create the message for a syntax error
|
|
1535
|
+
*/
|
|
1536
|
+
const syntaxError = (type, char) => {
|
|
1537
|
+
return `Missing ${type}: "${char}" - use "\\\\${char}" to match literal characters`;
|
|
1538
|
+
};
|
|
1539
|
+
const splitTopLevel = (input) => {
|
|
1540
|
+
const parts = [];
|
|
1541
|
+
let bracket = 0;
|
|
1542
|
+
let paren = 0;
|
|
1543
|
+
let quote = 0;
|
|
1544
|
+
let value = "";
|
|
1545
|
+
let escaped = false;
|
|
1546
|
+
for (const ch of input) {
|
|
1547
|
+
if (escaped === true) {
|
|
1548
|
+
value += ch;
|
|
1549
|
+
escaped = false;
|
|
1550
|
+
continue;
|
|
1551
|
+
}
|
|
1552
|
+
if (ch === "\\") {
|
|
1553
|
+
value += ch;
|
|
1554
|
+
escaped = true;
|
|
1555
|
+
continue;
|
|
1556
|
+
}
|
|
1557
|
+
if (ch === "\"") {
|
|
1558
|
+
quote = quote === 1 ? 0 : 1;
|
|
1559
|
+
value += ch;
|
|
1560
|
+
continue;
|
|
1561
|
+
}
|
|
1562
|
+
if (quote === 0) {
|
|
1563
|
+
if (ch === "[") bracket++;
|
|
1564
|
+
else if (ch === "]" && bracket > 0) bracket--;
|
|
1565
|
+
else if (bracket === 0) {
|
|
1566
|
+
if (ch === "(") paren++;
|
|
1567
|
+
else if (ch === ")" && paren > 0) paren--;
|
|
1568
|
+
else if (ch === "|" && paren === 0) {
|
|
1569
|
+
parts.push(value);
|
|
1570
|
+
value = "";
|
|
1571
|
+
continue;
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
value += ch;
|
|
1576
|
+
}
|
|
1577
|
+
parts.push(value);
|
|
1578
|
+
return parts;
|
|
1579
|
+
};
|
|
1580
|
+
const isPlainBranch = (branch) => {
|
|
1581
|
+
let escaped = false;
|
|
1582
|
+
for (const ch of branch) {
|
|
1583
|
+
if (escaped === true) {
|
|
1584
|
+
escaped = false;
|
|
1585
|
+
continue;
|
|
1586
|
+
}
|
|
1587
|
+
if (ch === "\\") {
|
|
1588
|
+
escaped = true;
|
|
1589
|
+
continue;
|
|
1590
|
+
}
|
|
1591
|
+
if (/[?*+@!()[\]{}]/.test(ch)) return false;
|
|
1592
|
+
}
|
|
1593
|
+
return true;
|
|
1594
|
+
};
|
|
1595
|
+
const normalizeSimpleBranch = (branch) => {
|
|
1596
|
+
let value = branch.trim();
|
|
1597
|
+
let changed = true;
|
|
1598
|
+
while (changed === true) {
|
|
1599
|
+
changed = false;
|
|
1600
|
+
if (/^@\([^\\()[\]{}|]+\)$/.test(value)) {
|
|
1601
|
+
value = value.slice(2, -1);
|
|
1602
|
+
changed = true;
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
if (!isPlainBranch(value)) return;
|
|
1606
|
+
return value.replace(/\\(.)/g, "$1");
|
|
1607
|
+
};
|
|
1608
|
+
const hasRepeatedCharPrefixOverlap = (branches) => {
|
|
1609
|
+
const values = branches.map(normalizeSimpleBranch).filter(Boolean);
|
|
1610
|
+
for (let i = 0; i < values.length; i++) for (let j = i + 1; j < values.length; j++) {
|
|
1611
|
+
const a = values[i];
|
|
1612
|
+
const b = values[j];
|
|
1613
|
+
const char = a[0];
|
|
1614
|
+
if (!char || a !== char.repeat(a.length) || b !== char.repeat(b.length)) continue;
|
|
1615
|
+
if (a === b || a.startsWith(b) || b.startsWith(a)) return true;
|
|
1616
|
+
}
|
|
1617
|
+
return false;
|
|
1618
|
+
};
|
|
1619
|
+
const parseRepeatedExtglob = (pattern, requireEnd = true) => {
|
|
1620
|
+
if (pattern[0] !== "+" && pattern[0] !== "*" || pattern[1] !== "(") return;
|
|
1621
|
+
let bracket = 0;
|
|
1622
|
+
let paren = 0;
|
|
1623
|
+
let quote = 0;
|
|
1624
|
+
let escaped = false;
|
|
1625
|
+
for (let i = 1; i < pattern.length; i++) {
|
|
1626
|
+
const ch = pattern[i];
|
|
1627
|
+
if (escaped === true) {
|
|
1628
|
+
escaped = false;
|
|
1629
|
+
continue;
|
|
1630
|
+
}
|
|
1631
|
+
if (ch === "\\") {
|
|
1632
|
+
escaped = true;
|
|
1633
|
+
continue;
|
|
1634
|
+
}
|
|
1635
|
+
if (ch === "\"") {
|
|
1636
|
+
quote = quote === 1 ? 0 : 1;
|
|
1637
|
+
continue;
|
|
1638
|
+
}
|
|
1639
|
+
if (quote === 1) continue;
|
|
1640
|
+
if (ch === "[") {
|
|
1641
|
+
bracket++;
|
|
1642
|
+
continue;
|
|
1643
|
+
}
|
|
1644
|
+
if (ch === "]" && bracket > 0) {
|
|
1645
|
+
bracket--;
|
|
1646
|
+
continue;
|
|
1647
|
+
}
|
|
1648
|
+
if (bracket > 0) continue;
|
|
1649
|
+
if (ch === "(") {
|
|
1650
|
+
paren++;
|
|
1651
|
+
continue;
|
|
1652
|
+
}
|
|
1653
|
+
if (ch === ")") {
|
|
1654
|
+
paren--;
|
|
1655
|
+
if (paren === 0) {
|
|
1656
|
+
if (requireEnd === true && i !== pattern.length - 1) return;
|
|
1657
|
+
return {
|
|
1658
|
+
type: pattern[0],
|
|
1659
|
+
body: pattern.slice(2, i),
|
|
1660
|
+
end: i
|
|
1661
|
+
};
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
};
|
|
1666
|
+
const getStarExtglobSequenceOutput = (pattern) => {
|
|
1667
|
+
let index = 0;
|
|
1668
|
+
const chars = [];
|
|
1669
|
+
while (index < pattern.length) {
|
|
1670
|
+
const match = parseRepeatedExtglob(pattern.slice(index), false);
|
|
1671
|
+
if (!match || match.type !== "*") return;
|
|
1672
|
+
const branches = splitTopLevel(match.body).map((branch) => branch.trim());
|
|
1673
|
+
if (branches.length !== 1) return;
|
|
1674
|
+
const branch = normalizeSimpleBranch(branches[0]);
|
|
1675
|
+
if (!branch || branch.length !== 1) return;
|
|
1676
|
+
chars.push(branch);
|
|
1677
|
+
index += match.end + 1;
|
|
1678
|
+
}
|
|
1679
|
+
if (chars.length < 1) return;
|
|
1680
|
+
return `${chars.length === 1 ? utils.escapeRegex(chars[0]) : `[${chars.map((ch) => utils.escapeRegex(ch)).join("")}]`}*`;
|
|
1681
|
+
};
|
|
1682
|
+
const repeatedExtglobRecursion = (pattern) => {
|
|
1683
|
+
let depth = 0;
|
|
1684
|
+
let value = pattern.trim();
|
|
1685
|
+
let match = parseRepeatedExtglob(value);
|
|
1686
|
+
while (match) {
|
|
1687
|
+
depth++;
|
|
1688
|
+
value = match.body.trim();
|
|
1689
|
+
match = parseRepeatedExtglob(value);
|
|
1690
|
+
}
|
|
1691
|
+
return depth;
|
|
1692
|
+
};
|
|
1693
|
+
const analyzeRepeatedExtglob = (body, options) => {
|
|
1694
|
+
if (options.maxExtglobRecursion === false) return { risky: false };
|
|
1695
|
+
const max = typeof options.maxExtglobRecursion === "number" ? options.maxExtglobRecursion : constants.DEFAULT_MAX_EXTGLOB_RECURSION;
|
|
1696
|
+
const branches = splitTopLevel(body).map((branch) => branch.trim());
|
|
1697
|
+
if (branches.length > 1) {
|
|
1698
|
+
if (branches.some((branch) => branch === "") || branches.some((branch) => /^[*?]+$/.test(branch)) || hasRepeatedCharPrefixOverlap(branches)) return { risky: true };
|
|
1699
|
+
}
|
|
1700
|
+
for (const branch of branches) {
|
|
1701
|
+
const safeOutput = getStarExtglobSequenceOutput(branch);
|
|
1702
|
+
if (safeOutput) return {
|
|
1703
|
+
risky: true,
|
|
1704
|
+
safeOutput
|
|
1705
|
+
};
|
|
1706
|
+
if (repeatedExtglobRecursion(branch) > max) return { risky: true };
|
|
1707
|
+
}
|
|
1708
|
+
return { risky: false };
|
|
1709
|
+
};
|
|
1710
|
+
/**
|
|
1711
|
+
* Parse the given input string.
|
|
1712
|
+
* @param {String} input
|
|
1713
|
+
* @param {Object} options
|
|
1714
|
+
* @return {Object}
|
|
1715
|
+
*/
|
|
1716
|
+
const parse = (input, options) => {
|
|
1717
|
+
if (typeof input !== "string") throw new TypeError("Expected a string");
|
|
1718
|
+
input = REPLACEMENTS[input] || input;
|
|
1719
|
+
const opts = { ...options };
|
|
1720
|
+
const max = typeof opts.maxLength === "number" ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH;
|
|
1721
|
+
let len = input.length;
|
|
1722
|
+
if (len > max) throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`);
|
|
1723
|
+
const bos = {
|
|
1724
|
+
type: "bos",
|
|
1725
|
+
value: "",
|
|
1726
|
+
output: opts.prepend || ""
|
|
1727
|
+
};
|
|
1728
|
+
const tokens = [bos];
|
|
1729
|
+
const capture = opts.capture ? "" : "?:";
|
|
1730
|
+
const PLATFORM_CHARS = constants.globChars(opts.windows);
|
|
1731
|
+
const EXTGLOB_CHARS = constants.extglobChars(PLATFORM_CHARS);
|
|
1732
|
+
const { DOT_LITERAL, PLUS_LITERAL, SLASH_LITERAL, ONE_CHAR, DOTS_SLASH, NO_DOT, NO_DOT_SLASH, NO_DOTS_SLASH, QMARK, QMARK_NO_DOT, STAR, START_ANCHOR } = PLATFORM_CHARS;
|
|
1733
|
+
const globstar = (opts) => {
|
|
1734
|
+
return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`;
|
|
1735
|
+
};
|
|
1736
|
+
const nodot = opts.dot ? "" : NO_DOT;
|
|
1737
|
+
const qmarkNoDot = opts.dot ? QMARK : QMARK_NO_DOT;
|
|
1738
|
+
let star = opts.bash === true ? globstar(opts) : STAR;
|
|
1739
|
+
if (opts.capture) star = `(${star})`;
|
|
1740
|
+
if (typeof opts.noext === "boolean") opts.noextglob = opts.noext;
|
|
1741
|
+
const state = {
|
|
1742
|
+
input,
|
|
1743
|
+
index: -1,
|
|
1744
|
+
start: 0,
|
|
1745
|
+
dot: opts.dot === true,
|
|
1746
|
+
consumed: "",
|
|
1747
|
+
output: "",
|
|
1748
|
+
prefix: "",
|
|
1749
|
+
backtrack: false,
|
|
1750
|
+
negated: false,
|
|
1751
|
+
brackets: 0,
|
|
1752
|
+
braces: 0,
|
|
1753
|
+
parens: 0,
|
|
1754
|
+
quotes: 0,
|
|
1755
|
+
globstar: false,
|
|
1756
|
+
tokens
|
|
1757
|
+
};
|
|
1758
|
+
input = utils.removePrefix(input, state);
|
|
1759
|
+
len = input.length;
|
|
1760
|
+
const extglobs = [];
|
|
1761
|
+
const braces = [];
|
|
1762
|
+
const stack = [];
|
|
1763
|
+
let prev = bos;
|
|
1764
|
+
let value;
|
|
1765
|
+
/**
|
|
1766
|
+
* Tokenizing helpers
|
|
1767
|
+
*/
|
|
1768
|
+
const eos = () => state.index === len - 1;
|
|
1769
|
+
const peek = state.peek = (n = 1) => input[state.index + n];
|
|
1770
|
+
const advance = state.advance = () => input[++state.index] || "";
|
|
1771
|
+
const remaining = () => input.slice(state.index + 1);
|
|
1772
|
+
const consume = (value = "", num = 0) => {
|
|
1773
|
+
state.consumed += value;
|
|
1774
|
+
state.index += num;
|
|
1775
|
+
};
|
|
1776
|
+
const append = (token) => {
|
|
1777
|
+
state.output += token.output != null ? token.output : token.value;
|
|
1778
|
+
consume(token.value);
|
|
1779
|
+
};
|
|
1780
|
+
const negate = () => {
|
|
1781
|
+
let count = 1;
|
|
1782
|
+
while (peek() === "!" && (peek(2) !== "(" || peek(3) === "?")) {
|
|
1783
|
+
advance();
|
|
1784
|
+
state.start++;
|
|
1785
|
+
count++;
|
|
1786
|
+
}
|
|
1787
|
+
if (count % 2 === 0) return false;
|
|
1788
|
+
state.negated = true;
|
|
1789
|
+
state.start++;
|
|
1790
|
+
return true;
|
|
1791
|
+
};
|
|
1792
|
+
const increment = (type) => {
|
|
1793
|
+
state[type]++;
|
|
1794
|
+
stack.push(type);
|
|
1795
|
+
};
|
|
1796
|
+
const decrement = (type) => {
|
|
1797
|
+
state[type]--;
|
|
1798
|
+
stack.pop();
|
|
1799
|
+
};
|
|
1800
|
+
/**
|
|
1801
|
+
* Push tokens onto the tokens array. This helper speeds up
|
|
1802
|
+
* tokenizing by 1) helping us avoid backtracking as much as possible,
|
|
1803
|
+
* and 2) helping us avoid creating extra tokens when consecutive
|
|
1804
|
+
* characters are plain text. This improves performance and simplifies
|
|
1805
|
+
* lookbehinds.
|
|
1806
|
+
*/
|
|
1807
|
+
const push = (tok) => {
|
|
1808
|
+
if (prev.type === "globstar") {
|
|
1809
|
+
const isBrace = state.braces > 0 && (tok.type === "comma" || tok.type === "brace");
|
|
1810
|
+
const isExtglob = tok.extglob === true || extglobs.length && (tok.type === "pipe" || tok.type === "paren");
|
|
1811
|
+
if (tok.type !== "slash" && tok.type !== "paren" && !isBrace && !isExtglob) {
|
|
1812
|
+
state.output = state.output.slice(0, -prev.output.length);
|
|
1813
|
+
prev.type = "star";
|
|
1814
|
+
prev.value = "*";
|
|
1815
|
+
prev.output = star;
|
|
1816
|
+
state.output += prev.output;
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
if (extglobs.length && tok.type !== "paren") extglobs[extglobs.length - 1].inner += tok.value;
|
|
1820
|
+
if (tok.value || tok.output) append(tok);
|
|
1821
|
+
if (prev && prev.type === "text" && tok.type === "text") {
|
|
1822
|
+
prev.output = (prev.output || prev.value) + tok.value;
|
|
1823
|
+
prev.value += tok.value;
|
|
1824
|
+
return;
|
|
1825
|
+
}
|
|
1826
|
+
tok.prev = prev;
|
|
1827
|
+
tokens.push(tok);
|
|
1828
|
+
prev = tok;
|
|
1829
|
+
};
|
|
1830
|
+
const extglobOpen = (type, value) => {
|
|
1831
|
+
const token = {
|
|
1832
|
+
...EXTGLOB_CHARS[value],
|
|
1833
|
+
conditions: 1,
|
|
1834
|
+
inner: ""
|
|
1835
|
+
};
|
|
1836
|
+
token.prev = prev;
|
|
1837
|
+
token.parens = state.parens;
|
|
1838
|
+
token.output = state.output;
|
|
1839
|
+
token.startIndex = state.index;
|
|
1840
|
+
token.tokensIndex = tokens.length;
|
|
1841
|
+
const output = (opts.capture ? "(" : "") + token.open;
|
|
1842
|
+
increment("parens");
|
|
1843
|
+
push({
|
|
1844
|
+
type,
|
|
1845
|
+
value,
|
|
1846
|
+
output: state.output ? "" : ONE_CHAR
|
|
1847
|
+
});
|
|
1848
|
+
push({
|
|
1849
|
+
type: "paren",
|
|
1850
|
+
extglob: true,
|
|
1851
|
+
value: advance(),
|
|
1852
|
+
output
|
|
1853
|
+
});
|
|
1854
|
+
extglobs.push(token);
|
|
1855
|
+
};
|
|
1856
|
+
const extglobClose = (token) => {
|
|
1857
|
+
const literal = input.slice(token.startIndex, state.index + 1);
|
|
1858
|
+
const analysis = analyzeRepeatedExtglob(input.slice(token.startIndex + 2, state.index), opts);
|
|
1859
|
+
if ((token.type === "plus" || token.type === "star") && analysis.risky) {
|
|
1860
|
+
const safeOutput = analysis.safeOutput ? (token.output ? "" : ONE_CHAR) + (opts.capture ? `(${analysis.safeOutput})` : analysis.safeOutput) : void 0;
|
|
1861
|
+
const open = tokens[token.tokensIndex];
|
|
1862
|
+
open.type = "text";
|
|
1863
|
+
open.value = literal;
|
|
1864
|
+
open.output = safeOutput || utils.escapeRegex(literal);
|
|
1865
|
+
for (let i = token.tokensIndex + 1; i < tokens.length; i++) {
|
|
1866
|
+
tokens[i].value = "";
|
|
1867
|
+
tokens[i].output = "";
|
|
1868
|
+
delete tokens[i].suffix;
|
|
1869
|
+
}
|
|
1870
|
+
state.output = token.output + open.output;
|
|
1871
|
+
state.backtrack = true;
|
|
1872
|
+
push({
|
|
1873
|
+
type: "paren",
|
|
1874
|
+
extglob: true,
|
|
1875
|
+
value,
|
|
1876
|
+
output: ""
|
|
1877
|
+
});
|
|
1878
|
+
decrement("parens");
|
|
1879
|
+
return;
|
|
1880
|
+
}
|
|
1881
|
+
let output = token.close + (opts.capture ? ")" : "");
|
|
1882
|
+
let rest;
|
|
1883
|
+
if (token.type === "negate") {
|
|
1884
|
+
let extglobStar = star;
|
|
1885
|
+
if (token.inner && token.inner.length > 1 && token.inner.includes("/")) extglobStar = globstar(opts);
|
|
1886
|
+
if (extglobStar !== star || eos() || /^\)+$/.test(remaining())) output = token.close = `)$))${extglobStar}`;
|
|
1887
|
+
if (token.inner.includes("*") && (rest = remaining()) && /^\.[^\\/.]+$/.test(rest)) output = token.close = `)${parse(rest, {
|
|
1888
|
+
...options,
|
|
1889
|
+
fastpaths: false
|
|
1890
|
+
}).output})${extglobStar})`;
|
|
1891
|
+
if (token.prev.type === "bos") state.negatedExtglob = true;
|
|
1892
|
+
}
|
|
1893
|
+
push({
|
|
1894
|
+
type: "paren",
|
|
1895
|
+
extglob: true,
|
|
1896
|
+
value,
|
|
1897
|
+
output
|
|
1898
|
+
});
|
|
1899
|
+
decrement("parens");
|
|
1900
|
+
};
|
|
1901
|
+
/**
|
|
1902
|
+
* Fast paths
|
|
1903
|
+
*/
|
|
1904
|
+
if (opts.fastpaths !== false && !/(^[*!]|[/()[\]{}"])/.test(input)) {
|
|
1905
|
+
let backslashes = false;
|
|
1906
|
+
let output = input.replace(REGEX_SPECIAL_CHARS_BACKREF, (m, esc, chars, first, rest, index) => {
|
|
1907
|
+
if (first === "\\") {
|
|
1908
|
+
backslashes = true;
|
|
1909
|
+
return m;
|
|
1910
|
+
}
|
|
1911
|
+
if (first === "?") {
|
|
1912
|
+
if (esc) return esc + first + (rest ? QMARK.repeat(rest.length) : "");
|
|
1913
|
+
if (index === 0) return qmarkNoDot + (rest ? QMARK.repeat(rest.length) : "");
|
|
1914
|
+
return QMARK.repeat(chars.length);
|
|
1915
|
+
}
|
|
1916
|
+
if (first === ".") return DOT_LITERAL.repeat(chars.length);
|
|
1917
|
+
if (first === "*") {
|
|
1918
|
+
if (esc) return esc + first + (rest ? star : "");
|
|
1919
|
+
return star;
|
|
1920
|
+
}
|
|
1921
|
+
return esc ? m : `\\${m}`;
|
|
1922
|
+
});
|
|
1923
|
+
if (backslashes === true) if (opts.unescape === true) output = output.replace(/\\/g, "");
|
|
1924
|
+
else output = output.replace(/\\+/g, (m) => {
|
|
1925
|
+
return m.length % 2 === 0 ? "\\\\" : m ? "\\" : "";
|
|
1926
|
+
});
|
|
1927
|
+
if (output === input && opts.contains === true) {
|
|
1928
|
+
state.output = input;
|
|
1929
|
+
return state;
|
|
1930
|
+
}
|
|
1931
|
+
state.output = utils.wrapOutput(output, state, options);
|
|
1932
|
+
return state;
|
|
1933
|
+
}
|
|
1934
|
+
/**
|
|
1935
|
+
* Tokenize input until we reach end-of-string
|
|
1936
|
+
*/
|
|
1937
|
+
while (!eos()) {
|
|
1938
|
+
value = advance();
|
|
1939
|
+
if (value === "\0") continue;
|
|
1940
|
+
/**
|
|
1941
|
+
* Escaped characters
|
|
1942
|
+
*/
|
|
1943
|
+
if (value === "\\") {
|
|
1944
|
+
const next = peek();
|
|
1945
|
+
if (next === "/" && opts.bash !== true) continue;
|
|
1946
|
+
if (next === "." || next === ";") continue;
|
|
1947
|
+
if (!next) {
|
|
1948
|
+
value += "\\";
|
|
1949
|
+
push({
|
|
1950
|
+
type: "text",
|
|
1951
|
+
value
|
|
1952
|
+
});
|
|
1953
|
+
continue;
|
|
1954
|
+
}
|
|
1955
|
+
const match = /^\\+/.exec(remaining());
|
|
1956
|
+
let slashes = 0;
|
|
1957
|
+
if (match && match[0].length > 2) {
|
|
1958
|
+
slashes = match[0].length;
|
|
1959
|
+
state.index += slashes;
|
|
1960
|
+
if (slashes % 2 !== 0) value += "\\";
|
|
1961
|
+
}
|
|
1962
|
+
if (opts.unescape === true) value = advance();
|
|
1963
|
+
else value += advance();
|
|
1964
|
+
if (state.brackets === 0) {
|
|
1965
|
+
push({
|
|
1966
|
+
type: "text",
|
|
1967
|
+
value
|
|
1968
|
+
});
|
|
1969
|
+
continue;
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
/**
|
|
1973
|
+
* If we're inside a regex character class, continue
|
|
1974
|
+
* until we reach the closing bracket.
|
|
1975
|
+
*/
|
|
1976
|
+
if (state.brackets > 0 && (value !== "]" || prev.value === "[" || prev.value === "[^")) {
|
|
1977
|
+
if (opts.posix !== false && value === ":") {
|
|
1978
|
+
const inner = prev.value.slice(1);
|
|
1979
|
+
if (inner.includes("[")) {
|
|
1980
|
+
prev.posix = true;
|
|
1981
|
+
if (inner.includes(":")) {
|
|
1982
|
+
const idx = prev.value.lastIndexOf("[");
|
|
1983
|
+
const pre = prev.value.slice(0, idx);
|
|
1984
|
+
const posix = POSIX_REGEX_SOURCE[prev.value.slice(idx + 2)];
|
|
1985
|
+
if (posix) {
|
|
1986
|
+
prev.value = pre + posix;
|
|
1987
|
+
state.backtrack = true;
|
|
1988
|
+
advance();
|
|
1989
|
+
if (!bos.output && tokens.indexOf(prev) === 1) bos.output = ONE_CHAR;
|
|
1990
|
+
continue;
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
if (value === "[" && peek() !== ":" || value === "-" && peek() === "]") value = `\\${value}`;
|
|
1996
|
+
if (value === "]" && (prev.value === "[" || prev.value === "[^")) value = `\\${value}`;
|
|
1997
|
+
if (opts.posix === true && value === "!" && prev.value === "[") value = "^";
|
|
1998
|
+
prev.value += value;
|
|
1999
|
+
append({ value });
|
|
2000
|
+
continue;
|
|
2001
|
+
}
|
|
2002
|
+
/**
|
|
2003
|
+
* If we're inside a quoted string, continue
|
|
2004
|
+
* until we reach the closing double quote.
|
|
2005
|
+
*/
|
|
2006
|
+
if (state.quotes === 1 && value !== "\"") {
|
|
2007
|
+
value = utils.escapeRegex(value);
|
|
2008
|
+
prev.value += value;
|
|
2009
|
+
append({ value });
|
|
2010
|
+
continue;
|
|
2011
|
+
}
|
|
2012
|
+
/**
|
|
2013
|
+
* Double quotes
|
|
2014
|
+
*/
|
|
2015
|
+
if (value === "\"") {
|
|
2016
|
+
state.quotes = state.quotes === 1 ? 0 : 1;
|
|
2017
|
+
if (opts.keepQuotes === true) push({
|
|
2018
|
+
type: "text",
|
|
2019
|
+
value
|
|
2020
|
+
});
|
|
2021
|
+
continue;
|
|
2022
|
+
}
|
|
2023
|
+
/**
|
|
2024
|
+
* Parentheses
|
|
2025
|
+
*/
|
|
2026
|
+
if (value === "(") {
|
|
2027
|
+
increment("parens");
|
|
2028
|
+
push({
|
|
2029
|
+
type: "paren",
|
|
2030
|
+
value
|
|
2031
|
+
});
|
|
2032
|
+
continue;
|
|
2033
|
+
}
|
|
2034
|
+
if (value === ")") {
|
|
2035
|
+
if (state.parens === 0 && opts.strictBrackets === true) throw new SyntaxError(syntaxError("opening", "("));
|
|
2036
|
+
const extglob = extglobs[extglobs.length - 1];
|
|
2037
|
+
if (extglob && state.parens === extglob.parens + 1) {
|
|
2038
|
+
extglobClose(extglobs.pop());
|
|
2039
|
+
continue;
|
|
2040
|
+
}
|
|
2041
|
+
push({
|
|
2042
|
+
type: "paren",
|
|
2043
|
+
value,
|
|
2044
|
+
output: state.parens ? ")" : "\\)"
|
|
2045
|
+
});
|
|
2046
|
+
decrement("parens");
|
|
2047
|
+
continue;
|
|
2048
|
+
}
|
|
2049
|
+
/**
|
|
2050
|
+
* Square brackets
|
|
2051
|
+
*/
|
|
2052
|
+
if (value === "[") {
|
|
2053
|
+
if (opts.nobracket === true || !remaining().includes("]")) {
|
|
2054
|
+
if (opts.nobracket !== true && opts.strictBrackets === true) throw new SyntaxError(syntaxError("closing", "]"));
|
|
2055
|
+
value = `\\${value}`;
|
|
2056
|
+
} else increment("brackets");
|
|
2057
|
+
push({
|
|
2058
|
+
type: "bracket",
|
|
2059
|
+
value
|
|
2060
|
+
});
|
|
2061
|
+
continue;
|
|
2062
|
+
}
|
|
2063
|
+
if (value === "]") {
|
|
2064
|
+
if (opts.nobracket === true || prev && prev.type === "bracket" && prev.value.length === 1) {
|
|
2065
|
+
push({
|
|
2066
|
+
type: "text",
|
|
2067
|
+
value,
|
|
2068
|
+
output: `\\${value}`
|
|
2069
|
+
});
|
|
2070
|
+
continue;
|
|
2071
|
+
}
|
|
2072
|
+
if (state.brackets === 0) {
|
|
2073
|
+
if (opts.strictBrackets === true) throw new SyntaxError(syntaxError("opening", "["));
|
|
2074
|
+
push({
|
|
2075
|
+
type: "text",
|
|
2076
|
+
value,
|
|
2077
|
+
output: `\\${value}`
|
|
2078
|
+
});
|
|
2079
|
+
continue;
|
|
2080
|
+
}
|
|
2081
|
+
decrement("brackets");
|
|
2082
|
+
const prevValue = prev.value.slice(1);
|
|
2083
|
+
if (prev.posix !== true && prevValue[0] === "^" && !prevValue.includes("/")) value = `/${value}`;
|
|
2084
|
+
prev.value += value;
|
|
2085
|
+
append({ value });
|
|
2086
|
+
if (opts.literalBrackets === false || utils.hasRegexChars(prevValue)) continue;
|
|
2087
|
+
const escaped = utils.escapeRegex(prev.value);
|
|
2088
|
+
state.output = state.output.slice(0, -prev.value.length);
|
|
2089
|
+
if (opts.literalBrackets === true) {
|
|
2090
|
+
state.output += escaped;
|
|
2091
|
+
prev.value = escaped;
|
|
2092
|
+
continue;
|
|
2093
|
+
}
|
|
2094
|
+
prev.value = `(${capture}${escaped}|${prev.value})`;
|
|
2095
|
+
state.output += prev.value;
|
|
2096
|
+
continue;
|
|
2097
|
+
}
|
|
2098
|
+
/**
|
|
2099
|
+
* Braces
|
|
2100
|
+
*/
|
|
2101
|
+
if (value === "{" && opts.nobrace !== true) {
|
|
2102
|
+
increment("braces");
|
|
2103
|
+
const open = {
|
|
2104
|
+
type: "brace",
|
|
2105
|
+
value,
|
|
2106
|
+
output: "(",
|
|
2107
|
+
outputIndex: state.output.length,
|
|
2108
|
+
tokensIndex: state.tokens.length
|
|
2109
|
+
};
|
|
2110
|
+
braces.push(open);
|
|
2111
|
+
push(open);
|
|
2112
|
+
continue;
|
|
2113
|
+
}
|
|
2114
|
+
if (value === "}") {
|
|
2115
|
+
const brace = braces[braces.length - 1];
|
|
2116
|
+
if (opts.nobrace === true || !brace) {
|
|
2117
|
+
push({
|
|
2118
|
+
type: "text",
|
|
2119
|
+
value,
|
|
2120
|
+
output: value
|
|
2121
|
+
});
|
|
2122
|
+
continue;
|
|
2123
|
+
}
|
|
2124
|
+
let output = ")";
|
|
2125
|
+
if (brace.dots === true) {
|
|
2126
|
+
const arr = tokens.slice();
|
|
2127
|
+
const range = [];
|
|
2128
|
+
for (let i = arr.length - 1; i >= 0; i--) {
|
|
2129
|
+
tokens.pop();
|
|
2130
|
+
if (arr[i].type === "brace") break;
|
|
2131
|
+
if (arr[i].type !== "dots") range.unshift(arr[i].value);
|
|
2132
|
+
}
|
|
2133
|
+
output = expandRange(range, opts);
|
|
2134
|
+
state.backtrack = true;
|
|
2135
|
+
}
|
|
2136
|
+
if (brace.comma !== true && brace.dots !== true) {
|
|
2137
|
+
const out = state.output.slice(0, brace.outputIndex);
|
|
2138
|
+
const toks = state.tokens.slice(brace.tokensIndex);
|
|
2139
|
+
brace.value = brace.output = "\\{";
|
|
2140
|
+
value = output = "\\}";
|
|
2141
|
+
state.output = out;
|
|
2142
|
+
for (const t of toks) state.output += t.output || t.value;
|
|
2143
|
+
}
|
|
2144
|
+
push({
|
|
2145
|
+
type: "brace",
|
|
2146
|
+
value,
|
|
2147
|
+
output
|
|
2148
|
+
});
|
|
2149
|
+
decrement("braces");
|
|
2150
|
+
braces.pop();
|
|
2151
|
+
continue;
|
|
2152
|
+
}
|
|
2153
|
+
/**
|
|
2154
|
+
* Pipes
|
|
2155
|
+
*/
|
|
2156
|
+
if (value === "|") {
|
|
2157
|
+
if (extglobs.length > 0) extglobs[extglobs.length - 1].conditions++;
|
|
2158
|
+
push({
|
|
2159
|
+
type: "text",
|
|
2160
|
+
value
|
|
2161
|
+
});
|
|
2162
|
+
continue;
|
|
2163
|
+
}
|
|
2164
|
+
/**
|
|
2165
|
+
* Commas
|
|
2166
|
+
*/
|
|
2167
|
+
if (value === ",") {
|
|
2168
|
+
let output = value;
|
|
2169
|
+
const brace = braces[braces.length - 1];
|
|
2170
|
+
if (brace && stack[stack.length - 1] === "braces") {
|
|
2171
|
+
brace.comma = true;
|
|
2172
|
+
output = "|";
|
|
2173
|
+
}
|
|
2174
|
+
push({
|
|
2175
|
+
type: "comma",
|
|
2176
|
+
value,
|
|
2177
|
+
output
|
|
2178
|
+
});
|
|
2179
|
+
continue;
|
|
2180
|
+
}
|
|
2181
|
+
/**
|
|
2182
|
+
* Slashes
|
|
2183
|
+
*/
|
|
2184
|
+
if (value === "/") {
|
|
2185
|
+
if (prev.type === "dot" && state.index === state.start + 1) {
|
|
2186
|
+
state.start = state.index + 1;
|
|
2187
|
+
state.consumed = "";
|
|
2188
|
+
state.output = "";
|
|
2189
|
+
tokens.pop();
|
|
2190
|
+
prev = bos;
|
|
2191
|
+
continue;
|
|
2192
|
+
}
|
|
2193
|
+
push({
|
|
2194
|
+
type: "slash",
|
|
2195
|
+
value,
|
|
2196
|
+
output: SLASH_LITERAL
|
|
2197
|
+
});
|
|
2198
|
+
continue;
|
|
2199
|
+
}
|
|
2200
|
+
/**
|
|
2201
|
+
* Dots
|
|
2202
|
+
*/
|
|
2203
|
+
if (value === ".") {
|
|
2204
|
+
if (state.braces > 0 && prev.type === "dot") {
|
|
2205
|
+
if (prev.value === ".") prev.output = DOT_LITERAL;
|
|
2206
|
+
const brace = braces[braces.length - 1];
|
|
2207
|
+
prev.type = "dots";
|
|
2208
|
+
prev.output += value;
|
|
2209
|
+
prev.value += value;
|
|
2210
|
+
brace.dots = true;
|
|
2211
|
+
continue;
|
|
2212
|
+
}
|
|
2213
|
+
if (state.braces + state.parens === 0 && prev.type !== "bos" && prev.type !== "slash") {
|
|
2214
|
+
push({
|
|
2215
|
+
type: "text",
|
|
2216
|
+
value,
|
|
2217
|
+
output: DOT_LITERAL
|
|
2218
|
+
});
|
|
2219
|
+
continue;
|
|
2220
|
+
}
|
|
2221
|
+
push({
|
|
2222
|
+
type: "dot",
|
|
2223
|
+
value,
|
|
2224
|
+
output: DOT_LITERAL
|
|
2225
|
+
});
|
|
2226
|
+
continue;
|
|
2227
|
+
}
|
|
2228
|
+
/**
|
|
2229
|
+
* Question marks
|
|
2230
|
+
*/
|
|
2231
|
+
if (value === "?") {
|
|
2232
|
+
if (!(prev && prev.value === "(") && opts.noextglob !== true && peek() === "(" && peek(2) !== "?") {
|
|
2233
|
+
extglobOpen("qmark", value);
|
|
2234
|
+
continue;
|
|
2235
|
+
}
|
|
2236
|
+
if (prev && prev.type === "paren") {
|
|
2237
|
+
const next = peek();
|
|
2238
|
+
let output = value;
|
|
2239
|
+
if (prev.value === "(" && !/[!=<:]/.test(next) || next === "<" && !/<([!=]|\w+>)/.test(remaining())) output = `\\${value}`;
|
|
2240
|
+
push({
|
|
2241
|
+
type: "text",
|
|
2242
|
+
value,
|
|
2243
|
+
output
|
|
2244
|
+
});
|
|
2245
|
+
continue;
|
|
2246
|
+
}
|
|
2247
|
+
if (opts.dot !== true && (prev.type === "slash" || prev.type === "bos")) {
|
|
2248
|
+
push({
|
|
2249
|
+
type: "qmark",
|
|
2250
|
+
value,
|
|
2251
|
+
output: QMARK_NO_DOT
|
|
2252
|
+
});
|
|
2253
|
+
continue;
|
|
2254
|
+
}
|
|
2255
|
+
push({
|
|
2256
|
+
type: "qmark",
|
|
2257
|
+
value,
|
|
2258
|
+
output: QMARK
|
|
2259
|
+
});
|
|
2260
|
+
continue;
|
|
2261
|
+
}
|
|
2262
|
+
/**
|
|
2263
|
+
* Exclamation
|
|
2264
|
+
*/
|
|
2265
|
+
if (value === "!") {
|
|
2266
|
+
if (opts.noextglob !== true && peek() === "(") {
|
|
2267
|
+
if (peek(2) !== "?" || !/[!=<:]/.test(peek(3))) {
|
|
2268
|
+
extglobOpen("negate", value);
|
|
2269
|
+
continue;
|
|
2270
|
+
}
|
|
2271
|
+
}
|
|
2272
|
+
if (opts.nonegate !== true && state.index === 0) {
|
|
2273
|
+
negate();
|
|
2274
|
+
continue;
|
|
2275
|
+
}
|
|
2276
|
+
}
|
|
2277
|
+
/**
|
|
2278
|
+
* Plus
|
|
2279
|
+
*/
|
|
2280
|
+
if (value === "+") {
|
|
2281
|
+
if (opts.noextglob !== true && peek() === "(" && peek(2) !== "?") {
|
|
2282
|
+
extglobOpen("plus", value);
|
|
2283
|
+
continue;
|
|
2284
|
+
}
|
|
2285
|
+
if (prev && prev.value === "(" || opts.regex === false) {
|
|
2286
|
+
push({
|
|
2287
|
+
type: "plus",
|
|
2288
|
+
value,
|
|
2289
|
+
output: PLUS_LITERAL
|
|
2290
|
+
});
|
|
2291
|
+
continue;
|
|
2292
|
+
}
|
|
2293
|
+
if (prev && (prev.type === "bracket" || prev.type === "paren" || prev.type === "brace") || state.parens > 0) {
|
|
2294
|
+
push({
|
|
2295
|
+
type: "plus",
|
|
2296
|
+
value
|
|
2297
|
+
});
|
|
2298
|
+
continue;
|
|
2299
|
+
}
|
|
2300
|
+
push({
|
|
2301
|
+
type: "plus",
|
|
2302
|
+
value: PLUS_LITERAL
|
|
2303
|
+
});
|
|
2304
|
+
continue;
|
|
2305
|
+
}
|
|
2306
|
+
/**
|
|
2307
|
+
* Plain text
|
|
2308
|
+
*/
|
|
2309
|
+
if (value === "@") {
|
|
2310
|
+
if (opts.noextglob !== true && peek() === "(" && peek(2) !== "?") {
|
|
2311
|
+
push({
|
|
2312
|
+
type: "at",
|
|
2313
|
+
extglob: true,
|
|
2314
|
+
value,
|
|
2315
|
+
output: ""
|
|
2316
|
+
});
|
|
2317
|
+
continue;
|
|
2318
|
+
}
|
|
2319
|
+
push({
|
|
2320
|
+
type: "text",
|
|
2321
|
+
value
|
|
2322
|
+
});
|
|
2323
|
+
continue;
|
|
2324
|
+
}
|
|
2325
|
+
/**
|
|
2326
|
+
* Plain text
|
|
2327
|
+
*/
|
|
2328
|
+
if (value !== "*") {
|
|
2329
|
+
if (value === "$" || value === "^") value = `\\${value}`;
|
|
2330
|
+
const match = REGEX_NON_SPECIAL_CHARS.exec(remaining());
|
|
2331
|
+
if (match) {
|
|
2332
|
+
value += match[0];
|
|
2333
|
+
state.index += match[0].length;
|
|
2334
|
+
}
|
|
2335
|
+
push({
|
|
2336
|
+
type: "text",
|
|
2337
|
+
value
|
|
2338
|
+
});
|
|
2339
|
+
continue;
|
|
2340
|
+
}
|
|
2341
|
+
/**
|
|
2342
|
+
* Stars
|
|
2343
|
+
*/
|
|
2344
|
+
if (prev && (prev.type === "globstar" || prev.star === true)) {
|
|
2345
|
+
prev.type = "star";
|
|
2346
|
+
prev.star = true;
|
|
2347
|
+
prev.value += value;
|
|
2348
|
+
prev.output = star;
|
|
2349
|
+
state.backtrack = true;
|
|
2350
|
+
state.globstar = true;
|
|
2351
|
+
consume(value);
|
|
2352
|
+
continue;
|
|
2353
|
+
}
|
|
2354
|
+
let rest = remaining();
|
|
2355
|
+
if (opts.noextglob !== true && /^\([^?]/.test(rest)) {
|
|
2356
|
+
extglobOpen("star", value);
|
|
2357
|
+
continue;
|
|
2358
|
+
}
|
|
2359
|
+
if (prev.type === "star") {
|
|
2360
|
+
if (opts.noglobstar === true) {
|
|
2361
|
+
consume(value);
|
|
2362
|
+
continue;
|
|
2363
|
+
}
|
|
2364
|
+
const prior = prev.prev;
|
|
2365
|
+
const before = prior.prev;
|
|
2366
|
+
const isStart = prior.type === "slash" || prior.type === "bos";
|
|
2367
|
+
const afterStar = before && (before.type === "star" || before.type === "globstar");
|
|
2368
|
+
if (opts.bash === true && (!isStart || rest[0] && rest[0] !== "/")) {
|
|
2369
|
+
push({
|
|
2370
|
+
type: "star",
|
|
2371
|
+
value,
|
|
2372
|
+
output: ""
|
|
2373
|
+
});
|
|
2374
|
+
continue;
|
|
2375
|
+
}
|
|
2376
|
+
const isBrace = state.braces > 0 && (prior.type === "comma" || prior.type === "brace");
|
|
2377
|
+
const isExtglob = extglobs.length && (prior.type === "pipe" || prior.type === "paren");
|
|
2378
|
+
if (!isStart && prior.type !== "paren" && !isBrace && !isExtglob) {
|
|
2379
|
+
push({
|
|
2380
|
+
type: "star",
|
|
2381
|
+
value,
|
|
2382
|
+
output: ""
|
|
2383
|
+
});
|
|
2384
|
+
continue;
|
|
2385
|
+
}
|
|
2386
|
+
while (rest.slice(0, 3) === "/**") {
|
|
2387
|
+
const after = input[state.index + 4];
|
|
2388
|
+
if (after && after !== "/") break;
|
|
2389
|
+
rest = rest.slice(3);
|
|
2390
|
+
consume("/**", 3);
|
|
2391
|
+
}
|
|
2392
|
+
if (prior.type === "bos" && eos()) {
|
|
2393
|
+
prev.type = "globstar";
|
|
2394
|
+
prev.value += value;
|
|
2395
|
+
prev.output = globstar(opts);
|
|
2396
|
+
state.output = prev.output;
|
|
2397
|
+
state.globstar = true;
|
|
2398
|
+
consume(value);
|
|
2399
|
+
continue;
|
|
2400
|
+
}
|
|
2401
|
+
if (prior.type === "slash" && prior.prev.type !== "bos" && !afterStar && eos()) {
|
|
2402
|
+
state.output = state.output.slice(0, -(prior.output + prev.output).length);
|
|
2403
|
+
prior.output = `(?:${prior.output}`;
|
|
2404
|
+
prev.type = "globstar";
|
|
2405
|
+
prev.output = globstar(opts) + (opts.strictSlashes ? ")" : "|$)");
|
|
2406
|
+
prev.value += value;
|
|
2407
|
+
state.globstar = true;
|
|
2408
|
+
state.output += prior.output + prev.output;
|
|
2409
|
+
consume(value);
|
|
2410
|
+
continue;
|
|
2411
|
+
}
|
|
2412
|
+
if (prior.type === "slash" && prior.prev.type !== "bos" && rest[0] === "/") {
|
|
2413
|
+
const end = rest[1] !== void 0 ? "|$" : "";
|
|
2414
|
+
state.output = state.output.slice(0, -(prior.output + prev.output).length);
|
|
2415
|
+
prior.output = `(?:${prior.output}`;
|
|
2416
|
+
prev.type = "globstar";
|
|
2417
|
+
prev.output = `${globstar(opts)}${SLASH_LITERAL}|${SLASH_LITERAL}${end})`;
|
|
2418
|
+
prev.value += value;
|
|
2419
|
+
state.output += prior.output + prev.output;
|
|
2420
|
+
state.globstar = true;
|
|
2421
|
+
consume(value + advance());
|
|
2422
|
+
push({
|
|
2423
|
+
type: "slash",
|
|
2424
|
+
value: "/",
|
|
2425
|
+
output: ""
|
|
2426
|
+
});
|
|
2427
|
+
continue;
|
|
2428
|
+
}
|
|
2429
|
+
if (prior.type === "bos" && rest[0] === "/") {
|
|
2430
|
+
prev.type = "globstar";
|
|
2431
|
+
prev.value += value;
|
|
2432
|
+
prev.output = `(?:^|${SLASH_LITERAL}|${globstar(opts)}${SLASH_LITERAL})`;
|
|
2433
|
+
state.output = prev.output;
|
|
2434
|
+
state.globstar = true;
|
|
2435
|
+
consume(value + advance());
|
|
2436
|
+
push({
|
|
2437
|
+
type: "slash",
|
|
2438
|
+
value: "/",
|
|
2439
|
+
output: ""
|
|
2440
|
+
});
|
|
2441
|
+
continue;
|
|
2442
|
+
}
|
|
2443
|
+
state.output = state.output.slice(0, -prev.output.length);
|
|
2444
|
+
prev.type = "globstar";
|
|
2445
|
+
prev.output = globstar(opts);
|
|
2446
|
+
prev.value += value;
|
|
2447
|
+
state.output += prev.output;
|
|
2448
|
+
state.globstar = true;
|
|
2449
|
+
consume(value);
|
|
2450
|
+
continue;
|
|
2451
|
+
}
|
|
2452
|
+
const token = {
|
|
2453
|
+
type: "star",
|
|
2454
|
+
value,
|
|
2455
|
+
output: star
|
|
2456
|
+
};
|
|
2457
|
+
if (opts.bash === true) {
|
|
2458
|
+
token.output = ".*?";
|
|
2459
|
+
if (prev.type === "bos" || prev.type === "slash") token.output = nodot + token.output;
|
|
2460
|
+
push(token);
|
|
2461
|
+
continue;
|
|
2462
|
+
}
|
|
2463
|
+
if (prev && (prev.type === "bracket" || prev.type === "paren") && opts.regex === true) {
|
|
2464
|
+
token.output = value;
|
|
2465
|
+
push(token);
|
|
2466
|
+
continue;
|
|
2467
|
+
}
|
|
2468
|
+
if (state.index === state.start || prev.type === "slash" || prev.type === "dot") {
|
|
2469
|
+
if (prev.type === "dot") {
|
|
2470
|
+
state.output += NO_DOT_SLASH;
|
|
2471
|
+
prev.output += NO_DOT_SLASH;
|
|
2472
|
+
} else if (opts.dot === true) {
|
|
2473
|
+
state.output += NO_DOTS_SLASH;
|
|
2474
|
+
prev.output += NO_DOTS_SLASH;
|
|
2475
|
+
} else {
|
|
2476
|
+
state.output += nodot;
|
|
2477
|
+
prev.output += nodot;
|
|
2478
|
+
}
|
|
2479
|
+
if (peek() !== "*") {
|
|
2480
|
+
state.output += ONE_CHAR;
|
|
2481
|
+
prev.output += ONE_CHAR;
|
|
2482
|
+
}
|
|
2483
|
+
}
|
|
2484
|
+
push(token);
|
|
2485
|
+
}
|
|
2486
|
+
while (state.brackets > 0) {
|
|
2487
|
+
if (opts.strictBrackets === true) throw new SyntaxError(syntaxError("closing", "]"));
|
|
2488
|
+
state.output = utils.escapeLast(state.output, "[");
|
|
2489
|
+
decrement("brackets");
|
|
2490
|
+
}
|
|
2491
|
+
while (state.parens > 0) {
|
|
2492
|
+
if (opts.strictBrackets === true) throw new SyntaxError(syntaxError("closing", ")"));
|
|
2493
|
+
state.output = utils.escapeLast(state.output, "(");
|
|
2494
|
+
decrement("parens");
|
|
2495
|
+
}
|
|
2496
|
+
while (state.braces > 0) {
|
|
2497
|
+
if (opts.strictBrackets === true) throw new SyntaxError(syntaxError("closing", "}"));
|
|
2498
|
+
state.output = utils.escapeLast(state.output, "{");
|
|
2499
|
+
decrement("braces");
|
|
2500
|
+
}
|
|
2501
|
+
if (opts.strictSlashes !== true && (prev.type === "star" || prev.type === "bracket")) push({
|
|
2502
|
+
type: "maybe_slash",
|
|
2503
|
+
value: "",
|
|
2504
|
+
output: `${SLASH_LITERAL}?`
|
|
2505
|
+
});
|
|
2506
|
+
if (state.backtrack === true) {
|
|
2507
|
+
state.output = "";
|
|
2508
|
+
for (const token of state.tokens) {
|
|
2509
|
+
state.output += token.output != null ? token.output : token.value;
|
|
2510
|
+
if (token.suffix) state.output += token.suffix;
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2513
|
+
return state;
|
|
2514
|
+
};
|
|
2515
|
+
/**
|
|
2516
|
+
* Fast paths for creating regular expressions for common glob patterns.
|
|
2517
|
+
* This can significantly speed up processing and has very little downside
|
|
2518
|
+
* impact when none of the fast paths match.
|
|
2519
|
+
*/
|
|
2520
|
+
parse.fastpaths = (input, options) => {
|
|
2521
|
+
const opts = { ...options };
|
|
2522
|
+
const max = typeof opts.maxLength === "number" ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH;
|
|
2523
|
+
const len = input.length;
|
|
2524
|
+
if (len > max) throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`);
|
|
2525
|
+
input = REPLACEMENTS[input] || input;
|
|
2526
|
+
const { DOT_LITERAL, SLASH_LITERAL, ONE_CHAR, DOTS_SLASH, NO_DOT, NO_DOTS, NO_DOTS_SLASH, STAR, START_ANCHOR } = constants.globChars(opts.windows);
|
|
2527
|
+
const nodot = opts.dot ? NO_DOTS : NO_DOT;
|
|
2528
|
+
const slashDot = opts.dot ? NO_DOTS_SLASH : NO_DOT;
|
|
2529
|
+
const capture = opts.capture ? "" : "?:";
|
|
2530
|
+
const state = {
|
|
2531
|
+
negated: false,
|
|
2532
|
+
prefix: ""
|
|
2533
|
+
};
|
|
2534
|
+
let star = opts.bash === true ? ".*?" : STAR;
|
|
2535
|
+
if (opts.capture) star = `(${star})`;
|
|
2536
|
+
const globstar = (opts) => {
|
|
2537
|
+
if (opts.noglobstar === true) return star;
|
|
2538
|
+
return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`;
|
|
2539
|
+
};
|
|
2540
|
+
const create = (str) => {
|
|
2541
|
+
switch (str) {
|
|
2542
|
+
case "*": return `${nodot}${ONE_CHAR}${star}`;
|
|
2543
|
+
case ".*": return `${DOT_LITERAL}${ONE_CHAR}${star}`;
|
|
2544
|
+
case "*.*": return `${nodot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`;
|
|
2545
|
+
case "*/*": return `${nodot}${star}${SLASH_LITERAL}${ONE_CHAR}${slashDot}${star}`;
|
|
2546
|
+
case "**": return nodot + globstar(opts);
|
|
2547
|
+
case "**/*": return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${ONE_CHAR}${star}`;
|
|
2548
|
+
case "**/*.*": return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`;
|
|
2549
|
+
case "**/.*": return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${DOT_LITERAL}${ONE_CHAR}${star}`;
|
|
2550
|
+
default: {
|
|
2551
|
+
const match = /^(.*?)\.(\w+)$/.exec(str);
|
|
2552
|
+
if (!match) return;
|
|
2553
|
+
const source = create(match[1]);
|
|
2554
|
+
if (!source) return;
|
|
2555
|
+
return source + DOT_LITERAL + match[2];
|
|
2556
|
+
}
|
|
2557
|
+
}
|
|
2558
|
+
};
|
|
2559
|
+
let source = create(utils.removePrefix(input, state));
|
|
2560
|
+
if (source && opts.strictSlashes !== true) source += `${SLASH_LITERAL}?`;
|
|
2561
|
+
return source;
|
|
2562
|
+
};
|
|
2563
|
+
module.exports = parse;
|
|
2564
|
+
}));
|
|
2565
|
+
//#endregion
|
|
2566
|
+
//#region ../../../node_modules/.pnpm/picomatch@4.0.4/node_modules/picomatch/lib/picomatch.js
|
|
2567
|
+
var require_picomatch = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
2568
|
+
const scan = require_scan();
|
|
2569
|
+
const parse = require_parse();
|
|
2570
|
+
const utils = require_utils();
|
|
2571
|
+
const constants = require_constants();
|
|
2572
|
+
const isObject = (val) => val && typeof val === "object" && !Array.isArray(val);
|
|
2573
|
+
/**
|
|
2574
|
+
* Creates a matcher function from one or more glob patterns. The
|
|
2575
|
+
* returned function takes a string to match as its first argument,
|
|
2576
|
+
* and returns true if the string is a match. The returned matcher
|
|
2577
|
+
* function also takes a boolean as the second argument that, when true,
|
|
2578
|
+
* returns an object with additional information.
|
|
2579
|
+
*
|
|
2580
|
+
* ```js
|
|
2581
|
+
* const picomatch = require('picomatch');
|
|
2582
|
+
* // picomatch(glob[, options]);
|
|
2583
|
+
*
|
|
2584
|
+
* const isMatch = picomatch('*.!(*a)');
|
|
2585
|
+
* console.log(isMatch('a.a')); //=> false
|
|
2586
|
+
* console.log(isMatch('a.b')); //=> true
|
|
2587
|
+
* ```
|
|
2588
|
+
* @name picomatch
|
|
2589
|
+
* @param {String|Array} `globs` One or more glob patterns.
|
|
2590
|
+
* @param {Object=} `options`
|
|
2591
|
+
* @return {Function=} Returns a matcher function.
|
|
2592
|
+
* @api public
|
|
2593
|
+
*/
|
|
2594
|
+
const picomatch = (glob, options, returnState = false) => {
|
|
2595
|
+
if (Array.isArray(glob)) {
|
|
2596
|
+
const fns = glob.map((input) => picomatch(input, options, returnState));
|
|
2597
|
+
const arrayMatcher = (str) => {
|
|
2598
|
+
for (const isMatch of fns) {
|
|
2599
|
+
const state = isMatch(str);
|
|
2600
|
+
if (state) return state;
|
|
2601
|
+
}
|
|
2602
|
+
return false;
|
|
2603
|
+
};
|
|
2604
|
+
return arrayMatcher;
|
|
2605
|
+
}
|
|
2606
|
+
const isState = isObject(glob) && glob.tokens && glob.input;
|
|
2607
|
+
if (glob === "" || typeof glob !== "string" && !isState) throw new TypeError("Expected pattern to be a non-empty string");
|
|
2608
|
+
const opts = options || {};
|
|
2609
|
+
const posix = opts.windows;
|
|
2610
|
+
const regex = isState ? picomatch.compileRe(glob, options) : picomatch.makeRe(glob, options, false, true);
|
|
2611
|
+
const state = regex.state;
|
|
2612
|
+
delete regex.state;
|
|
2613
|
+
let isIgnored = () => false;
|
|
2614
|
+
if (opts.ignore) {
|
|
2615
|
+
const ignoreOpts = {
|
|
2616
|
+
...options,
|
|
2617
|
+
ignore: null,
|
|
2618
|
+
onMatch: null,
|
|
2619
|
+
onResult: null
|
|
2620
|
+
};
|
|
2621
|
+
isIgnored = picomatch(opts.ignore, ignoreOpts, returnState);
|
|
2622
|
+
}
|
|
2623
|
+
const matcher = (input, returnObject = false) => {
|
|
2624
|
+
const { isMatch, match, output } = picomatch.test(input, regex, options, {
|
|
2625
|
+
glob,
|
|
2626
|
+
posix
|
|
2627
|
+
});
|
|
2628
|
+
const result = {
|
|
2629
|
+
glob,
|
|
2630
|
+
state,
|
|
2631
|
+
regex,
|
|
2632
|
+
posix,
|
|
2633
|
+
input,
|
|
2634
|
+
output,
|
|
2635
|
+
match,
|
|
2636
|
+
isMatch
|
|
2637
|
+
};
|
|
2638
|
+
if (typeof opts.onResult === "function") opts.onResult(result);
|
|
2639
|
+
if (isMatch === false) {
|
|
2640
|
+
result.isMatch = false;
|
|
2641
|
+
return returnObject ? result : false;
|
|
2642
|
+
}
|
|
2643
|
+
if (isIgnored(input)) {
|
|
2644
|
+
if (typeof opts.onIgnore === "function") opts.onIgnore(result);
|
|
2645
|
+
result.isMatch = false;
|
|
2646
|
+
return returnObject ? result : false;
|
|
2647
|
+
}
|
|
2648
|
+
if (typeof opts.onMatch === "function") opts.onMatch(result);
|
|
2649
|
+
return returnObject ? result : true;
|
|
2650
|
+
};
|
|
2651
|
+
if (returnState) matcher.state = state;
|
|
2652
|
+
return matcher;
|
|
2653
|
+
};
|
|
2654
|
+
/**
|
|
2655
|
+
* Test `input` with the given `regex`. This is used by the main
|
|
2656
|
+
* `picomatch()` function to test the input string.
|
|
2657
|
+
*
|
|
2658
|
+
* ```js
|
|
2659
|
+
* const picomatch = require('picomatch');
|
|
2660
|
+
* // picomatch.test(input, regex[, options]);
|
|
2661
|
+
*
|
|
2662
|
+
* console.log(picomatch.test('foo/bar', /^(?:([^/]*?)\/([^/]*?))$/));
|
|
2663
|
+
* // { isMatch: true, match: [ 'foo/', 'foo', 'bar' ], output: 'foo/bar' }
|
|
2664
|
+
* ```
|
|
2665
|
+
* @param {String} `input` String to test.
|
|
2666
|
+
* @param {RegExp} `regex`
|
|
2667
|
+
* @return {Object} Returns an object with matching info.
|
|
2668
|
+
* @api public
|
|
2669
|
+
*/
|
|
2670
|
+
picomatch.test = (input, regex, options, { glob, posix } = {}) => {
|
|
2671
|
+
if (typeof input !== "string") throw new TypeError("Expected input to be a string");
|
|
2672
|
+
if (input === "") return {
|
|
2673
|
+
isMatch: false,
|
|
2674
|
+
output: ""
|
|
2675
|
+
};
|
|
2676
|
+
const opts = options || {};
|
|
2677
|
+
const format = opts.format || (posix ? utils.toPosixSlashes : null);
|
|
2678
|
+
let match = input === glob;
|
|
2679
|
+
let output = match && format ? format(input) : input;
|
|
2680
|
+
if (match === false) {
|
|
2681
|
+
output = format ? format(input) : input;
|
|
2682
|
+
match = output === glob;
|
|
2683
|
+
}
|
|
2684
|
+
if (match === false || opts.capture === true) if (opts.matchBase === true || opts.basename === true) match = picomatch.matchBase(input, regex, options, posix);
|
|
2685
|
+
else match = regex.exec(output);
|
|
2686
|
+
return {
|
|
2687
|
+
isMatch: Boolean(match),
|
|
2688
|
+
match,
|
|
2689
|
+
output
|
|
2690
|
+
};
|
|
2691
|
+
};
|
|
2692
|
+
/**
|
|
2693
|
+
* Match the basename of a filepath.
|
|
2694
|
+
*
|
|
2695
|
+
* ```js
|
|
2696
|
+
* const picomatch = require('picomatch');
|
|
2697
|
+
* // picomatch.matchBase(input, glob[, options]);
|
|
2698
|
+
* console.log(picomatch.matchBase('foo/bar.js', '*.js'); // true
|
|
2699
|
+
* ```
|
|
2700
|
+
* @param {String} `input` String to test.
|
|
2701
|
+
* @param {RegExp|String} `glob` Glob pattern or regex created by [.makeRe](#makeRe).
|
|
2702
|
+
* @return {Boolean}
|
|
2703
|
+
* @api public
|
|
2704
|
+
*/
|
|
2705
|
+
picomatch.matchBase = (input, glob, options) => {
|
|
2706
|
+
return (glob instanceof RegExp ? glob : picomatch.makeRe(glob, options)).test(utils.basename(input));
|
|
2707
|
+
};
|
|
2708
|
+
/**
|
|
2709
|
+
* Returns true if **any** of the given glob `patterns` match the specified `string`.
|
|
2710
|
+
*
|
|
2711
|
+
* ```js
|
|
2712
|
+
* const picomatch = require('picomatch');
|
|
2713
|
+
* // picomatch.isMatch(string, patterns[, options]);
|
|
2714
|
+
*
|
|
2715
|
+
* console.log(picomatch.isMatch('a.a', ['b.*', '*.a'])); //=> true
|
|
2716
|
+
* console.log(picomatch.isMatch('a.a', 'b.*')); //=> false
|
|
2717
|
+
* ```
|
|
2718
|
+
* @param {String|Array} str The string to test.
|
|
2719
|
+
* @param {String|Array} patterns One or more glob patterns to use for matching.
|
|
2720
|
+
* @param {Object} [options] See available [options](#options).
|
|
2721
|
+
* @return {Boolean} Returns true if any patterns match `str`
|
|
2722
|
+
* @api public
|
|
2723
|
+
*/
|
|
2724
|
+
picomatch.isMatch = (str, patterns, options) => picomatch(patterns, options)(str);
|
|
2725
|
+
/**
|
|
2726
|
+
* Parse a glob pattern to create the source string for a regular
|
|
2727
|
+
* expression.
|
|
2728
|
+
*
|
|
2729
|
+
* ```js
|
|
2730
|
+
* const picomatch = require('picomatch');
|
|
2731
|
+
* const result = picomatch.parse(pattern[, options]);
|
|
2732
|
+
* ```
|
|
2733
|
+
* @param {String} `pattern`
|
|
2734
|
+
* @param {Object} `options`
|
|
2735
|
+
* @return {Object} Returns an object with useful properties and output to be used as a regex source string.
|
|
2736
|
+
* @api public
|
|
2737
|
+
*/
|
|
2738
|
+
picomatch.parse = (pattern, options) => {
|
|
2739
|
+
if (Array.isArray(pattern)) return pattern.map((p) => picomatch.parse(p, options));
|
|
2740
|
+
return parse(pattern, {
|
|
2741
|
+
...options,
|
|
2742
|
+
fastpaths: false
|
|
2743
|
+
});
|
|
2744
|
+
};
|
|
2745
|
+
/**
|
|
2746
|
+
* Scan a glob pattern to separate the pattern into segments.
|
|
2747
|
+
*
|
|
2748
|
+
* ```js
|
|
2749
|
+
* const picomatch = require('picomatch');
|
|
2750
|
+
* // picomatch.scan(input[, options]);
|
|
2751
|
+
*
|
|
2752
|
+
* const result = picomatch.scan('!./foo/*.js');
|
|
2753
|
+
* console.log(result);
|
|
2754
|
+
* { prefix: '!./',
|
|
2755
|
+
* input: '!./foo/*.js',
|
|
2756
|
+
* start: 3,
|
|
2757
|
+
* base: 'foo',
|
|
2758
|
+
* glob: '*.js',
|
|
2759
|
+
* isBrace: false,
|
|
2760
|
+
* isBracket: false,
|
|
2761
|
+
* isGlob: true,
|
|
2762
|
+
* isExtglob: false,
|
|
2763
|
+
* isGlobstar: false,
|
|
2764
|
+
* negated: true }
|
|
2765
|
+
* ```
|
|
2766
|
+
* @param {String} `input` Glob pattern to scan.
|
|
2767
|
+
* @param {Object} `options`
|
|
2768
|
+
* @return {Object} Returns an object with
|
|
2769
|
+
* @api public
|
|
2770
|
+
*/
|
|
2771
|
+
picomatch.scan = (input, options) => scan(input, options);
|
|
2772
|
+
/**
|
|
2773
|
+
* Compile a regular expression from the `state` object returned by the
|
|
2774
|
+
* [parse()](#parse) method.
|
|
2775
|
+
*
|
|
2776
|
+
* ```js
|
|
2777
|
+
* const picomatch = require('picomatch');
|
|
2778
|
+
* const state = picomatch.parse('*.js');
|
|
2779
|
+
* // picomatch.compileRe(state[, options]);
|
|
2780
|
+
*
|
|
2781
|
+
* console.log(picomatch.compileRe(state));
|
|
2782
|
+
* //=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/
|
|
2783
|
+
* ```
|
|
2784
|
+
* @param {Object} `state`
|
|
2785
|
+
* @param {Object} `options`
|
|
2786
|
+
* @param {Boolean} `returnOutput` Intended for implementors, this argument allows you to return the raw output from the parser.
|
|
2787
|
+
* @param {Boolean} `returnState` Adds the state to a `state` property on the returned regex. Useful for implementors and debugging.
|
|
2788
|
+
* @return {RegExp}
|
|
2789
|
+
* @api public
|
|
2790
|
+
*/
|
|
2791
|
+
picomatch.compileRe = (state, options, returnOutput = false, returnState = false) => {
|
|
2792
|
+
if (returnOutput === true) return state.output;
|
|
2793
|
+
const opts = options || {};
|
|
2794
|
+
const prepend = opts.contains ? "" : "^";
|
|
2795
|
+
const append = opts.contains ? "" : "$";
|
|
2796
|
+
let source = `${prepend}(?:${state.output})${append}`;
|
|
2797
|
+
if (state && state.negated === true) source = `^(?!${source}).*$`;
|
|
2798
|
+
const regex = picomatch.toRegex(source, options);
|
|
2799
|
+
if (returnState === true) regex.state = state;
|
|
2800
|
+
return regex;
|
|
2801
|
+
};
|
|
2802
|
+
/**
|
|
2803
|
+
* Create a regular expression from a parsed glob pattern.
|
|
2804
|
+
*
|
|
2805
|
+
* ```js
|
|
2806
|
+
* const picomatch = require('picomatch');
|
|
2807
|
+
* // picomatch.makeRe(state[, options]);
|
|
2808
|
+
*
|
|
2809
|
+
* const result = picomatch.makeRe('*.js');
|
|
2810
|
+
* console.log(result);
|
|
2811
|
+
* //=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/
|
|
2812
|
+
* ```
|
|
2813
|
+
* @param {String} `state` The object returned from the `.parse` method.
|
|
2814
|
+
* @param {Object} `options`
|
|
2815
|
+
* @param {Boolean} `returnOutput` Implementors may use this argument to return the compiled output, instead of a regular expression. This is not exposed on the options to prevent end-users from mutating the result.
|
|
2816
|
+
* @param {Boolean} `returnState` Implementors may use this argument to return the state from the parsed glob with the returned regular expression.
|
|
2817
|
+
* @return {RegExp} Returns a regex created from the given pattern.
|
|
2818
|
+
* @api public
|
|
2819
|
+
*/
|
|
2820
|
+
picomatch.makeRe = (input, options = {}, returnOutput = false, returnState = false) => {
|
|
2821
|
+
if (!input || typeof input !== "string") throw new TypeError("Expected a non-empty string");
|
|
2822
|
+
let parsed = {
|
|
2823
|
+
negated: false,
|
|
2824
|
+
fastpaths: true
|
|
2825
|
+
};
|
|
2826
|
+
if (options.fastpaths !== false && (input[0] === "." || input[0] === "*")) parsed.output = parse.fastpaths(input, options);
|
|
2827
|
+
if (!parsed.output) parsed = parse(input, options);
|
|
2828
|
+
return picomatch.compileRe(parsed, options, returnOutput, returnState);
|
|
2829
|
+
};
|
|
2830
|
+
/**
|
|
2831
|
+
* Create a regular expression from the given regex source string.
|
|
2832
|
+
*
|
|
2833
|
+
* ```js
|
|
2834
|
+
* const picomatch = require('picomatch');
|
|
2835
|
+
* // picomatch.toRegex(source[, options]);
|
|
2836
|
+
*
|
|
2837
|
+
* const { output } = picomatch.parse('*.js');
|
|
2838
|
+
* console.log(picomatch.toRegex(output));
|
|
2839
|
+
* //=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/
|
|
2840
|
+
* ```
|
|
2841
|
+
* @param {String} `source` Regular expression source string.
|
|
2842
|
+
* @param {Object} `options`
|
|
2843
|
+
* @return {RegExp}
|
|
2844
|
+
* @api public
|
|
2845
|
+
*/
|
|
2846
|
+
picomatch.toRegex = (source, options) => {
|
|
2847
|
+
try {
|
|
2848
|
+
const opts = options || {};
|
|
2849
|
+
return new RegExp(source, opts.flags || (opts.nocase ? "i" : ""));
|
|
2850
|
+
} catch (err) {
|
|
2851
|
+
if (options && options.debug === true) throw err;
|
|
2852
|
+
return /$^/;
|
|
2853
|
+
}
|
|
2854
|
+
};
|
|
2855
|
+
/**
|
|
2856
|
+
* Picomatch constants.
|
|
2857
|
+
* @return {Object}
|
|
2858
|
+
*/
|
|
2859
|
+
picomatch.constants = constants;
|
|
2860
|
+
/**
|
|
2861
|
+
* Expose "picomatch"
|
|
2862
|
+
*/
|
|
2863
|
+
module.exports = picomatch;
|
|
2864
|
+
}));
|
|
2865
|
+
//#endregion
|
|
2866
|
+
//#region lib/types/client/tab-registry.js
|
|
2867
|
+
var import_posix = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
2868
|
+
module.exports = require_picomatch();
|
|
2869
|
+
})))(), 1);
|
|
2870
|
+
/** Rank of each band, highest first. */
|
|
2871
|
+
const RANKS = {
|
|
2872
|
+
extension: 3,
|
|
2873
|
+
builtin: 2,
|
|
2874
|
+
fallback: 1
|
|
2875
|
+
};
|
|
2876
|
+
/** The band a definition that names none is in. */
|
|
2877
|
+
const DEFAULT_BAND = "extension";
|
|
2878
|
+
/**
|
|
2879
|
+
* Whether a band may join a held kind: an `extension` and a `builtin` pair up
|
|
2880
|
+
* once, and a `fallback` shares its kind with nothing.
|
|
2881
|
+
*/
|
|
2882
|
+
function coexists(slot, band) {
|
|
2883
|
+
return band !== "fallback" && slot.inForce.band !== "fallback" && slot.inForce.band !== band && slot.shadowed === void 0;
|
|
2884
|
+
}
|
|
2885
|
+
/**
|
|
2886
|
+
* The address's URI path: what a pattern with no scheme separator matches
|
|
2887
|
+
* against. `dsh-resource://file/session/s1/home/me/b.md` gives `/session/s1/home/me/b.md`;
|
|
2888
|
+
* `sidebar://guide` gives `''`; an address that is not a URI gives nothing.
|
|
2889
|
+
*/
|
|
2890
|
+
function pathOf(address) {
|
|
2891
|
+
try {
|
|
2892
|
+
return new URL(address).pathname;
|
|
2893
|
+
} catch {
|
|
2894
|
+
return;
|
|
2895
|
+
}
|
|
2896
|
+
}
|
|
2897
|
+
/** Compile one declared pattern into the test the router runs. */
|
|
2898
|
+
function matcherFor(pattern) {
|
|
2899
|
+
const whole = pattern.includes(":");
|
|
2900
|
+
const match = (0, import_posix.default)(pattern, {
|
|
2901
|
+
nocase: true,
|
|
2902
|
+
dot: true,
|
|
2903
|
+
...whole ? {} : { basename: true }
|
|
2904
|
+
});
|
|
2905
|
+
return (address) => {
|
|
2906
|
+
if (whole) return match(address);
|
|
2907
|
+
const path = pathOf(address);
|
|
2908
|
+
return path !== void 0 && match(path);
|
|
2909
|
+
};
|
|
2910
|
+
}
|
|
2911
|
+
/**
|
|
2912
|
+
* The registered tab types.
|
|
2913
|
+
*
|
|
2914
|
+
* Registration order is part of the contract: it breaks ties between types that
|
|
2915
|
+
* recognize an address equally well.
|
|
2916
|
+
*/
|
|
2917
|
+
var SidebarRightTabRegistry = class {
|
|
2918
|
+
ctx;
|
|
2919
|
+
kinds = /* @__PURE__ */ new Map();
|
|
2920
|
+
ids = /* @__PURE__ */ new Set();
|
|
2921
|
+
listeners = /* @__PURE__ */ new Set();
|
|
2922
|
+
registrations = 0;
|
|
2923
|
+
cached = [];
|
|
2924
|
+
guideEntries = [];
|
|
2925
|
+
/** @param ctx - Context whose effects own the contributed types. */
|
|
2926
|
+
constructor(ctx) {
|
|
2927
|
+
this.ctx = ctx;
|
|
2928
|
+
}
|
|
2929
|
+
/**
|
|
2930
|
+
* Register one tab type for the caller's lifetime.
|
|
2931
|
+
*
|
|
2932
|
+
* The caller holds the returned disposer inside its own `ctx.effect`, so a
|
|
2933
|
+
* type's registration lives exactly as long as the plugin that contributed it.
|
|
2934
|
+
* An `extension` may register a kind a `builtin` already holds and takes it
|
|
2935
|
+
* over until it unregisters; a second registration in the same band, or any
|
|
2936
|
+
* registration meeting a `fallback` of the same kind, is a wiring mistake, and
|
|
2937
|
+
* so is an `id` already in use.
|
|
2938
|
+
* @param definition - the contributed type.
|
|
2939
|
+
* @returns idempotent disposer.
|
|
2940
|
+
* @throws when the id is taken, or the kind is already registered in a way this one cannot coexist with.
|
|
2941
|
+
*/
|
|
2942
|
+
register(definition) {
|
|
2943
|
+
const { id, kind } = definition;
|
|
2944
|
+
const band = definition.priority ?? DEFAULT_BAND;
|
|
2945
|
+
if (this.ids.has(id)) throw new Error(`sidebarRight: tab type id "${id}" is already registered`);
|
|
2946
|
+
const held = this.kinds.get(kind);
|
|
2947
|
+
if (held !== void 0 && !coexists(held, band)) throw new Error(`sidebarRight: tab kind "${kind}" is already registered (${held.inForce.band})`);
|
|
2948
|
+
this.registrations += 1;
|
|
2949
|
+
const entry = {
|
|
2950
|
+
definition,
|
|
2951
|
+
band,
|
|
2952
|
+
matchers: (definition.patterns ?? []).map((pattern) => ({
|
|
2953
|
+
pattern,
|
|
2954
|
+
test: matcherFor(pattern)
|
|
2955
|
+
})),
|
|
2956
|
+
order: this.registrations
|
|
2957
|
+
};
|
|
2958
|
+
const dispose = this.ctx.effect(() => {
|
|
2959
|
+
this.ids.add(id);
|
|
2960
|
+
const slot = this.enter(kind, entry);
|
|
2961
|
+
this.refresh();
|
|
2962
|
+
return () => {
|
|
2963
|
+
this.ids.delete(id);
|
|
2964
|
+
this.leave(kind, slot, entry);
|
|
2965
|
+
this.refresh();
|
|
2966
|
+
};
|
|
2967
|
+
}, `sidebarRight.tabs.register(${JSON.stringify(id)})`);
|
|
2968
|
+
return () => {
|
|
2969
|
+
dispose();
|
|
2970
|
+
};
|
|
2971
|
+
}
|
|
2972
|
+
/** Add a registration to its kind's slot, the higher band in force; `coexists` has already admitted it. */
|
|
2973
|
+
enter(kind, entry) {
|
|
2974
|
+
const held = this.kinds.get(kind);
|
|
2975
|
+
if (held === void 0) {
|
|
2976
|
+
const slot = {
|
|
2977
|
+
inForce: entry,
|
|
2978
|
+
shadowed: void 0
|
|
2979
|
+
};
|
|
2980
|
+
this.kinds.set(kind, slot);
|
|
2981
|
+
return slot;
|
|
2982
|
+
}
|
|
2983
|
+
if (RANKS[entry.band] > RANKS[held.inForce.band]) {
|
|
2984
|
+
held.shadowed = held.inForce;
|
|
2985
|
+
held.inForce = entry;
|
|
2986
|
+
} else held.shadowed = entry;
|
|
2987
|
+
return held;
|
|
2988
|
+
}
|
|
2989
|
+
/** Remove a registration from its kind's slot: a shadowed builtin resumes, and an emptied kind is freed. */
|
|
2990
|
+
leave(kind, slot, entry) {
|
|
2991
|
+
if (slot.inForce !== entry) slot.shadowed = void 0;
|
|
2992
|
+
else if (slot.shadowed === void 0) this.kinds.delete(kind);
|
|
2993
|
+
else {
|
|
2994
|
+
slot.inForce = slot.shadowed;
|
|
2995
|
+
slot.shadowed = void 0;
|
|
2996
|
+
}
|
|
2997
|
+
}
|
|
2998
|
+
/** Every kind's registration in force, in registration order. */
|
|
2999
|
+
active() {
|
|
3000
|
+
return [...this.kinds.values()].map((slot) => slot.inForce).sort((left, right) => left.order - right.order);
|
|
3001
|
+
}
|
|
3002
|
+
/**
|
|
3003
|
+
* Registered types in registration order.
|
|
3004
|
+
* @returns reference-stable entries.
|
|
3005
|
+
*/
|
|
3006
|
+
entries() {
|
|
3007
|
+
return this.cached;
|
|
3008
|
+
}
|
|
3009
|
+
/**
|
|
3010
|
+
* Every type in force's guide entries, in `order`, each naming the kind it opens.
|
|
3011
|
+
* @returns reference-stable entries.
|
|
3012
|
+
*/
|
|
3013
|
+
guide() {
|
|
3014
|
+
return this.guideEntries;
|
|
3015
|
+
}
|
|
3016
|
+
/**
|
|
3017
|
+
* The type in force for a kind.
|
|
3018
|
+
* @param kind - the type discriminator.
|
|
3019
|
+
* @returns the type, or `undefined` when nothing registered it.
|
|
3020
|
+
*/
|
|
3021
|
+
get(kind) {
|
|
3022
|
+
return this.kinds.get(kind)?.inForce.definition;
|
|
3023
|
+
}
|
|
3024
|
+
/**
|
|
3025
|
+
* Every type that would open an address, best first.
|
|
3026
|
+
*
|
|
3027
|
+
* Ranked by priority band, then by the length of the pattern that matched,
|
|
3028
|
+
* then by registration order. Types whose `canOpen` vetoes are absent.
|
|
3029
|
+
* @param address - the address a caller wants opened.
|
|
3030
|
+
* @returns the ranked types; empty when nothing recognizes the address.
|
|
3031
|
+
*/
|
|
3032
|
+
candidates(address) {
|
|
3033
|
+
const ranked = [];
|
|
3034
|
+
for (const { definition, band, matchers, order } of this.active()) {
|
|
3035
|
+
let length = -1;
|
|
3036
|
+
for (const matcher of matchers) if (matcher.test(address) && matcher.pattern.length > length) length = matcher.pattern.length;
|
|
3037
|
+
if (length < 0) continue;
|
|
3038
|
+
if (definition.canOpen !== void 0 && !definition.canOpen(address)) continue;
|
|
3039
|
+
ranked.push({
|
|
3040
|
+
definition,
|
|
3041
|
+
rank: RANKS[band],
|
|
3042
|
+
length,
|
|
3043
|
+
order
|
|
3044
|
+
});
|
|
3045
|
+
}
|
|
3046
|
+
ranked.sort((left, right) => right.rank - left.rank || right.length - left.length || left.order - right.order);
|
|
3047
|
+
return ranked.map((entry) => entry.definition);
|
|
3048
|
+
}
|
|
3049
|
+
/**
|
|
3050
|
+
* Decide which type opens an address, and as what.
|
|
3051
|
+
*
|
|
3052
|
+
* Without `kind`, the best candidate wins. With `kind`, that type opens the
|
|
3053
|
+
* address if its `canOpen` agrees — its globs are not consulted, because
|
|
3054
|
+
* naming the type IS the decision.
|
|
3055
|
+
*
|
|
3056
|
+
* An address no type will open is a wiring mistake, not a user error, so this
|
|
3057
|
+
* throws rather than reporting absence.
|
|
3058
|
+
* @param address - the address a caller wants opened.
|
|
3059
|
+
* @param kind - a type named by the caller, overriding the ranking.
|
|
3060
|
+
* @returns the claiming type and the record to open.
|
|
3061
|
+
*/
|
|
3062
|
+
claim(address, kind) {
|
|
3063
|
+
if (kind !== void 0) {
|
|
3064
|
+
const definition = this.get(kind);
|
|
3065
|
+
if (definition === void 0) throw new Error(`sidebarRight: no tab type is registered as "${kind}"`);
|
|
3066
|
+
if (definition.canOpen !== void 0 && !definition.canOpen(address)) throw new Error(`sidebarRight: tab type "${kind}" refuses "${address}"`);
|
|
3067
|
+
return {
|
|
3068
|
+
kind,
|
|
3069
|
+
contentId: address,
|
|
3070
|
+
title: definition.title(address)
|
|
3071
|
+
};
|
|
3072
|
+
}
|
|
3073
|
+
const [chosen] = this.candidates(address);
|
|
3074
|
+
if (chosen === void 0) throw new Error(`sidebarRight: no registered tab type claims "${address}"`);
|
|
3075
|
+
return {
|
|
3076
|
+
kind: chosen.kind,
|
|
3077
|
+
contentId: address,
|
|
3078
|
+
title: chosen.title(address)
|
|
3079
|
+
};
|
|
3080
|
+
}
|
|
3081
|
+
/**
|
|
3082
|
+
* Observe low-frequency registry changes.
|
|
3083
|
+
* @param listener - synchronous invalidation callback.
|
|
3084
|
+
* @returns unsubscribe callback.
|
|
3085
|
+
*/
|
|
3086
|
+
subscribe(listener) {
|
|
3087
|
+
this.listeners.add(listener);
|
|
3088
|
+
return () => {
|
|
3089
|
+
this.listeners.delete(listener);
|
|
3090
|
+
};
|
|
3091
|
+
}
|
|
3092
|
+
refresh() {
|
|
3093
|
+
this.cached = this.active().map((entry) => entry.definition);
|
|
3094
|
+
this.guideEntries = this.cached.flatMap((definition) => (definition.guide ?? []).map((entry) => ({
|
|
3095
|
+
...entry,
|
|
3096
|
+
kind: definition.kind
|
|
3097
|
+
}))).sort((left, right) => left.order - right.order);
|
|
3098
|
+
(0, _deepseek_ai_dsh_client_store.notifySubscribers)(this.listeners, "[ui-sidebar-right] tab registry");
|
|
3099
|
+
}
|
|
3100
|
+
};
|
|
3101
|
+
//#endregion
|
|
3102
|
+
//#region lib/types/client/stores.js
|
|
3103
|
+
/**
|
|
3104
|
+
* The store shell over the docking kit: one surface per session, held as plain
|
|
3105
|
+
* data so the kit's pure functions are the only thing that ever computes a
|
|
3106
|
+
* layout.
|
|
3107
|
+
*
|
|
3108
|
+
* Every action follows the same steps — mint the ids the intent needs, ask the
|
|
3109
|
+
* kit's planner what operations carry it out, let the settle planner keep every
|
|
3110
|
+
* pane populated, record it all as one history entry — and then assigns the
|
|
3111
|
+
* session's whole surface back in one go. Nothing here reaches into a draft to
|
|
3112
|
+
* edit a layout in place, which is what keeps the kit testable without a store
|
|
3113
|
+
* and keeps snapshot identity honest.
|
|
3114
|
+
*
|
|
3115
|
+
* The settle step is this product's rule, not the kit's: a docked pane never
|
|
3116
|
+
* stays empty, and the last pane reseeds the guide tab, so there is always at
|
|
3117
|
+
* least one tab to look at.
|
|
3118
|
+
*
|
|
3119
|
+
* A focus that changes nothing — a tab already active in its already-active
|
|
3120
|
+
* pane, a pane already active — plans nothing and records nothing, whoever
|
|
3121
|
+
* asks: the kit's chip click and `ctx.sidebarRight.focus` alike.
|
|
3122
|
+
*
|
|
3123
|
+
* So is the guide's uniqueness: a pane holds at most one guide tab. Opening the
|
|
3124
|
+
* guide into a pane that has one focuses it, and a guide dragged, dropped, or
|
|
3125
|
+
* docked into such a pane merges into it — the arriving guide closes and the
|
|
3126
|
+
* pane's own is focused. The kit plans none of this; it is decided here before
|
|
3127
|
+
* its planners run.
|
|
3128
|
+
*/
|
|
3129
|
+
/** A mint that counts, so the surface can carry its position forward. */
|
|
3130
|
+
function counting(from) {
|
|
3131
|
+
let counter = from;
|
|
3132
|
+
const mint = ((prefix) => {
|
|
3133
|
+
counter += 1;
|
|
3134
|
+
return `${prefix}${counter}`;
|
|
3135
|
+
});
|
|
3136
|
+
return {
|
|
3137
|
+
mint,
|
|
3138
|
+
used: () => counter
|
|
3139
|
+
};
|
|
3140
|
+
}
|
|
3141
|
+
/**
|
|
3142
|
+
* The surface a session starts with: collapsed, one pane, one guide tab.
|
|
3143
|
+
* @param seedTitle - the guide type's display name at mint time.
|
|
3144
|
+
* @returns the initial surface.
|
|
3145
|
+
*/
|
|
3146
|
+
function createSurface(seedTitle) {
|
|
3147
|
+
const counter = counting(0);
|
|
3148
|
+
return {
|
|
3149
|
+
layout: (0, _deepseek_ai_dsh_client_ui_dockkit.createInitialState)({ next: counter.mint }, (id) => makeGuideTab(id, seedTitle())),
|
|
3150
|
+
history: _deepseek_ai_dsh_client_ui_dockkit.EMPTY_HISTORY,
|
|
3151
|
+
minted: counter.used()
|
|
3152
|
+
};
|
|
3153
|
+
}
|
|
3154
|
+
/** The guide tab a pane holds, if any. */
|
|
3155
|
+
function paneGuide(state, paneId) {
|
|
3156
|
+
return (0, _deepseek_ai_dsh_client_ui_dockkit.findPaneContentTab)(state, paneId, pageAddress(GUIDE_KIND), GUIDE_KIND);
|
|
3157
|
+
}
|
|
3158
|
+
/** Whether a tab is the guide, which a pane holds at most once and which is therefore never copied. */
|
|
3159
|
+
function isGuide(state, tabId) {
|
|
3160
|
+
return state.tabs[tabId]?.kind === GUIDE_KIND;
|
|
3161
|
+
}
|
|
3162
|
+
/** Focus a tab: nothing to plan while it is its pane's active tab and its pane is the active one. */
|
|
3163
|
+
function planFocusTab(state, tabId) {
|
|
3164
|
+
const pane = (0, _deepseek_ai_dsh_client_ui_dockkit.findTabPane)(state, tabId);
|
|
3165
|
+
return pane.activeTabId === tabId && state.activePaneId === pane.id ? [] : [{
|
|
3166
|
+
type: "focusTab",
|
|
3167
|
+
tabId
|
|
3168
|
+
}];
|
|
3169
|
+
}
|
|
3170
|
+
/** Focus a pane: nothing to plan while it is the active one. */
|
|
3171
|
+
function planFocusPane(state, paneId) {
|
|
3172
|
+
return state.activePaneId === paneId ? [] : [{
|
|
3173
|
+
type: "focusPane",
|
|
3174
|
+
paneId
|
|
3175
|
+
}];
|
|
3176
|
+
}
|
|
3177
|
+
/**
|
|
3178
|
+
* Plan a tab's arrival in a docked pane: a guide arriving where one already is
|
|
3179
|
+
* merges into it, anything else plans as the kit does.
|
|
3180
|
+
* @param state - current layout.
|
|
3181
|
+
* @param tabId - the arriving tab.
|
|
3182
|
+
* @param toPaneId - the pane it arrives in.
|
|
3183
|
+
* @param otherwise - the kit's plan for the move.
|
|
3184
|
+
* @returns the operations.
|
|
3185
|
+
*/
|
|
3186
|
+
function arriving(state, tabId, toPaneId, otherwise) {
|
|
3187
|
+
if (!isGuide(state, tabId)) return otherwise();
|
|
3188
|
+
const existing = paneGuide(state, toPaneId);
|
|
3189
|
+
if (existing === void 0 || existing === tabId) return otherwise();
|
|
3190
|
+
return [{
|
|
3191
|
+
type: "closeTab",
|
|
3192
|
+
tabId
|
|
3193
|
+
}, {
|
|
3194
|
+
type: "focusTab",
|
|
3195
|
+
tabId: existing
|
|
3196
|
+
}];
|
|
3197
|
+
}
|
|
3198
|
+
/**
|
|
3199
|
+
* Run one planner against a surface, settle what it left behind, and record the
|
|
3200
|
+
* whole intent as one history entry.
|
|
3201
|
+
* @param surface - the session's current surface.
|
|
3202
|
+
* @param plan - the kit planner to consult.
|
|
3203
|
+
* @param seedTitle - the guide type's display name, for a reseeded root pane.
|
|
3204
|
+
* @returns the next surface, or the same one when the intent changes nothing.
|
|
3205
|
+
*/
|
|
3206
|
+
function advance(surface, plan, seedTitle) {
|
|
3207
|
+
const counter = counting(surface.minted);
|
|
3208
|
+
const planned = plan(surface.layout, counter.mint);
|
|
3209
|
+
if (planned.length === 0) return surface;
|
|
3210
|
+
const settled = (0, _deepseek_ai_dsh_client_ui_dockkit.planSettle)((0, _deepseek_ai_dsh_client_ui_dockkit.replay)(surface.layout, planned), counter.mint, (id) => makeGuideTab(id, seedTitle()));
|
|
3211
|
+
const stepped = (0, _deepseek_ai_dsh_client_ui_dockkit.record)(surface.history, surface.layout, [...planned, ...settled]);
|
|
3212
|
+
return {
|
|
3213
|
+
layout: stepped.state,
|
|
3214
|
+
history: stepped.history,
|
|
3215
|
+
minted: counter.used()
|
|
3216
|
+
};
|
|
3217
|
+
}
|
|
3218
|
+
/**
|
|
3219
|
+
* Replace one session's surface, leaving every other session by reference.
|
|
3220
|
+
*
|
|
3221
|
+
* A session with no surface yet gets its initial one even when the intent
|
|
3222
|
+
* changes nothing: materializing is itself the change.
|
|
3223
|
+
*/
|
|
3224
|
+
function seat(state, sessionId, seedTitle, next) {
|
|
3225
|
+
const existing = state.bySession[sessionId];
|
|
3226
|
+
const updated = next(existing ?? createSurface(seedTitle));
|
|
3227
|
+
return updated === existing ? state.bySession : {
|
|
3228
|
+
...state.bySession,
|
|
3229
|
+
[sessionId]: updated
|
|
3230
|
+
};
|
|
3231
|
+
}
|
|
3232
|
+
/** Step a surface through the history in one direction. */
|
|
3233
|
+
function stepped(surface, step) {
|
|
3234
|
+
const moved = step(surface.history, surface.layout);
|
|
3235
|
+
return moved === void 0 ? surface : {
|
|
3236
|
+
...surface,
|
|
3237
|
+
layout: moved.state,
|
|
3238
|
+
history: moved.history
|
|
3239
|
+
};
|
|
3240
|
+
}
|
|
3241
|
+
/**
|
|
3242
|
+
* Create the Sidebar store handle.
|
|
3243
|
+
*
|
|
3244
|
+
* The seed title arrives as a thunk rather than a string: a pane is seeded
|
|
3245
|
+
* whenever one is created, which can be long after the store was built and in a
|
|
3246
|
+
* language the user has since changed to.
|
|
3247
|
+
* @param seedTitle - the guide type's display name, read at each mint.
|
|
3248
|
+
* @returns the handle (spec, type, identity, and factory in one).
|
|
3249
|
+
*/
|
|
3250
|
+
function createSidebarRightStore(seedTitle) {
|
|
3251
|
+
return (0, _deepseek_ai_dsh_client_store.defineStore)({
|
|
3252
|
+
init: () => ({ bySession: {} }),
|
|
3253
|
+
actions: {
|
|
3254
|
+
open: (d, sessionId) => {
|
|
3255
|
+
d.bySession = seat(d, sessionId, seedTitle, (surface) => surface);
|
|
3256
|
+
},
|
|
3257
|
+
setExpanded: (d, sessionId, expanded) => {
|
|
3258
|
+
d.bySession = seat(d, sessionId, seedTitle, (s) => advance(s, (state) => (0, _deepseek_ai_dsh_client_ui_dockkit.planSetExpanded)(state, expanded), seedTitle));
|
|
3259
|
+
},
|
|
3260
|
+
toggleExpanded: (d, sessionId) => {
|
|
3261
|
+
d.bySession = seat(d, sessionId, seedTitle, (s) => advance(s, (state) => (0, _deepseek_ai_dsh_client_ui_dockkit.planSetExpanded)(state, !state.expanded), seedTitle));
|
|
3262
|
+
},
|
|
3263
|
+
setMode: (d, sessionId, mode) => {
|
|
3264
|
+
d.bySession = seat(d, sessionId, seedTitle, (s) => advance(s, (state) => (0, _deepseek_ai_dsh_client_ui_dockkit.planSetMode)(state, mode), seedTitle));
|
|
3265
|
+
},
|
|
3266
|
+
splitPane: (d, sessionId, paneId, settled) => {
|
|
3267
|
+
d.bySession = seat(d, sessionId, seedTitle, (s) => {
|
|
3268
|
+
const next = advance(s, (state, mint) => (0, _deepseek_ai_dsh_client_ui_dockkit.dockPaneIds)(state).length >= 2 ? [] : (0, _deepseek_ai_dsh_client_ui_dockkit.planSplitPane)(state, mint, paneId, (id) => makeGuideTab(id, seedTitle())), seedTitle);
|
|
3269
|
+
if (settled !== void 0 && next !== s) {
|
|
3270
|
+
const before = new Set((0, _deepseek_ai_dsh_client_ui_dockkit.dockPaneIds)(s.layout));
|
|
3271
|
+
for (const id of (0, _deepseek_ai_dsh_client_ui_dockkit.dockPaneIds)(next.layout)) if (!before.has(id)) settled(id);
|
|
3272
|
+
}
|
|
3273
|
+
return next;
|
|
3274
|
+
});
|
|
3275
|
+
},
|
|
3276
|
+
openContent: (d, sessionId, intent, settled) => {
|
|
3277
|
+
d.bySession = seat(d, sessionId, seedTitle, (s) => advance(s, (state, mint) => {
|
|
3278
|
+
const { kind, contentId, title, replaceTab: replace } = intent;
|
|
3279
|
+
const ops = [...(0, _deepseek_ai_dsh_client_ui_dockkit.planSetExpanded)(state, true)];
|
|
3280
|
+
const replaced = replace === void 0 ? void 0 : (0, _deepseek_ai_dsh_client_ui_dockkit.findTabPane)(state, replace);
|
|
3281
|
+
const lent = replace !== void 0 && replaced !== void 0 && replaced.host === "dock" ? replaced : void 0;
|
|
3282
|
+
const paneId = lent?.id ?? intent.paneId;
|
|
3283
|
+
const index = lent === void 0 || replace === void 0 ? void 0 : lent.tabs.indexOf(replace);
|
|
3284
|
+
const held = kind === "guide" ? paneGuide(state, paneId ?? (0, _deepseek_ai_dsh_client_ui_dockkit.activeDockPaneId)(state)) : void 0;
|
|
3285
|
+
const planned = held !== void 0 ? {
|
|
3286
|
+
ops: [{
|
|
3287
|
+
type: "focusTab",
|
|
3288
|
+
tabId: held
|
|
3289
|
+
}],
|
|
3290
|
+
tabId: held
|
|
3291
|
+
} : (0, _deepseek_ai_dsh_client_ui_dockkit.planOpenContent)(state, mint, {
|
|
3292
|
+
kind,
|
|
3293
|
+
contentId,
|
|
3294
|
+
title,
|
|
3295
|
+
...paneId === void 0 ? {} : { paneId },
|
|
3296
|
+
...index === void 0 ? {} : { index },
|
|
3297
|
+
...intent.revealIfOpened === void 0 ? {} : { revealIfOpened: intent.revealIfOpened }
|
|
3298
|
+
});
|
|
3299
|
+
ops.push(...planned.ops);
|
|
3300
|
+
if (replace !== void 0 && replace !== planned.tabId) ops.push({
|
|
3301
|
+
type: "closeTab",
|
|
3302
|
+
tabId: replace
|
|
3303
|
+
});
|
|
3304
|
+
settled(planned.tabId);
|
|
3305
|
+
return ops;
|
|
3306
|
+
}, seedTitle));
|
|
3307
|
+
},
|
|
3308
|
+
duplicateTab: (d, sessionId, tabId) => {
|
|
3309
|
+
d.bySession = seat(d, sessionId, seedTitle, (s) => advance(s, (state, mint) => isGuide(state, tabId) ? [] : (0, _deepseek_ai_dsh_client_ui_dockkit.planDuplicateTab)(state, mint, tabId).ops, seedTitle));
|
|
3310
|
+
},
|
|
3311
|
+
closeTab: (d, sessionId, tabId) => {
|
|
3312
|
+
d.bySession = seat(d, sessionId, seedTitle, (s) => advance(s, (state) => state.tabs[tabId] === void 0 ? [] : [{
|
|
3313
|
+
type: "closeTab",
|
|
3314
|
+
tabId
|
|
3315
|
+
}], seedTitle));
|
|
3316
|
+
},
|
|
3317
|
+
focusTab: (d, sessionId, tabId) => {
|
|
3318
|
+
d.bySession = seat(d, sessionId, seedTitle, (s) => advance(s, (state) => planFocusTab(state, tabId), seedTitle));
|
|
3319
|
+
},
|
|
3320
|
+
focusPane: (d, sessionId, paneId) => {
|
|
3321
|
+
d.bySession = seat(d, sessionId, seedTitle, (s) => advance(s, (state) => planFocusPane(state, paneId), seedTitle));
|
|
3322
|
+
},
|
|
3323
|
+
placeTab: (d, sessionId, tabId, toPaneId, index) => {
|
|
3324
|
+
d.bySession = seat(d, sessionId, seedTitle, (s) => advance(s, (state) => arriving(state, tabId, toPaneId, () => (0, _deepseek_ai_dsh_client_ui_dockkit.planPlaceTab)(state, tabId, toPaneId, index)), seedTitle));
|
|
3325
|
+
},
|
|
3326
|
+
dropTab: (d, sessionId, tabId, paneId, zone) => {
|
|
3327
|
+
d.bySession = seat(d, sessionId, seedTitle, (s) => advance(s, (state, mint) => {
|
|
3328
|
+
if (zone === "top" || zone === "bottom") return [];
|
|
3329
|
+
if (zone !== "center" && (0, _deepseek_ai_dsh_client_ui_dockkit.dockPaneIds)(state).length >= 2) return [];
|
|
3330
|
+
const plan = () => (0, _deepseek_ai_dsh_client_ui_dockkit.planDropTab)(state, mint, tabId, paneId, zone);
|
|
3331
|
+
return zone === "center" ? arriving(state, tabId, paneId, plan) : plan();
|
|
3332
|
+
}, seedTitle));
|
|
3333
|
+
},
|
|
3334
|
+
floatTab: (d, sessionId, tabId, rect) => {
|
|
3335
|
+
d.bySession = seat(d, sessionId, seedTitle, (s) => advance(s, (state, mint) => (0, _deepseek_ai_dsh_client_ui_dockkit.planFloatTab)(state, mint, tabId, rect).ops, seedTitle));
|
|
3336
|
+
},
|
|
3337
|
+
unfloatPane: (d, sessionId, paneId) => {
|
|
3338
|
+
d.bySession = seat(d, sessionId, seedTitle, (s) => advance(s, (state) => {
|
|
3339
|
+
const guide = paneGuide(state, paneId);
|
|
3340
|
+
const plan = () => (0, _deepseek_ai_dsh_client_ui_dockkit.planUnfloatPane)(state, paneId);
|
|
3341
|
+
return guide === void 0 ? plan() : arriving(state, guide, (0, _deepseek_ai_dsh_client_ui_dockkit.activeDockPaneId)(state), plan);
|
|
3342
|
+
}, seedTitle));
|
|
3343
|
+
},
|
|
3344
|
+
moveFloat: (d, sessionId, paneId, x, y) => {
|
|
3345
|
+
d.bySession = seat(d, sessionId, seedTitle, (s) => advance(s, () => [{
|
|
3346
|
+
type: "moveFloat",
|
|
3347
|
+
paneId,
|
|
3348
|
+
x,
|
|
3349
|
+
y
|
|
3350
|
+
}], seedTitle));
|
|
3351
|
+
},
|
|
3352
|
+
resizeFloat: (d, sessionId, paneId, rect) => {
|
|
3353
|
+
d.bySession = seat(d, sessionId, seedTitle, (s) => advance(s, () => [{
|
|
3354
|
+
type: "resizeFloat",
|
|
3355
|
+
paneId,
|
|
3356
|
+
rect
|
|
3357
|
+
}], seedTitle));
|
|
3358
|
+
},
|
|
3359
|
+
resizeSplit: (d, sessionId, splitId, sizes) => {
|
|
3360
|
+
d.bySession = seat(d, sessionId, seedTitle, (s) => advance(s, () => (0, _deepseek_ai_dsh_client_ui_dockkit.planResizeSplit)(splitId, sizes, .2), seedTitle));
|
|
3361
|
+
},
|
|
3362
|
+
undo: (d, sessionId) => {
|
|
3363
|
+
d.bySession = seat(d, sessionId, seedTitle, (s) => stepped(s, _deepseek_ai_dsh_client_ui_dockkit.stepBack));
|
|
3364
|
+
},
|
|
3365
|
+
redo: (d, sessionId) => {
|
|
3366
|
+
d.bySession = seat(d, sessionId, seedTitle, (s) => stepped(s, _deepseek_ai_dsh_client_ui_dockkit.stepForward));
|
|
3367
|
+
}
|
|
3368
|
+
}
|
|
3369
|
+
});
|
|
3370
|
+
}
|
|
3371
|
+
//#endregion
|
|
3372
|
+
//#region lib/types/client/locales.js
|
|
3373
|
+
/**
|
|
3374
|
+
* `sidebarRight` namespace dictionaries.
|
|
3375
|
+
*
|
|
3376
|
+
* Everything a user reads in this column is here, including the strings handed
|
|
3377
|
+
* to the docking kit — the kit renders no copy of its own, so its whole
|
|
3378
|
+
* vocabulary is this package's to own and translate.
|
|
3379
|
+
*/
|
|
3380
|
+
/** Simplified Chinese dictionary and key-set source of truth. */
|
|
3381
|
+
const zh = {
|
|
3382
|
+
"chrome.expand": "展开侧栏",
|
|
3383
|
+
"chrome.collapse": "收起侧栏",
|
|
3384
|
+
"chrome.toFullscreen": "全屏显示侧栏",
|
|
3385
|
+
"chrome.exitFullscreen": "退出侧栏全屏",
|
|
3386
|
+
"dock.emptyPane": "空面板",
|
|
3387
|
+
"dock.splitPane": "向右分栏",
|
|
3388
|
+
"dock.splitPaneDisabled": "已达两格上限",
|
|
3389
|
+
"dock.splitPaneNarrow": "栏宽不足,拖宽侧栏后再分栏",
|
|
3390
|
+
"dock.closeTab": "关闭",
|
|
3391
|
+
"dock.addTab": "新标签页",
|
|
3392
|
+
"dock.dockFloat": "收回到侧栏",
|
|
3393
|
+
"dock.closeFloat": "关闭",
|
|
3394
|
+
"tab.guide.title": "开始",
|
|
3395
|
+
"tab.unavailable": "这类内容还没有可用的查看方式。",
|
|
3396
|
+
"guide.lead": "侧栏用来放你想一直看着的东西。",
|
|
3397
|
+
"guide.body": "会话里的文件和产物会开在这一栏,也可以从下面的入口打开。"
|
|
3398
|
+
};
|
|
3399
|
+
/** English dictionary, checked against the Chinese key set. */
|
|
3400
|
+
const en = {
|
|
3401
|
+
"chrome.expand": "Open the sidebar",
|
|
3402
|
+
"chrome.collapse": "Close the sidebar",
|
|
3403
|
+
"chrome.toFullscreen": "Show the sidebar fullscreen",
|
|
3404
|
+
"chrome.exitFullscreen": "Exit sidebar fullscreen",
|
|
3405
|
+
"dock.emptyPane": "Empty pane",
|
|
3406
|
+
"dock.splitPane": "Split to the right",
|
|
3407
|
+
"dock.splitPaneDisabled": "Two panes is the limit",
|
|
3408
|
+
"dock.splitPaneNarrow": "Not enough width to split; widen the sidebar",
|
|
3409
|
+
"dock.closeTab": "Close",
|
|
3410
|
+
"dock.addTab": "New tab",
|
|
3411
|
+
"dock.dockFloat": "Send back to the sidebar",
|
|
3412
|
+
"dock.closeFloat": "Close",
|
|
3413
|
+
"tab.guide.title": "Start",
|
|
3414
|
+
"tab.unavailable": "Nothing here can view this kind of content yet.",
|
|
3415
|
+
"guide.lead": "The sidebar holds what you want to keep looking at.",
|
|
3416
|
+
"guide.body": "Files and artifacts from the conversation open in this column; the entries below open more."
|
|
3417
|
+
};
|
|
3418
|
+
//#endregion
|
|
3419
|
+
//#region lib/types/client/tabs/guide/definition.js
|
|
3420
|
+
/** The shipped guide implementation's identity: the key its body registers under. */
|
|
3421
|
+
const GUIDE_ID = "@deepseek-ai/dsh-client-ui-sidebar-right/guide";
|
|
3422
|
+
/**
|
|
3423
|
+
* The guide type's registry definition.
|
|
3424
|
+
*
|
|
3425
|
+
* A page type: it recognizes no resource address, because a guide views
|
|
3426
|
+
* nothing, and is opened by kind; `builtin` is the ordinary band for a type
|
|
3427
|
+
* shipped here.
|
|
3428
|
+
* @param t - namespace-bound translate, read fresh on every title call.
|
|
3429
|
+
* @returns the definition to register.
|
|
3430
|
+
*/
|
|
3431
|
+
function guideDefinition(t) {
|
|
3432
|
+
return {
|
|
3433
|
+
id: GUIDE_ID,
|
|
3434
|
+
kind: GUIDE_KIND,
|
|
3435
|
+
priority: "builtin",
|
|
3436
|
+
title: () => t("tab.guide.title")
|
|
3437
|
+
};
|
|
3438
|
+
}
|
|
3439
|
+
//#endregion
|
|
3440
|
+
//#region lib/types/client/tab-info.js
|
|
3441
|
+
/** Slot-owned tab information derived from framework-bound store and navigation hooks. */
|
|
3442
|
+
/**
|
|
3443
|
+
* Bind a tab occurrence without subscribing or creating records during factory evaluation.
|
|
3444
|
+
* @param standard - framework session identity.
|
|
3445
|
+
* @param context - stable record lifetime and framework-bound readers.
|
|
3446
|
+
* @returns the tab information hook.
|
|
3447
|
+
*/
|
|
3448
|
+
const tabInfoFactory = (standard, context) => {
|
|
3449
|
+
const { sessionId } = standard;
|
|
3450
|
+
const { tabId, title, fullscreen, signal, actions, useStore, useTabNavigation } = context;
|
|
3451
|
+
return function useTabInfo() {
|
|
3452
|
+
const layout = useStore((state) => state.bySession[sessionId]?.layout);
|
|
3453
|
+
const navigation = useTabNavigation(tabId);
|
|
3454
|
+
return (0, react.useMemo)(() => {
|
|
3455
|
+
const tab = layout?.tabs[tabId];
|
|
3456
|
+
if (layout === void 0 || tab === void 0 || navigation === void 0) throw new Error(`sidebarRight: tab "${tabId}" is not committed in session "${sessionId}"`);
|
|
3457
|
+
const pane = (0, _deepseek_ai_dsh_client_ui_dockkit.findTabPane)(layout, tabId);
|
|
3458
|
+
return {
|
|
3459
|
+
sidebar: {
|
|
3460
|
+
expanded: layout.expanded,
|
|
3461
|
+
fullscreen
|
|
3462
|
+
},
|
|
3463
|
+
panel: { id: pane.id },
|
|
3464
|
+
tab: {
|
|
3465
|
+
...tab,
|
|
3466
|
+
visible: pane.host === "float" || layout.expanded && (title || pane.activeTabId === tabId),
|
|
3467
|
+
navigation,
|
|
3468
|
+
signal,
|
|
3469
|
+
actions
|
|
3470
|
+
}
|
|
3471
|
+
};
|
|
3472
|
+
}, [
|
|
3473
|
+
layout,
|
|
3474
|
+
navigation,
|
|
3475
|
+
tabId,
|
|
3476
|
+
title,
|
|
3477
|
+
fullscreen,
|
|
3478
|
+
signal,
|
|
3479
|
+
actions
|
|
3480
|
+
]);
|
|
3481
|
+
};
|
|
3482
|
+
};
|
|
3483
|
+
/**
|
|
3484
|
+
* Forward the framework-bound tab hook to a guide replacement.
|
|
3485
|
+
* @param _standard - the guide's framework standard props.
|
|
3486
|
+
* @param useTabInfo - the enclosing tab's framework-bound reader.
|
|
3487
|
+
* @returns the same reader for the replacement.
|
|
3488
|
+
*/
|
|
3489
|
+
const guideTabInfoFactory = (_standard, useTabInfo) => useTabInfo;
|
|
3490
|
+
//#endregion
|
|
3491
|
+
//#region lib/types/client/index.js
|
|
3492
|
+
/** This package's copy namespace. */
|
|
3493
|
+
const NS = "sidebarRight";
|
|
3494
|
+
/** Required browser services: the slot registry, the frame's panel actions, copy, and the resource model. */
|
|
3495
|
+
const inject = [
|
|
3496
|
+
"slots",
|
|
3497
|
+
"layout",
|
|
3498
|
+
"locale",
|
|
3499
|
+
"resources"
|
|
3500
|
+
];
|
|
3501
|
+
/**
|
|
3502
|
+
* Client plugin body: provide the registry and the navigation face, register the
|
|
3503
|
+
* panel seat and the rail seat over one store with their extension children, and
|
|
3504
|
+
* register the guide type through the same public two-stage path any other type
|
|
3505
|
+
* uses.
|
|
3506
|
+
* @param ctx - client root context carrying the slot registry, the frame's face, and copy.
|
|
3507
|
+
*/
|
|
3508
|
+
function apply(ctx) {
|
|
3509
|
+
const t = ctx.locale.bind(NS);
|
|
3510
|
+
const tabs = new SidebarRightTabRegistry(ctx);
|
|
3511
|
+
const { controller, adopt } = createSidebarRightController(tabs, (address, signal) => {
|
|
3512
|
+
ctx.resources.pin(address, signal);
|
|
3513
|
+
});
|
|
3514
|
+
const disposeRegistry = ctx.reflect.provide("sidebarRightTabs", tabs);
|
|
3515
|
+
const disposeService = ctx.reflect.provide("sidebarRight", controller);
|
|
3516
|
+
ctx.effect(() => () => {
|
|
3517
|
+
controller.tabDomain.dispose();
|
|
3518
|
+
disposeService();
|
|
3519
|
+
disposeRegistry();
|
|
3520
|
+
}, "ui-sidebar-right: service faces");
|
|
3521
|
+
ctx.effect(() => ctx.locale.register(NS, {
|
|
3522
|
+
zh,
|
|
3523
|
+
en
|
|
3524
|
+
}), "ui-sidebar-right: dictionaries");
|
|
3525
|
+
ctx.effect(() => {
|
|
3526
|
+
const handle = createSidebarRightStore(() => t("tab.guide.title"));
|
|
3527
|
+
const adoptions = [];
|
|
3528
|
+
const store = {
|
|
3529
|
+
...handle,
|
|
3530
|
+
create: (scopeKey) => {
|
|
3531
|
+
const instance = handle.create(scopeKey);
|
|
3532
|
+
if (scopeKey !== void 0) adoptions.push(adopt(scopeKey, instance));
|
|
3533
|
+
return instance;
|
|
3534
|
+
}
|
|
3535
|
+
};
|
|
3536
|
+
const layout = ctx.layout;
|
|
3537
|
+
const injected = {
|
|
3538
|
+
syncPresentation({ shown, track, fullscreen }) {
|
|
3539
|
+
if (shown) layout.openRightbar(track, fullscreen);
|
|
3540
|
+
else layout.closeRightbar();
|
|
3541
|
+
},
|
|
3542
|
+
bindService: (binding) => controller.bind(binding),
|
|
3543
|
+
openTab: (kind, options) => {
|
|
3544
|
+
controller.openTab(kind, options);
|
|
3545
|
+
},
|
|
3546
|
+
hooks: { tabTypes: {
|
|
3547
|
+
subscribe: (listener) => tabs.subscribe(listener),
|
|
3548
|
+
getSnapshot: () => tabs.entries()
|
|
3549
|
+
} }
|
|
3550
|
+
};
|
|
3551
|
+
const disposeTypes = [tabs.register(guideDefinition(t))];
|
|
3552
|
+
const disposeSeat = ctx.slots.inject("rightbar", () => ctx.slots.register({
|
|
3553
|
+
name: "rightbar",
|
|
3554
|
+
locale: NS,
|
|
3555
|
+
children: {
|
|
3556
|
+
"sidebar.right.pane.tab": {
|
|
3557
|
+
kind: "keyed",
|
|
3558
|
+
scope: "session",
|
|
3559
|
+
inject: { hooks: { tabInfo: tabInfoFactory } }
|
|
3560
|
+
},
|
|
3561
|
+
"sidebar.right.pane.tab.title": {
|
|
3562
|
+
kind: "keyed",
|
|
3563
|
+
scope: "session",
|
|
3564
|
+
inject: { hooks: { tabInfo: tabInfoFactory } }
|
|
3565
|
+
},
|
|
3566
|
+
"sidebar.right.tab.menu.item": {
|
|
3567
|
+
kind: "list",
|
|
3568
|
+
scope: "session"
|
|
3569
|
+
}
|
|
3570
|
+
},
|
|
3571
|
+
store,
|
|
3572
|
+
inject: (sessionId) => ({
|
|
3573
|
+
...injected,
|
|
3574
|
+
keyedHooks: { tabNavigation: (key) => controller.tabDomain.occurrence(sessionId, { id: key }).navigation },
|
|
3575
|
+
occurrence: (tab) => controller.tabDomain.occurrence(sessionId, tab)
|
|
3576
|
+
})
|
|
3577
|
+
}, RightbarSeat));
|
|
3578
|
+
const disposeExpand = ctx.slots.inject("conversation.session.header.corner", () => ctx.slots.register({
|
|
3579
|
+
name: "conversation.session.header.corner",
|
|
3580
|
+
locale: NS,
|
|
3581
|
+
store
|
|
3582
|
+
}, ExpandButton));
|
|
3583
|
+
const guideInjected = { hooks: { guideEntries: {
|
|
3584
|
+
subscribe: (listener) => tabs.subscribe(listener),
|
|
3585
|
+
getSnapshot: () => tabs.guide()
|
|
3586
|
+
} } };
|
|
3587
|
+
const disposeGuide = ctx.slots.inject("sidebar.right.pane.tab", () => ctx.slots.register({
|
|
3588
|
+
name: "sidebar.right.pane.tab",
|
|
3589
|
+
key: GUIDE_ID,
|
|
3590
|
+
locale: NS,
|
|
3591
|
+
children: { "sidebar.right.tab.guide": {
|
|
3592
|
+
kind: "chain",
|
|
3593
|
+
scope: "session",
|
|
3594
|
+
inject: { hooks: { tabInfo: guideTabInfoFactory } }
|
|
3595
|
+
} },
|
|
3596
|
+
inject: () => guideInjected
|
|
3597
|
+
}, GuideBody));
|
|
3598
|
+
return () => {
|
|
3599
|
+
disposeGuide();
|
|
3600
|
+
disposeExpand();
|
|
3601
|
+
disposeSeat();
|
|
3602
|
+
for (const dispose of disposeTypes.reverse()) dispose();
|
|
3603
|
+
for (const release of adoptions) release();
|
|
3604
|
+
};
|
|
3605
|
+
}, "ui-sidebar-right: seats and shipped tab type");
|
|
3606
|
+
}
|
|
3607
|
+
//#endregion
|
|
3608
|
+
exports.apply = apply;
|
|
3609
|
+
exports.inject = inject;
|
|
3610
|
+
return module.exports;
|
|
3611
|
+
}
|
|
3612
|
+
});
|
|
3613
|
+
|
|
3614
|
+
//# sourceMappingURL=client.js.map
|