@harborclient/sdk 1.3.4 → 1.3.5
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/CHANGELOG.md +14 -0
- package/dist/components/Breadcrumb/SegmentShell.d.ts +12 -1
- package/dist/components/Breadcrumb/SegmentShell.d.ts.map +1 -1
- package/dist/components/Breadcrumb/SegmentShell.js +7 -5
- package/dist/components/CodeEditor/index.d.ts +7 -4
- package/dist/components/CodeEditor/index.d.ts.map +1 -1
- package/dist/components/CodeEditor/index.js +15 -176
- package/dist/components/CodeEditor/selectionActionToolbar.d.ts +162 -0
- package/dist/components/CodeEditor/selectionActionToolbar.d.ts.map +1 -0
- package/dist/components/CodeEditor/selectionActionToolbar.js +319 -0
- package/dist/components/CopyToChatButton/constants.d.ts +18 -0
- package/dist/components/CopyToChatButton/constants.d.ts.map +1 -0
- package/dist/components/CopyToChatButton/constants.js +19 -0
- package/dist/components/CopyToChatButton/index.d.ts +49 -0
- package/dist/components/CopyToChatButton/index.d.ts.map +1 -0
- package/dist/components/CopyToChatButton/index.js +43 -0
- package/dist/components/FloatingDialog/floatingDialogPosition.d.ts +52 -0
- package/dist/components/FloatingDialog/floatingDialogPosition.d.ts.map +1 -0
- package/dist/components/FloatingDialog/floatingDialogPosition.js +33 -0
- package/dist/components/FloatingDialog/index.d.ts +95 -0
- package/dist/components/FloatingDialog/index.d.ts.map +1 -0
- package/dist/components/FloatingDialog/index.js +275 -0
- package/dist/components/PageHeader/index.d.ts +2 -1
- package/dist/components/PageHeader/index.d.ts.map +1 -1
- package/dist/components/PageHeader/index.js +3 -2
- package/dist/components/SidebarItem/SidebarEnvironmentItem.d.ts +42 -4
- package/dist/components/SidebarItem/SidebarEnvironmentItem.d.ts.map +1 -1
- package/dist/components/SidebarItem/SidebarEnvironmentItem.js +34 -5
- package/dist/components/SidebarItem/SidebarWorkspaceItem.d.ts +9 -1
- package/dist/components/SidebarItem/SidebarWorkspaceItem.d.ts.map +1 -1
- package/dist/components/SidebarItem/SidebarWorkspaceItem.js +15 -2
- package/dist/components/TabBar/TabContextMenu.d.ts +3 -0
- package/dist/components/TabBar/TabContextMenu.d.ts.map +1 -1
- package/dist/components/TabBar/TabContextMenu.js +25 -16
- package/dist/components/VariableTable/index.d.ts +1 -1
- package/dist/components/VariableTable/index.d.ts.map +1 -1
- package/dist/components/VariableTable/index.js +14 -3
- package/dist/components/index.d.ts +4 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +3 -1
- package/dist/runtime/createBridgedPluginContext.js +74 -0
- package/dist/snippets.d.ts +26 -2
- package/dist/styles.css +16 -0
- package/dist/types.d.ts +273 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +1 -0
- package/package.json +1 -1
- package/dist/components/SelectionActionToolbar/index.d.ts +0 -41
- package/dist/components/SelectionActionToolbar/index.d.ts.map +0 -1
- package/dist/components/SelectionActionToolbar/index.js +0 -13
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import { EditorView, ViewPlugin } from '@codemirror/view';
|
|
2
|
+
/** Delay before showing the selection action toolbar after selection settles. */
|
|
3
|
+
export const SELECTION_TOOLBAR_SHOW_DELAY_MS = 450;
|
|
4
|
+
/**
|
|
5
|
+
* Computes viewport anchor coordinates for the selection action toolbar.
|
|
6
|
+
*
|
|
7
|
+
* @param view - CodeMirror editor view (or test double).
|
|
8
|
+
* @param selectionFrom - Start offset (inclusive) of the selection.
|
|
9
|
+
* @param selectionTo - End offset (exclusive) of the selection.
|
|
10
|
+
* @returns Toolbar anchor position, or null when coords are unavailable.
|
|
11
|
+
*/
|
|
12
|
+
export function computeSelectionActionToolbarCoords(view, selectionFrom, selectionTo) {
|
|
13
|
+
const startCoords = view.coordsAtPos(selectionFrom);
|
|
14
|
+
if (!startCoords) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
const endCoords = view.coordsAtPos(selectionTo);
|
|
18
|
+
const startCenter = startCoords.left + (startCoords.right - startCoords.left) / 2;
|
|
19
|
+
const endCenter = endCoords
|
|
20
|
+
? endCoords.left + (endCoords.right - endCoords.left) / 2
|
|
21
|
+
: startCenter;
|
|
22
|
+
return {
|
|
23
|
+
top: startCoords.top,
|
|
24
|
+
left: (startCenter + endCenter) / 2
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Builds a toolbar snapshot from the current editor selection when it is usable.
|
|
29
|
+
*
|
|
30
|
+
* @param view - CodeMirror editor view (or test double).
|
|
31
|
+
* @returns Toolbar state, or null when the selection is empty, whitespace-only, or coords fail.
|
|
32
|
+
*/
|
|
33
|
+
export function buildSelectionActionToolbarState(view) {
|
|
34
|
+
const { from, to } = view.state.selection.main;
|
|
35
|
+
if (from === to) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
const selectionFrom = Math.min(from, to);
|
|
39
|
+
const selectionTo = Math.max(from, to);
|
|
40
|
+
const text = view.state.sliceDoc(selectionFrom, selectionTo);
|
|
41
|
+
if (!text.trim()) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
const coords = computeSelectionActionToolbarCoords(view, selectionFrom, selectionTo);
|
|
45
|
+
if (!coords) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
top: coords.top,
|
|
50
|
+
left: coords.left,
|
|
51
|
+
text,
|
|
52
|
+
from: selectionFrom,
|
|
53
|
+
to: selectionTo
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Creates a fresh controller used by the selection toolbar extensions and unit tests.
|
|
58
|
+
*
|
|
59
|
+
* @returns Mutable controller state for pointer-drag and debounce bookkeeping.
|
|
60
|
+
*/
|
|
61
|
+
export function createSelectionActionToolbarController() {
|
|
62
|
+
return {
|
|
63
|
+
showTimer: undefined,
|
|
64
|
+
isPointerSelecting: false,
|
|
65
|
+
isToolbarOpen: false,
|
|
66
|
+
pendingState: null,
|
|
67
|
+
lastNonEmptySelection: null,
|
|
68
|
+
suppressCollapseDismiss: false
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Applies a view update to the selection toolbar controller.
|
|
73
|
+
*
|
|
74
|
+
* Encodes the RTL-gutter fix: collapsed selections during a pointer drag (or
|
|
75
|
+
* immediately after pointerup) do not dismiss a previously captured selection.
|
|
76
|
+
*
|
|
77
|
+
* @param controller - Shared toolbar controller.
|
|
78
|
+
* @param update - CodeMirror view update.
|
|
79
|
+
* @param options - Enable/open callbacks and notify/dismiss helpers.
|
|
80
|
+
* @returns Whether the toolbar should remain open after this update (for tests).
|
|
81
|
+
*/
|
|
82
|
+
export function applySelectionActionToolbarUpdate(controller, update, options) {
|
|
83
|
+
if (!options.isEnabled()) {
|
|
84
|
+
options.dismissToolbar();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (!update.selectionSet && !update.docChanged && !update.viewportChanged) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const { from, to } = update.state.selection.main;
|
|
91
|
+
if (from === to) {
|
|
92
|
+
// During drag or immediately after pointer finalize, CodeMirror may collapse
|
|
93
|
+
// into the gutter; keep the last non-empty selection for finalize.
|
|
94
|
+
if (controller.isPointerSelecting || controller.suppressCollapseDismiss) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
options.dismissToolbar();
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const nextState = buildSelectionActionToolbarState(update.view);
|
|
101
|
+
if (!nextState) {
|
|
102
|
+
if (controller.isPointerSelecting || controller.suppressCollapseDismiss) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
options.dismissToolbar();
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (controller.isPointerSelecting) {
|
|
109
|
+
controller.lastNonEmptySelection = nextState;
|
|
110
|
+
}
|
|
111
|
+
if (options.isOpen() && !update.selectionSet && update.viewportChanged) {
|
|
112
|
+
options.showImmediately(nextState);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (options.isOpen() && update.selectionSet) {
|
|
116
|
+
options.scheduleShow(nextState);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
options.scheduleShow(nextState);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Finalizes the toolbar after a primary-button pointer drag ends.
|
|
123
|
+
*
|
|
124
|
+
* Prefers the live selection; falls back to {@link SelectionActionToolbarController.lastNonEmptySelection}
|
|
125
|
+
* so RTL releases into the gutter still reveal Copy to chat.
|
|
126
|
+
*
|
|
127
|
+
* @param controller - Shared toolbar controller.
|
|
128
|
+
* @param view - CodeMirror editor view at pointerup.
|
|
129
|
+
* @param options - Open check and show helpers.
|
|
130
|
+
*/
|
|
131
|
+
export function finalizeSelectionActionToolbarOnPointerUp(controller, view, options) {
|
|
132
|
+
if (!controller.isPointerSelecting) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
controller.isPointerSelecting = false;
|
|
136
|
+
const liveSnapshot = buildSelectionActionToolbarState(view);
|
|
137
|
+
const snapshot = liveSnapshot ?? controller.lastNonEmptySelection;
|
|
138
|
+
controller.lastNonEmptySelection = null;
|
|
139
|
+
if (snapshot) {
|
|
140
|
+
controller.suppressCollapseDismiss = true;
|
|
141
|
+
options.showImmediately(snapshot);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (controller.pendingState != null && !options.isOpen()) {
|
|
145
|
+
options.scheduleShow(controller.pendingState);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Builds update and pointer handlers that debounce toolbar display until selection settles.
|
|
150
|
+
*
|
|
151
|
+
* @param onToolbarChange - React setter for toolbar visibility and position.
|
|
152
|
+
* @param isEnabled - Whether selection actions are currently configured.
|
|
153
|
+
* @param isOpen - Whether the toolbar is currently visible in React state.
|
|
154
|
+
* @param onDismiss - Extra host cleanup when Escape dismisses the toolbar.
|
|
155
|
+
* @returns CodeMirror extensions that drive the floating selection toolbar.
|
|
156
|
+
*/
|
|
157
|
+
export function createSelectionActionToolbarExtensions(onToolbarChange, isEnabled, isOpen, onDismiss) {
|
|
158
|
+
const controller = createSelectionActionToolbarController();
|
|
159
|
+
let activeView = null;
|
|
160
|
+
/**
|
|
161
|
+
* Clears a scheduled toolbar reveal without changing open state.
|
|
162
|
+
*/
|
|
163
|
+
const clearShowTimer = () => {
|
|
164
|
+
if (controller.showTimer) {
|
|
165
|
+
clearTimeout(controller.showTimer);
|
|
166
|
+
controller.showTimer = undefined;
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
/**
|
|
170
|
+
* Notifies React of toolbar visibility while keeping controller state in sync.
|
|
171
|
+
*
|
|
172
|
+
* @param state - Next toolbar snapshot, or null to hide.
|
|
173
|
+
*/
|
|
174
|
+
const notifyToolbarChange = (state) => {
|
|
175
|
+
controller.isToolbarOpen = state != null;
|
|
176
|
+
if (state == null) {
|
|
177
|
+
controller.pendingState = null;
|
|
178
|
+
}
|
|
179
|
+
onToolbarChange(state);
|
|
180
|
+
};
|
|
181
|
+
/**
|
|
182
|
+
* Hides the toolbar immediately and cancels any pending reveal.
|
|
183
|
+
*
|
|
184
|
+
* Does not clear {@link SelectionActionToolbarController.lastNonEmptySelection}
|
|
185
|
+
* so a later pointerup can still recover an RTL gutter collapse.
|
|
186
|
+
*/
|
|
187
|
+
const dismissToolbar = () => {
|
|
188
|
+
clearShowTimer();
|
|
189
|
+
if (controller.isToolbarOpen || controller.pendingState != null) {
|
|
190
|
+
notifyToolbarChange(null);
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
/**
|
|
194
|
+
* Schedules toolbar display after the configured settle delay.
|
|
195
|
+
*
|
|
196
|
+
* @param state - Snapshot to show once the settle timer fires.
|
|
197
|
+
*/
|
|
198
|
+
const scheduleShow = (state) => {
|
|
199
|
+
controller.pendingState = state;
|
|
200
|
+
clearShowTimer();
|
|
201
|
+
if (controller.isPointerSelecting) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
controller.showTimer = setTimeout(() => {
|
|
205
|
+
controller.showTimer = undefined;
|
|
206
|
+
if (controller.isPointerSelecting) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
notifyToolbarChange(state);
|
|
210
|
+
}, SELECTION_TOOLBAR_SHOW_DELAY_MS);
|
|
211
|
+
};
|
|
212
|
+
/**
|
|
213
|
+
* Repositions or reveals the toolbar without waiting when it is already open
|
|
214
|
+
* or when pointerup finalizes a drag selection.
|
|
215
|
+
*
|
|
216
|
+
* @param state - Snapshot to show immediately.
|
|
217
|
+
*/
|
|
218
|
+
const showImmediately = (state) => {
|
|
219
|
+
clearShowTimer();
|
|
220
|
+
controller.pendingState = state;
|
|
221
|
+
notifyToolbarChange(state);
|
|
222
|
+
};
|
|
223
|
+
/**
|
|
224
|
+
* Finalizes an in-progress pointer drag using the last known editor view.
|
|
225
|
+
*/
|
|
226
|
+
const finalizePointerSelection = () => {
|
|
227
|
+
if (activeView == null) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
finalizeSelectionActionToolbarOnPointerUp(controller, activeView, {
|
|
231
|
+
isOpen,
|
|
232
|
+
scheduleShow,
|
|
233
|
+
showImmediately
|
|
234
|
+
});
|
|
235
|
+
};
|
|
236
|
+
if (typeof window !== 'undefined') {
|
|
237
|
+
/**
|
|
238
|
+
* Finalizes drag-select when pointerup lands in the gutter, which bypasses
|
|
239
|
+
* CodeMirror domEventHandlers.
|
|
240
|
+
*/
|
|
241
|
+
const handleWindowPointerUp = () => {
|
|
242
|
+
if (!controller.isPointerSelecting) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
finalizePointerSelection();
|
|
246
|
+
};
|
|
247
|
+
window.addEventListener('pointerup', handleWindowPointerUp, true);
|
|
248
|
+
}
|
|
249
|
+
const updateListener = EditorView.updateListener.of((update) => {
|
|
250
|
+
applySelectionActionToolbarUpdate(controller, {
|
|
251
|
+
selectionSet: update.selectionSet,
|
|
252
|
+
docChanged: update.docChanged,
|
|
253
|
+
viewportChanged: update.viewportChanged,
|
|
254
|
+
view: update.view,
|
|
255
|
+
state: update.state
|
|
256
|
+
}, {
|
|
257
|
+
isEnabled,
|
|
258
|
+
isOpen,
|
|
259
|
+
notifyToolbarChange,
|
|
260
|
+
clearShowTimer,
|
|
261
|
+
scheduleShow,
|
|
262
|
+
showImmediately,
|
|
263
|
+
dismissToolbar
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
const pointerGuard = EditorView.domEventHandlers({
|
|
267
|
+
/**
|
|
268
|
+
* Suppresses toolbar reveal while the user is drag-selecting with the primary button.
|
|
269
|
+
*
|
|
270
|
+
* @param event - Native pointerdown on the editor.
|
|
271
|
+
*/
|
|
272
|
+
pointerdown(event, view) {
|
|
273
|
+
if (event.button === 0) {
|
|
274
|
+
activeView = view;
|
|
275
|
+
controller.suppressCollapseDismiss = false;
|
|
276
|
+
controller.isPointerSelecting = true;
|
|
277
|
+
controller.lastNonEmptySelection = null;
|
|
278
|
+
clearShowTimer();
|
|
279
|
+
}
|
|
280
|
+
return false;
|
|
281
|
+
},
|
|
282
|
+
/**
|
|
283
|
+
* Finalizes the toolbar after drag-select completes, recovering gutter collapses.
|
|
284
|
+
*
|
|
285
|
+
* @param _event - Native pointerup on the editor.
|
|
286
|
+
* @param view - CodeMirror view at release time.
|
|
287
|
+
*/
|
|
288
|
+
pointerup(_event, view) {
|
|
289
|
+
activeView = view;
|
|
290
|
+
finalizePointerSelection();
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
const dismissHandler = EditorView.domEventHandlers({
|
|
295
|
+
/**
|
|
296
|
+
* Dismisses the open toolbar when Escape is pressed.
|
|
297
|
+
*
|
|
298
|
+
* @param event - Native keydown on the editor.
|
|
299
|
+
*/
|
|
300
|
+
keydown(event) {
|
|
301
|
+
if (event.key === 'Escape' && isOpen()) {
|
|
302
|
+
event.preventDefault();
|
|
303
|
+
dismissToolbar();
|
|
304
|
+
onDismiss();
|
|
305
|
+
return true;
|
|
306
|
+
}
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
const cleanupPlugin = ViewPlugin.fromClass(class {
|
|
311
|
+
/**
|
|
312
|
+
* Clears any pending toolbar reveal when the editor extension is destroyed.
|
|
313
|
+
*/
|
|
314
|
+
destroy() {
|
|
315
|
+
clearShowTimer();
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
return [updateListener, pointerGuard, dismissHandler, cleanupPlugin];
|
|
319
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { IconDefinition } from '@fortawesome/fontawesome-svg-core';
|
|
2
|
+
/** Visible button and menu label for Copy to chat. */
|
|
3
|
+
export declare const COPY_TO_CHAT_LABEL = "Copy to chat";
|
|
4
|
+
/** Display text for the copy-to-chat keyboard shortcut shown next to the label. */
|
|
5
|
+
export declare const COPY_TO_CHAT_SHORTCUT_HINT = "Ctrl+Shift+O";
|
|
6
|
+
/** CodeMirror keymap binding for the copy-to-chat selection action. */
|
|
7
|
+
export declare const COPY_TO_CHAT_SHORTCUT_CODEMIRROR_KEY = "Ctrl-Shift-o";
|
|
8
|
+
/** Lowercase letter key used by custom key handlers for copy-to-chat. */
|
|
9
|
+
export declare const COPY_TO_CHAT_SHORTCUT_LETTER = "o";
|
|
10
|
+
/** Icon shown on Copy to chat buttons. */
|
|
11
|
+
export declare const COPY_TO_CHAT_ICON: IconDefinition;
|
|
12
|
+
/**
|
|
13
|
+
* Builds the lint-tooltip / diagnostic action name that includes the shortcut hint.
|
|
14
|
+
*
|
|
15
|
+
* @returns Label such as `Copy to chat (Ctrl+Shift+O)`.
|
|
16
|
+
*/
|
|
17
|
+
export declare function copyToChatActionLabel(): string;
|
|
18
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/components/CopyToChatButton/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAGxE,sDAAsD;AACtD,eAAO,MAAM,kBAAkB,iBAAiB,CAAC;AAEjD,mFAAmF;AACnF,eAAO,MAAM,0BAA0B,iBAAiB,CAAC;AAEzD,uEAAuE;AACvE,eAAO,MAAM,oCAAoC,iBAAiB,CAAC;AAEnE,yEAAyE;AACzE,eAAO,MAAM,4BAA4B,MAAM,CAAC;AAEhD,0CAA0C;AAC1C,eAAO,MAAM,iBAAiB,EAAE,cAAoC,CAAC;AAErE;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { faWandMagicSparkles } from '@fortawesome/free-solid-svg-icons';
|
|
2
|
+
/** Visible button and menu label for Copy to chat. */
|
|
3
|
+
export const COPY_TO_CHAT_LABEL = 'Copy to chat';
|
|
4
|
+
/** Display text for the copy-to-chat keyboard shortcut shown next to the label. */
|
|
5
|
+
export const COPY_TO_CHAT_SHORTCUT_HINT = 'Ctrl+Shift+O';
|
|
6
|
+
/** CodeMirror keymap binding for the copy-to-chat selection action. */
|
|
7
|
+
export const COPY_TO_CHAT_SHORTCUT_CODEMIRROR_KEY = 'Ctrl-Shift-o';
|
|
8
|
+
/** Lowercase letter key used by custom key handlers for copy-to-chat. */
|
|
9
|
+
export const COPY_TO_CHAT_SHORTCUT_LETTER = 'o';
|
|
10
|
+
/** Icon shown on Copy to chat buttons. */
|
|
11
|
+
export const COPY_TO_CHAT_ICON = faWandMagicSparkles;
|
|
12
|
+
/**
|
|
13
|
+
* Builds the lint-tooltip / diagnostic action name that includes the shortcut hint.
|
|
14
|
+
*
|
|
15
|
+
* @returns Label such as `Copy to chat (Ctrl+Shift+O)`.
|
|
16
|
+
*/
|
|
17
|
+
export function copyToChatActionLabel() {
|
|
18
|
+
return `${COPY_TO_CHAT_LABEL} (${COPY_TO_CHAT_SHORTCUT_HINT})`;
|
|
19
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { JSX, MouseEventHandler } from 'react';
|
|
2
|
+
export { COPY_TO_CHAT_ICON, COPY_TO_CHAT_LABEL, COPY_TO_CHAT_SHORTCUT_CODEMIRROR_KEY, COPY_TO_CHAT_SHORTCUT_HINT, COPY_TO_CHAT_SHORTCUT_LETTER, copyToChatActionLabel } from './constants.js';
|
|
3
|
+
export interface Props {
|
|
4
|
+
/**
|
|
5
|
+
* Invoked when the user activates the Copy to chat control.
|
|
6
|
+
*/
|
|
7
|
+
onSelect: () => void;
|
|
8
|
+
/**
|
|
9
|
+
* Accessible name announced for the action button.
|
|
10
|
+
*/
|
|
11
|
+
'aria-label': string;
|
|
12
|
+
/**
|
|
13
|
+
* Visual presentation of the control.
|
|
14
|
+
*
|
|
15
|
+
* - `labeled` — breadcrumb-style icon cap plus label and shortcut hint (default).
|
|
16
|
+
* - `icon` — icon-only button matching shared icon controls.
|
|
17
|
+
*/
|
|
18
|
+
appearance?: 'labeled' | 'icon';
|
|
19
|
+
/**
|
|
20
|
+
* When set, portals a fixed-position floating button at these viewport coordinates
|
|
21
|
+
* so overflow-hidden editor or terminal containers cannot clip it.
|
|
22
|
+
*/
|
|
23
|
+
coords?: {
|
|
24
|
+
top: number;
|
|
25
|
+
left: number;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Additional classes merged onto the button (layout tweaks only).
|
|
29
|
+
*/
|
|
30
|
+
className?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Optional native click handler for hosts that need stopPropagation.
|
|
33
|
+
* Defaults to calling {@link onSelect} only.
|
|
34
|
+
*/
|
|
35
|
+
onClick?: MouseEventHandler<HTMLButtonElement>;
|
|
36
|
+
/**
|
|
37
|
+
* Optional native title attribute (icon appearance often pairs this with aria-label).
|
|
38
|
+
*/
|
|
39
|
+
title?: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Shared Copy to chat control used for selection toolbars, floating overlays, and
|
|
43
|
+
* icon-only row/header actions so label, icon, shortcut, and styles stay in one place.
|
|
44
|
+
*
|
|
45
|
+
* The labeled appearance uses interlocking breadcrumb segments: a blue wand icon
|
|
46
|
+
* cap on the left and a dark label/shortcut segment on the right.
|
|
47
|
+
*/
|
|
48
|
+
export declare function CopyToChatButton({ onSelect, 'aria-label': ariaLabel, appearance, coords, className, onClick, title }: Props): JSX.Element;
|
|
49
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/CopyToChatButton/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAc,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAQhE,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,oCAAoC,EACpC,0BAA0B,EAC1B,4BAA4B,EAC5B,qBAAqB,EACtB,MAAM,gBAAgB,CAAC;AAMxB,MAAM,WAAW,KAAK;IACpB;;OAEG;IACH,QAAQ,EAAE,MAAM,IAAI,CAAC;IAErB;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IAEhC;;;OAGG;IACH,MAAM,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAEvC;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,OAAO,CAAC,EAAE,iBAAiB,CAAC,iBAAiB,CAAC,CAAC;IAE/C;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,YAAY,EAAE,SAAS,EACvB,UAAsB,EACtB,MAAM,EACN,SAAS,EACT,OAAO,EACP,KAAK,EACN,EAAE,KAAK,GAAG,GAAG,CAAC,OAAO,CAuErB"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@harborclient/sdk/jsx-runtime";
|
|
2
|
+
import { SegmentShell } from '../Breadcrumb/SegmentShell.js';
|
|
3
|
+
import { Button } from '../Button/index.js';
|
|
4
|
+
import { FaIcon } from '../FaIcon/index.js';
|
|
5
|
+
import { portalToBody } from '../portalToBody.js';
|
|
6
|
+
import { cn } from '../utils.js';
|
|
7
|
+
import { COPY_TO_CHAT_ICON, COPY_TO_CHAT_LABEL, COPY_TO_CHAT_SHORTCUT_HINT } from './constants.js';
|
|
8
|
+
export { COPY_TO_CHAT_ICON, COPY_TO_CHAT_LABEL, COPY_TO_CHAT_SHORTCUT_CODEMIRROR_KEY, COPY_TO_CHAT_SHORTCUT_HINT, COPY_TO_CHAT_SHORTCUT_LETTER, copyToChatActionLabel } from './constants.js';
|
|
9
|
+
/** Shared chrome for the labeled breadcrumb-style Copy to chat control. */
|
|
10
|
+
const LABELED_BUTTON_CLASS = 'hc-copy-to-chat-button hc-code-editor-selection-action app-no-drag inline-flex cursor-pointer items-stretch overflow-hidden rounded-lg border-none bg-breadcrumb-background p-0 text-[14px] text-text shadow-sm hover:brightness-110 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-accent';
|
|
11
|
+
/**
|
|
12
|
+
* Shared Copy to chat control used for selection toolbars, floating overlays, and
|
|
13
|
+
* icon-only row/header actions so label, icon, shortcut, and styles stay in one place.
|
|
14
|
+
*
|
|
15
|
+
* The labeled appearance uses interlocking breadcrumb segments: a blue wand icon
|
|
16
|
+
* cap on the left and a dark label/shortcut segment on the right.
|
|
17
|
+
*/
|
|
18
|
+
export function CopyToChatButton({ onSelect, 'aria-label': ariaLabel, appearance = 'labeled', coords, className, onClick, title }) {
|
|
19
|
+
/**
|
|
20
|
+
* Runs optional host click side effects (for example stopPropagation), then
|
|
21
|
+
* invokes {@link onSelect}.
|
|
22
|
+
*
|
|
23
|
+
* @param event - Native click from the button.
|
|
24
|
+
*/
|
|
25
|
+
const handleClick = (event) => {
|
|
26
|
+
onClick?.(event);
|
|
27
|
+
onSelect();
|
|
28
|
+
};
|
|
29
|
+
if (appearance === 'icon') {
|
|
30
|
+
const iconButton = (_jsx(Button, { type: "button", variant: "icon", "aria-label": ariaLabel, title: title, className: cn('hc-copy-to-chat-button', className), onClick: handleClick, children: _jsx(FaIcon, { icon: COPY_TO_CHAT_ICON, className: "h-3.5 w-3.5", "aria-hidden": true }) }));
|
|
31
|
+
if (coords == null) {
|
|
32
|
+
return iconButton;
|
|
33
|
+
}
|
|
34
|
+
return portalToBody(_jsx("div", { className: "pointer-events-auto fixed z-[70]", style: { top: coords.top, left: coords.left }, children: iconButton }));
|
|
35
|
+
}
|
|
36
|
+
const labeledButton = (_jsxs("button", { type: "button", className: cn(LABELED_BUTTON_CLASS, coords != null && 'pointer-events-auto fixed z-[70]', className), style: coords != null ? { top: coords.top, left: coords.left } : undefined, "aria-label": ariaLabel, title: title, onMouseDown: (event) => {
|
|
37
|
+
event.preventDefault();
|
|
38
|
+
}, onClick: handleClick, children: [_jsx(SegmentShell, { shape: "first", tone: "selection", density: "compact", className: "shrink-0", children: _jsx(FaIcon, { icon: COPY_TO_CHAT_ICON, className: "h-3.5 w-3.5 shrink-0", "aria-hidden": true }) }), _jsxs(SegmentShell, { shape: "last", tone: "current", density: "compact", className: "shrink-0", children: [_jsx("span", { children: COPY_TO_CHAT_LABEL }), _jsx("span", { className: "text-[14px] text-muted", children: COPY_TO_CHAT_SHORTCUT_HINT })] })] }));
|
|
39
|
+
if (coords == null) {
|
|
40
|
+
return labeledButton;
|
|
41
|
+
}
|
|
42
|
+
return portalToBody(labeledButton);
|
|
43
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimum gap between a floating dialog and the viewport edge.
|
|
3
|
+
*/
|
|
4
|
+
export declare const FLOATING_DIALOG_VIEWPORT_MARGIN_PX = 8;
|
|
5
|
+
/**
|
|
6
|
+
* Default corner left offset used when a dialog must be relocated on-screen.
|
|
7
|
+
*/
|
|
8
|
+
export declare const FLOATING_DIALOG_DEFAULT_LEFT = 96;
|
|
9
|
+
/**
|
|
10
|
+
* Default corner top offset used when a dialog must be relocated on-screen.
|
|
11
|
+
*/
|
|
12
|
+
export declare const FLOATING_DIALOG_DEFAULT_TOP = 96;
|
|
13
|
+
/**
|
|
14
|
+
* Top-left coordinates for a fixed floating dialog.
|
|
15
|
+
*/
|
|
16
|
+
export interface FloatingDialogPosition {
|
|
17
|
+
/**
|
|
18
|
+
* CSS `left` in viewport pixels.
|
|
19
|
+
*/
|
|
20
|
+
left: number;
|
|
21
|
+
/**
|
|
22
|
+
* CSS `top` in viewport pixels.
|
|
23
|
+
*/
|
|
24
|
+
top: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Measured floating dialog dimensions.
|
|
28
|
+
*/
|
|
29
|
+
export interface FloatingDialogSize {
|
|
30
|
+
/**
|
|
31
|
+
* Panel width in pixels.
|
|
32
|
+
*/
|
|
33
|
+
width: number;
|
|
34
|
+
/**
|
|
35
|
+
* Panel height in pixels.
|
|
36
|
+
*/
|
|
37
|
+
height: number;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Returns whether a floating dialog at `position` with `size` fits fully inside
|
|
41
|
+
* the viewport, respecting the viewport margin.
|
|
42
|
+
*
|
|
43
|
+
* @param position - Proposed top-left coordinates.
|
|
44
|
+
* @param size - Measured panel width and height.
|
|
45
|
+
* @param viewport - Optional viewport size (defaults to `window` when available).
|
|
46
|
+
* @returns True when the panel is fully visible inside the margin.
|
|
47
|
+
*/
|
|
48
|
+
export declare function isFloatingDialogFullyOnScreen(position: FloatingDialogPosition, size: FloatingDialogSize, viewport?: {
|
|
49
|
+
width: number;
|
|
50
|
+
height: number;
|
|
51
|
+
}): boolean;
|
|
52
|
+
//# sourceMappingURL=floatingDialogPosition.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"floatingDialogPosition.d.ts","sourceRoot":"","sources":["../../../src/components/FloatingDialog/floatingDialogPosition.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,kCAAkC,IAAI,CAAC;AAEpD;;GAEG;AACH,eAAO,MAAM,4BAA4B,KAAK,CAAC;AAE/C;;GAEG;AACH,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAE9C;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;GAQG;AACH,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,sBAAsB,EAChC,IAAI,EAAE,kBAAkB,EACxB,QAAQ,CAAC,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC3C,OAAO,CAgBT"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimum gap between a floating dialog and the viewport edge.
|
|
3
|
+
*/
|
|
4
|
+
export const FLOATING_DIALOG_VIEWPORT_MARGIN_PX = 8;
|
|
5
|
+
/**
|
|
6
|
+
* Default corner left offset used when a dialog must be relocated on-screen.
|
|
7
|
+
*/
|
|
8
|
+
export const FLOATING_DIALOG_DEFAULT_LEFT = 96;
|
|
9
|
+
/**
|
|
10
|
+
* Default corner top offset used when a dialog must be relocated on-screen.
|
|
11
|
+
*/
|
|
12
|
+
export const FLOATING_DIALOG_DEFAULT_TOP = 96;
|
|
13
|
+
/**
|
|
14
|
+
* Returns whether a floating dialog at `position` with `size` fits fully inside
|
|
15
|
+
* the viewport, respecting the viewport margin.
|
|
16
|
+
*
|
|
17
|
+
* @param position - Proposed top-left coordinates.
|
|
18
|
+
* @param size - Measured panel width and height.
|
|
19
|
+
* @param viewport - Optional viewport size (defaults to `window` when available).
|
|
20
|
+
* @returns True when the panel is fully visible inside the margin.
|
|
21
|
+
*/
|
|
22
|
+
export function isFloatingDialogFullyOnScreen(position, size, viewport) {
|
|
23
|
+
const margin = FLOATING_DIALOG_VIEWPORT_MARGIN_PX;
|
|
24
|
+
const viewportWidth = viewport?.width ?? (typeof window !== 'undefined' ? window.innerWidth : 0);
|
|
25
|
+
const viewportHeight = viewport?.height ?? (typeof window !== 'undefined' ? window.innerHeight : 0);
|
|
26
|
+
if (size.width <= 0 || size.height <= 0 || viewportWidth <= 0 || viewportHeight <= 0) {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
return (position.left >= margin &&
|
|
30
|
+
position.top >= margin &&
|
|
31
|
+
position.left + size.width <= viewportWidth - margin &&
|
|
32
|
+
position.top + size.height <= viewportHeight - margin);
|
|
33
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { JSX, ReactNode } from 'react';
|
|
2
|
+
import { type FloatingDialogPosition, type FloatingDialogSize } from './floatingDialogPosition.js';
|
|
3
|
+
interface Props {
|
|
4
|
+
/**
|
|
5
|
+
* Accessible name for the dialog when no labelled heading id is provided.
|
|
6
|
+
*/
|
|
7
|
+
label: string;
|
|
8
|
+
/**
|
|
9
|
+
* Optional id of a visible heading that labels the dialog.
|
|
10
|
+
*/
|
|
11
|
+
labelledBy?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Called when Escape is pressed (parent decides discard / close).
|
|
14
|
+
*/
|
|
15
|
+
onClose: () => void;
|
|
16
|
+
/**
|
|
17
|
+
* When true, Escape does not call `onClose`.
|
|
18
|
+
*/
|
|
19
|
+
disableEscape?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Optional width/position classes for the floating panel.
|
|
22
|
+
*/
|
|
23
|
+
className?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Optional classes for the scrollable body region.
|
|
26
|
+
*/
|
|
27
|
+
bodyClassName?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Initial CSS `left` in pixels.
|
|
30
|
+
*/
|
|
31
|
+
initialLeft?: number;
|
|
32
|
+
/**
|
|
33
|
+
* Initial CSS `top` in pixels.
|
|
34
|
+
*/
|
|
35
|
+
initialTop?: number;
|
|
36
|
+
/**
|
|
37
|
+
* Called when the panel position changes after a drag ends or a corner reset.
|
|
38
|
+
*
|
|
39
|
+
* @param position - Final top-left coordinates in viewport pixels.
|
|
40
|
+
*/
|
|
41
|
+
onPositionChange?: (position: FloatingDialogPosition) => void;
|
|
42
|
+
/**
|
|
43
|
+
* When set with {@link initialHeight}, enables SE resize with explicit pixel size.
|
|
44
|
+
*/
|
|
45
|
+
initialWidth?: number;
|
|
46
|
+
/**
|
|
47
|
+
* When set with {@link initialWidth}, enables SE resize with explicit pixel size.
|
|
48
|
+
*/
|
|
49
|
+
initialHeight?: number;
|
|
50
|
+
/**
|
|
51
|
+
* Minimum panel width while resizing (defaults to 240).
|
|
52
|
+
*/
|
|
53
|
+
minWidth?: number;
|
|
54
|
+
/**
|
|
55
|
+
* Minimum panel height while resizing (defaults to 120).
|
|
56
|
+
*/
|
|
57
|
+
minHeight?: number;
|
|
58
|
+
/**
|
|
59
|
+
* Maximum panel width while resizing (defaults to viewport minus margins).
|
|
60
|
+
*/
|
|
61
|
+
maxWidth?: number;
|
|
62
|
+
/**
|
|
63
|
+
* Maximum panel height while resizing (defaults to viewport minus margins).
|
|
64
|
+
*/
|
|
65
|
+
maxHeight?: number;
|
|
66
|
+
/**
|
|
67
|
+
* Called when a resize gesture ends (or keyboard resize applies).
|
|
68
|
+
*
|
|
69
|
+
* @param size - Final width and height in viewport pixels.
|
|
70
|
+
*/
|
|
71
|
+
onSizeChange?: (size: FloatingDialogSize) => void;
|
|
72
|
+
/**
|
|
73
|
+
* Header content used as the drag handle.
|
|
74
|
+
*/
|
|
75
|
+
dragHandle: ReactNode;
|
|
76
|
+
/**
|
|
77
|
+
* Dialog body content.
|
|
78
|
+
*/
|
|
79
|
+
children: ReactNode;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Non-blocking, draggable dialog panel with no modal mask.
|
|
83
|
+
*
|
|
84
|
+
* Clicks pass through to the UI behind the panel. Drag by the handle region.
|
|
85
|
+
* When `initialWidth` and `initialHeight` are provided, a southeast resize
|
|
86
|
+
* handle appears (pointer + arrow-key accessible). Parents persist geometry via
|
|
87
|
+
* `onPositionChange` / `onSizeChange`. If the panel would open or remain
|
|
88
|
+
* off-screen after a window resize, it relocates to the default corner.
|
|
89
|
+
*
|
|
90
|
+
* @param props - Dialog labelling, geometry, and content props.
|
|
91
|
+
* @returns Floating dialog element.
|
|
92
|
+
*/
|
|
93
|
+
export declare function FloatingDialog({ label, labelledBy, onClose, disableEscape, className, bodyClassName, initialLeft, initialTop, onPositionChange, initialWidth, initialHeight, minWidth, minHeight, maxWidth, maxHeight, onSizeChange, dragHandle, children }: Props): JSX.Element;
|
|
94
|
+
export {};
|
|
95
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/FloatingDialog/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAqD,SAAS,EAAE,MAAM,OAAO,CAAC;AAE/F,OAAO,EAIL,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EAExB,MAAM,6BAA6B,CAAC;AAErC,UAAU,KAAK;IACb;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,OAAO,EAAE,MAAM,IAAI,CAAC;IAEpB;;OAEG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,sBAAsB,KAAK,IAAI,CAAC;IAE9D;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;;OAIG;IACH,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAElD;;OAEG;IACH,UAAU,EAAE,SAAS,CAAC;IAEtB;;OAEG;IACH,QAAQ,EAAE,SAAS,CAAC;CACrB;AAuCD;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,UAAU,EACV,OAAO,EACP,aAAqB,EACrB,SAAS,EACT,aAAa,EACb,WAA0C,EAC1C,UAAwC,EACxC,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,QAAc,EACd,SAAe,EACf,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,UAAU,EACV,QAAQ,EACT,EAAE,KAAK,GAAG,GAAG,CAAC,OAAO,CA+VrB"}
|