@nextclaw/ui 0.13.5 → 0.13.7
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 +70 -0
- package/dist/assets/api-BCDb13Uv.js +8 -0
- package/dist/assets/app-presenter-provider-C-QMjK7m.js +3 -0
- package/dist/assets/arrow-left-rIXFA2Zy.js +1 -0
- package/dist/assets/{channels-list-page-BnGxv6ma.js → channels-list-page-DnqwsKGe.js} +6 -6
- package/dist/assets/chat-page-CsQx20xL.js +105 -0
- package/dist/assets/{config-split-page-CWN2YG9U.js → config-split-page-BwhGqmUS.js} +1 -1
- package/dist/assets/confirm-dialog-DBkKcQ1q.js +5 -0
- package/dist/assets/{desktop-update-config-abmYzHSy.js → desktop-update-config-BE9KjiXi.js} +1 -1
- package/dist/assets/dist-BI0bX8jv.js +1 -0
- package/dist/assets/dist-C8JRt-tf.js +1 -0
- package/dist/assets/{doc-browser-context-Cb1sEUT0.js → doc-browser-context-VHUOIUXL.js} +1 -1
- package/dist/assets/doc-browser-eqdNEerh.js +1 -0
- package/dist/assets/doc-browser-lpF_pyx4.js +1 -0
- package/dist/assets/ellipsis-C9VB2CxT.js +1 -0
- package/dist/assets/es2015-BCw3Q7nY.js +41 -0
- package/dist/assets/external-link-Bp7_4s9t.js +1 -0
- package/dist/assets/index-BCq7kbC8.css +1 -0
- package/dist/assets/{index-Ccbswzt9.js → index-DTdPDqto.js} +12 -12
- package/dist/assets/key-round-Ccy6oND3.js +1 -0
- package/dist/assets/loader-circle-OpH4EErP.js +1 -0
- package/dist/assets/{mcp-marketplace-page-CmUKUeno.js → mcp-marketplace-page-B-GxKOqS.js} +2 -2
- package/dist/assets/mcp-marketplace-page-Dm-0-w51.js +1 -0
- package/dist/assets/{model-config-Dbo3zWDl.js → model-config-BXPnV_7n.js} +1 -1
- package/dist/assets/{notice-card-CFTFtJky.js → notice-card-Dsbkl7u7.js} +1 -1
- package/dist/assets/play-CvgYhNbo.js +1 -0
- package/dist/assets/plus-DHcP-K1U.js +1 -0
- package/dist/assets/popover-BTLZE5Iz.js +1 -0
- package/dist/assets/provider-scoped-model-input-DKI9KN7N.js +1 -0
- package/dist/assets/providers-list-Bko6B7dE.js +1 -0
- package/dist/assets/react-BqS8oMyM.js +8 -0
- package/dist/assets/refresh-cw-BIaj7txb.js +1 -0
- package/dist/assets/remote-DHn_b8QU.js +1 -0
- package/dist/assets/rotate-cw-DMqSkRJQ.js +1 -0
- package/dist/assets/{runtime-config-page-Dg8aEgWx.js → runtime-config-page-DX_wSK1J.js} +1 -1
- package/dist/assets/save-BEoORten.js +1 -0
- package/dist/assets/search-COFh_SFI.js +1 -0
- package/dist/assets/{search-config-DAibVFGR.js → search-config-DA5By-tE.js} +1 -1
- package/dist/assets/{secrets-config-CoJzy13_.js → secrets-config-eDxG7iN4.js} +1 -1
- package/dist/assets/{status-dot-D8xx5ZR8.js → status-dot-oWBjTovo.js} +1 -1
- package/dist/assets/{tabs-DxmND60Q.js → tabs-CGaY0BVO.js} +1 -1
- package/dist/assets/{tabs-custom-Cjo0bHw8.js → tabs-custom-D2fPYb4G.js} +1 -1
- package/dist/assets/{tag-chip-DnNWFRMu.js → tag-chip-TVkgofS1.js} +1 -1
- package/dist/assets/tooltip-BwHPKBv9.js +1 -0
- package/dist/assets/trash-2-D3psAGrz.js +1 -0
- package/dist/assets/x-CHAxzyp2.js +1 -0
- package/dist/index.html +25 -24
- package/package.json +8 -8
- package/src/features/chat/components/chat-welcome.test.tsx +15 -0
- package/src/features/chat/components/chat-welcome.tsx +4 -7
- package/src/shared/components/doc-browser/doc-browser-tab-strip.tsx +180 -117
- package/src/shared/components/doc-browser/doc-browser.test.tsx +146 -63
- package/dist/assets/api-BAyyGaoz.js +0 -15
- package/dist/assets/app-presenter-provider-DqHHfC1K.js +0 -3
- package/dist/assets/arrow-left-Bn-3Pj3p.js +0 -1
- package/dist/assets/boxes-ClVuotcz.js +0 -1
- package/dist/assets/chat-page-DInyWLb4.js +0 -105
- package/dist/assets/confirm-dialog-CVbgKuHp.js +0 -5
- package/dist/assets/createLucideIcon-CjPUPAAK.js +0 -1
- package/dist/assets/dist-hkrb6Ynt.js +0 -41
- package/dist/assets/doc-browser-BlrpUKVJ.js +0 -1
- package/dist/assets/doc-browser-kJ2ClG0H.js +0 -1
- package/dist/assets/ellipsis-CdkxFkVZ.js +0 -1
- package/dist/assets/external-link-CT9_qFwN.js +0 -1
- package/dist/assets/index-BGXTR0F_.css +0 -1
- package/dist/assets/key-round-xJfR4iRe.js +0 -1
- package/dist/assets/loader-circle-BI6HJDg5.js +0 -1
- package/dist/assets/mcp-marketplace-page-DH11bSVx.js +0 -1
- package/dist/assets/play-DyBWW2j4.js +0 -1
- package/dist/assets/plus-BlnBmcKE.js +0 -1
- package/dist/assets/popover-DPqa_Mfb.js +0 -1
- package/dist/assets/provider-scoped-model-input-C6xL7zSJ.js +0 -1
- package/dist/assets/providers-list-DOj_7GL8.js +0 -1
- package/dist/assets/react-oAHNoVya.js +0 -1
- package/dist/assets/refresh-cw-o8NSmIYV.js +0 -1
- package/dist/assets/remote-fCE5fLsj.js +0 -1
- package/dist/assets/rotate-cw-DA8EqNAH.js +0 -1
- package/dist/assets/save-D4-g0EAq.js +0 -1
- package/dist/assets/search-G2cMn3M5.js +0 -1
- package/dist/assets/trash-2-C9tc-9sQ.js +0 -1
- package/dist/assets/x-DmLl2yG6.js +0 -1
- /package/dist/assets/{config-hints-TmNgpFHv.js → config-hints-Ceiol9x4.js} +0 -0
- /package/dist/assets/{provider-models-CXUgDxGR.js → provider-models-C_yOh6DE.js} +0 -0
|
@@ -1,8 +1,23 @@
|
|
|
1
|
-
import type { PointerEvent } from
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import type { ButtonHTMLAttributes, PointerEvent, ReactElement } from "react";
|
|
2
|
+
import {
|
|
3
|
+
ArrowLeft,
|
|
4
|
+
ArrowRight,
|
|
5
|
+
Maximize2,
|
|
6
|
+
PanelRightOpen,
|
|
7
|
+
Pin,
|
|
8
|
+
PinOff,
|
|
9
|
+
Plus,
|
|
10
|
+
X,
|
|
11
|
+
} from "lucide-react";
|
|
12
|
+
import type { DocBrowserDockState, DocBrowserTab } from "./doc-browser-context";
|
|
13
|
+
import { cn } from "@/shared/lib/utils";
|
|
14
|
+
import { t } from "@/shared/lib/i18n";
|
|
15
|
+
import {
|
|
16
|
+
Tooltip,
|
|
17
|
+
TooltipContent,
|
|
18
|
+
TooltipProvider,
|
|
19
|
+
TooltipTrigger,
|
|
20
|
+
} from "@/shared/components/ui/tooltip";
|
|
6
21
|
|
|
7
22
|
type DocBrowserTabStripProps = {
|
|
8
23
|
tabs: DocBrowserTab[];
|
|
@@ -23,6 +38,53 @@ type DocBrowserTabStripProps = {
|
|
|
23
38
|
onToggleMode: () => void;
|
|
24
39
|
};
|
|
25
40
|
|
|
41
|
+
type DocBrowserIconActionButtonProps = Omit<
|
|
42
|
+
ButtonHTMLAttributes<HTMLButtonElement>,
|
|
43
|
+
"children" | "type"
|
|
44
|
+
> & {
|
|
45
|
+
disabled?: boolean;
|
|
46
|
+
icon: ReactElement;
|
|
47
|
+
label: string;
|
|
48
|
+
variant?: "tab" | "toolbar";
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
function DocBrowserIconActionButton({
|
|
52
|
+
className,
|
|
53
|
+
disabled = false,
|
|
54
|
+
icon,
|
|
55
|
+
label,
|
|
56
|
+
variant = "toolbar",
|
|
57
|
+
...buttonProps
|
|
58
|
+
}: DocBrowserIconActionButtonProps) {
|
|
59
|
+
const button = (
|
|
60
|
+
<button
|
|
61
|
+
{...buttonProps}
|
|
62
|
+
type="button"
|
|
63
|
+
disabled={disabled}
|
|
64
|
+
className={cn(
|
|
65
|
+
variant === "toolbar"
|
|
66
|
+
? "rounded-md p-1.5 text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-700 disabled:cursor-not-allowed disabled:text-gray-300 disabled:opacity-60 disabled:hover:bg-transparent disabled:hover:text-gray-300"
|
|
67
|
+
: "rounded p-0.5 transition-colors hover:bg-black/10",
|
|
68
|
+
className,
|
|
69
|
+
)}
|
|
70
|
+
aria-label={label}
|
|
71
|
+
>
|
|
72
|
+
{icon}
|
|
73
|
+
</button>
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<Tooltip>
|
|
78
|
+
<TooltipTrigger asChild>
|
|
79
|
+
{disabled ? <span className="inline-flex">{button}</span> : button}
|
|
80
|
+
</TooltipTrigger>
|
|
81
|
+
<TooltipContent side="bottom" className="text-xs">
|
|
82
|
+
{label}
|
|
83
|
+
</TooltipContent>
|
|
84
|
+
</Tooltip>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
26
88
|
export function DocBrowserTabStrip({
|
|
27
89
|
tabs,
|
|
28
90
|
activeTabId,
|
|
@@ -41,123 +103,124 @@ export function DocBrowserTabStrip({
|
|
|
41
103
|
onDragStart,
|
|
42
104
|
onToggleMode,
|
|
43
105
|
}: DocBrowserTabStripProps) {
|
|
106
|
+
const backLabel = t("docBrowserBack");
|
|
107
|
+
const closeLabel = t("docBrowserClose");
|
|
108
|
+
const closeTabLabel = t("docBrowserCloseTab");
|
|
109
|
+
const dockLabel = dockState?.isDocked
|
|
110
|
+
? dockState.removable
|
|
111
|
+
? t("sideDockUnpinCurrent")
|
|
112
|
+
: t("sideDockBuiltInDocked")
|
|
113
|
+
: t("sideDockPinCurrent");
|
|
114
|
+
const forwardLabel = t("docBrowserForward");
|
|
115
|
+
const modeLabel = isDocked
|
|
116
|
+
? t("docBrowserFloatMode")
|
|
117
|
+
: t("docBrowserDockMode");
|
|
118
|
+
const newTabLabel = t("docBrowserNewTab");
|
|
119
|
+
|
|
44
120
|
return (
|
|
45
|
-
<
|
|
46
|
-
data-testid="doc-browser-tab-strip"
|
|
47
|
-
className={cn(
|
|
48
|
-
'flex h-11 items-stretch gap-2 px-2.5 bg-background border-b border-[#f1e7d4] shrink-0 select-none',
|
|
49
|
-
isFullscreen && 'h-[calc(env(safe-area-inset-top,0px)+2.75rem)] pt-[env(safe-area-inset-top,0px)]',
|
|
50
|
-
)}
|
|
51
|
-
onPointerDown={!isDocked && !isFullscreen ? onDragStart : undefined}
|
|
52
|
-
>
|
|
121
|
+
<TooltipProvider delayDuration={250}>
|
|
53
122
|
<div
|
|
54
|
-
|
|
55
|
-
|
|
123
|
+
data-testid="doc-browser-tab-strip"
|
|
124
|
+
className={cn(
|
|
125
|
+
"flex h-11 items-stretch gap-2 px-2.5 bg-background border-b border-[#f1e7d4] shrink-0 select-none",
|
|
126
|
+
isFullscreen &&
|
|
127
|
+
"h-[calc(env(safe-area-inset-top,0px)+2.75rem)] pt-[env(safe-area-inset-top,0px)]",
|
|
128
|
+
)}
|
|
129
|
+
onPointerDown={!isDocked && !isFullscreen ? onDragStart : undefined}
|
|
56
130
|
>
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
<div
|
|
61
|
-
key={tab.id}
|
|
62
|
-
className={cn(
|
|
63
|
-
'inline-flex items-center gap-1 h-7 px-1.5 rounded-lg text-xs border max-w-[220px] shrink-0 transition-colors',
|
|
64
|
-
isActive
|
|
65
|
-
? 'bg-amber-50/80 border-amber-200 text-amber-900 shadow-[0_1px_2px_rgba(30,20,10,0.04)]'
|
|
66
|
-
: 'bg-[#f9f8f5] border-[#eee3d1] text-[#78644d] hover:bg-[#fff7ea] hover:text-[#2f2212]',
|
|
67
|
-
)}
|
|
68
|
-
>
|
|
69
|
-
<button
|
|
70
|
-
type="button"
|
|
71
|
-
onClick={() => onSetActiveTab(tab.id)}
|
|
72
|
-
className="truncate text-left px-1"
|
|
73
|
-
title={tab.title}
|
|
74
|
-
>
|
|
75
|
-
{tab.title || t('docBrowserTabUntitled')}
|
|
76
|
-
</button>
|
|
77
|
-
<button
|
|
78
|
-
type="button"
|
|
79
|
-
onClick={(event) => {
|
|
80
|
-
event.stopPropagation();
|
|
81
|
-
onCloseTab(tab.id);
|
|
82
|
-
}}
|
|
83
|
-
className="rounded p-0.5 hover:bg-black/10"
|
|
84
|
-
aria-label={t('docBrowserCloseTab')}
|
|
85
|
-
>
|
|
86
|
-
<X className="w-3 h-3" />
|
|
87
|
-
</button>
|
|
88
|
-
</div>
|
|
89
|
-
);
|
|
90
|
-
})}
|
|
91
|
-
</div>
|
|
92
|
-
<div
|
|
93
|
-
className="flex h-full items-center gap-1 shrink-0"
|
|
94
|
-
data-testid="doc-browser-tab-actions"
|
|
95
|
-
onPointerDown={(event) => event.stopPropagation()}
|
|
96
|
-
>
|
|
97
|
-
<button
|
|
98
|
-
type="button"
|
|
99
|
-
onClick={onGoBack}
|
|
100
|
-
disabled={!canGoBack}
|
|
101
|
-
className="rounded-md p-1.5 text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-700 disabled:cursor-not-allowed disabled:text-gray-300 disabled:opacity-60 disabled:hover:bg-transparent disabled:hover:text-gray-300"
|
|
102
|
-
title={t('docBrowserBack')}
|
|
103
|
-
>
|
|
104
|
-
<ArrowLeft className="w-3.5 h-3.5" />
|
|
105
|
-
</button>
|
|
106
|
-
<button
|
|
107
|
-
type="button"
|
|
108
|
-
onClick={onGoForward}
|
|
109
|
-
disabled={!canGoForward}
|
|
110
|
-
className="rounded-md p-1.5 text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-700 disabled:cursor-not-allowed disabled:text-gray-300 disabled:opacity-60 disabled:hover:bg-transparent disabled:hover:text-gray-300"
|
|
111
|
-
title={t('docBrowserForward')}
|
|
131
|
+
<div
|
|
132
|
+
className="doc-browser-tab-scrollbar flex h-full min-w-0 flex-1 items-center gap-1.5 overflow-x-auto"
|
|
133
|
+
onPointerDown={(event) => event.stopPropagation()}
|
|
112
134
|
>
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
</button>
|
|
151
|
-
) : null}
|
|
152
|
-
<button
|
|
153
|
-
type="button"
|
|
154
|
-
onClick={onClose}
|
|
155
|
-
className="hover:bg-gray-100 rounded-md p-1.5 text-gray-500 hover:text-gray-700 transition-colors"
|
|
156
|
-
title={t('docBrowserClose')}
|
|
135
|
+
{tabs.map((tab) => {
|
|
136
|
+
const isActive = tab.id === activeTabId;
|
|
137
|
+
return (
|
|
138
|
+
<div
|
|
139
|
+
key={tab.id}
|
|
140
|
+
className={cn(
|
|
141
|
+
"inline-flex items-center gap-1 h-7 px-1.5 rounded-lg text-xs border max-w-[220px] shrink-0 transition-colors",
|
|
142
|
+
isActive
|
|
143
|
+
? "bg-amber-50/80 border-amber-200 text-amber-900 shadow-[0_1px_2px_rgba(30,20,10,0.04)]"
|
|
144
|
+
: "bg-[#f9f8f5] border-[#eee3d1] text-[#78644d] hover:bg-[#fff7ea] hover:text-[#2f2212]",
|
|
145
|
+
)}
|
|
146
|
+
>
|
|
147
|
+
<button
|
|
148
|
+
type="button"
|
|
149
|
+
onClick={() => onSetActiveTab(tab.id)}
|
|
150
|
+
className="truncate text-left px-1"
|
|
151
|
+
title={tab.title}
|
|
152
|
+
>
|
|
153
|
+
{tab.title || t("docBrowserTabUntitled")}
|
|
154
|
+
</button>
|
|
155
|
+
<DocBrowserIconActionButton
|
|
156
|
+
icon={<X className="w-3 h-3" />}
|
|
157
|
+
label={closeTabLabel}
|
|
158
|
+
variant="tab"
|
|
159
|
+
onClick={(event) => {
|
|
160
|
+
event.stopPropagation();
|
|
161
|
+
onCloseTab(tab.id);
|
|
162
|
+
}}
|
|
163
|
+
/>
|
|
164
|
+
</div>
|
|
165
|
+
);
|
|
166
|
+
})}
|
|
167
|
+
</div>
|
|
168
|
+
<div
|
|
169
|
+
className="flex h-full items-center gap-1 shrink-0"
|
|
170
|
+
data-testid="doc-browser-tab-actions"
|
|
171
|
+
onPointerDown={(event) => event.stopPropagation()}
|
|
157
172
|
>
|
|
158
|
-
<
|
|
159
|
-
|
|
173
|
+
<DocBrowserIconActionButton
|
|
174
|
+
disabled={!canGoBack}
|
|
175
|
+
icon={<ArrowLeft className="w-3.5 h-3.5" />}
|
|
176
|
+
label={backLabel}
|
|
177
|
+
onClick={onGoBack}
|
|
178
|
+
/>
|
|
179
|
+
<DocBrowserIconActionButton
|
|
180
|
+
disabled={!canGoForward}
|
|
181
|
+
icon={<ArrowRight className="w-3.5 h-3.5" />}
|
|
182
|
+
label={forwardLabel}
|
|
183
|
+
onClick={onGoForward}
|
|
184
|
+
/>
|
|
185
|
+
<DocBrowserIconActionButton
|
|
186
|
+
icon={<Plus className="w-3.5 h-3.5" />}
|
|
187
|
+
label={newTabLabel}
|
|
188
|
+
onClick={onOpenNewTab}
|
|
189
|
+
/>
|
|
190
|
+
{dockState?.canDock ? (
|
|
191
|
+
<DocBrowserIconActionButton
|
|
192
|
+
disabled={dockState.isDocked && !dockState.removable}
|
|
193
|
+
icon={
|
|
194
|
+
dockState.isDocked && dockState.removable ? (
|
|
195
|
+
<PinOff className="w-3.5 h-3.5" />
|
|
196
|
+
) : (
|
|
197
|
+
<Pin className="w-3.5 h-3.5" />
|
|
198
|
+
)
|
|
199
|
+
}
|
|
200
|
+
label={dockLabel}
|
|
201
|
+
onClick={onToggleDock}
|
|
202
|
+
/>
|
|
203
|
+
) : null}
|
|
204
|
+
{!isFullscreen ? (
|
|
205
|
+
<DocBrowserIconActionButton
|
|
206
|
+
icon={
|
|
207
|
+
isDocked ? (
|
|
208
|
+
<Maximize2 className="w-3.5 h-3.5" />
|
|
209
|
+
) : (
|
|
210
|
+
<PanelRightOpen className="w-3.5 h-3.5" />
|
|
211
|
+
)
|
|
212
|
+
}
|
|
213
|
+
label={modeLabel}
|
|
214
|
+
onClick={onToggleMode}
|
|
215
|
+
/>
|
|
216
|
+
) : null}
|
|
217
|
+
<DocBrowserIconActionButton
|
|
218
|
+
icon={<X className="w-3.5 h-3.5" />}
|
|
219
|
+
label={closeLabel}
|
|
220
|
+
onClick={onClose}
|
|
221
|
+
/>
|
|
222
|
+
</div>
|
|
160
223
|
</div>
|
|
161
|
-
</
|
|
224
|
+
</TooltipProvider>
|
|
162
225
|
);
|
|
163
226
|
}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { fireEvent, render, screen } from "@testing-library/react";
|
|
2
|
+
import userEvent from "@testing-library/user-event";
|
|
2
3
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
4
|
import { DocBrowser } from "@/shared/components/doc-browser/doc-browser";
|
|
4
|
-
import type {
|
|
5
|
+
import type {
|
|
6
|
+
DocBrowserContextValue,
|
|
7
|
+
DocBrowserTab,
|
|
8
|
+
} from "@/shared/components/doc-browser/doc-browser-context";
|
|
5
9
|
import { PANEL_APPS_DOC_BROWSER_RENDERERS } from "@/features/panel-apps";
|
|
6
10
|
|
|
7
11
|
const { navigateMock } = vi.hoisted(() => ({
|
|
@@ -9,11 +13,13 @@ const { navigateMock } = vi.hoisted(() => ({
|
|
|
9
13
|
}));
|
|
10
14
|
|
|
11
15
|
vi.mock("react-router-dom", async () => ({
|
|
12
|
-
...(await vi.importActual("react-router-dom") as object),
|
|
16
|
+
...((await vi.importActual("react-router-dom")) as object),
|
|
13
17
|
useNavigate: () => navigateMock,
|
|
14
18
|
}));
|
|
15
19
|
|
|
16
|
-
const { docBrowserState } = vi.hoisted<{
|
|
20
|
+
const { docBrowserState } = vi.hoisted<{
|
|
21
|
+
docBrowserState: DocBrowserContextValue;
|
|
22
|
+
}>(() => ({
|
|
17
23
|
docBrowserState: {
|
|
18
24
|
isOpen: true,
|
|
19
25
|
mode: "docked" as "docked" | "floating",
|
|
@@ -29,11 +35,13 @@ const { docBrowserState } = vi.hoisted<{ docBrowserState: DocBrowserContextValue
|
|
|
29
35
|
},
|
|
30
36
|
],
|
|
31
37
|
activeTabId: "docs",
|
|
32
|
-
activeHistory: [
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
38
|
+
activeHistory: [
|
|
39
|
+
{
|
|
40
|
+
kind: "docs" as const,
|
|
41
|
+
tabId: "docs",
|
|
42
|
+
url: "https://docs.nextclaw.io/en/guide/getting-started",
|
|
43
|
+
},
|
|
44
|
+
],
|
|
37
45
|
activeHistoryIndex: 0,
|
|
38
46
|
currentTab: {
|
|
39
47
|
id: "docs",
|
|
@@ -99,11 +107,13 @@ describe("DocBrowser", () => {
|
|
|
99
107
|
},
|
|
100
108
|
];
|
|
101
109
|
docBrowserState.activeTabId = "docs";
|
|
102
|
-
docBrowserState.activeHistory = [
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
110
|
+
docBrowserState.activeHistory = [
|
|
111
|
+
{
|
|
112
|
+
kind: "docs" as const,
|
|
113
|
+
tabId: "docs",
|
|
114
|
+
url: "https://docs.nextclaw.io/en/guide/getting-started",
|
|
115
|
+
},
|
|
116
|
+
];
|
|
107
117
|
docBrowserState.activeHistoryIndex = 0;
|
|
108
118
|
docBrowserState.currentTab = docBrowserState.tabs[0];
|
|
109
119
|
Object.defineProperty(window, "innerWidth", {
|
|
@@ -133,24 +143,41 @@ describe("DocBrowser", () => {
|
|
|
133
143
|
expect(panel.className).toContain("inset-0");
|
|
134
144
|
expect(panel.className).toContain("w-screen");
|
|
135
145
|
expect(
|
|
136
|
-
screen.
|
|
146
|
+
screen.queryByRole("button", { name: /float/i }) ??
|
|
147
|
+
screen.queryByRole("button", { name: /dock/i }),
|
|
137
148
|
).toBeNull();
|
|
138
149
|
expect(panel.querySelector(".cursor-ew-resize")).toBeNull();
|
|
139
150
|
expect(panel.querySelector(".cursor-se-resize")).toBeNull();
|
|
140
151
|
});
|
|
141
152
|
|
|
142
|
-
it("keeps browser window controls on the tab strip", () => {
|
|
143
|
-
|
|
153
|
+
it("keeps browser window controls on the tab strip", async () => {
|
|
154
|
+
const user = userEvent.setup();
|
|
155
|
+
render(
|
|
156
|
+
<DocBrowser customTabRenderers={PANEL_APPS_DOC_BROWSER_RENDERERS} />,
|
|
157
|
+
);
|
|
144
158
|
|
|
145
159
|
const tabStrip = screen.getByTestId("doc-browser-tab-strip");
|
|
146
160
|
const tabActions = screen.getByTestId("doc-browser-tab-actions");
|
|
161
|
+
const newTabButton = screen.getByRole("button", { name: "New Tab" });
|
|
147
162
|
|
|
148
|
-
expect(
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
expect(
|
|
152
|
-
|
|
163
|
+
expect(
|
|
164
|
+
tabActions.contains(screen.getByRole("button", { name: "Back" })),
|
|
165
|
+
).toBe(true);
|
|
166
|
+
expect(
|
|
167
|
+
tabActions.contains(screen.getByRole("button", { name: "Forward" })),
|
|
168
|
+
).toBe(true);
|
|
169
|
+
expect(
|
|
170
|
+
tabStrip.contains(screen.getByRole("button", { name: "Float Window" })),
|
|
171
|
+
).toBe(true);
|
|
172
|
+
expect(
|
|
173
|
+
tabStrip.contains(screen.getByRole("button", { name: "Close" })),
|
|
174
|
+
).toBe(true);
|
|
175
|
+
expect(tabActions.contains(newTabButton)).toBe(true);
|
|
153
176
|
expect(screen.queryByText("Embedded Browser")).toBeNull();
|
|
177
|
+
|
|
178
|
+
await user.hover(newTabButton);
|
|
179
|
+
|
|
180
|
+
expect((await screen.findAllByText("New Tab")).length).toBeGreaterThan(0);
|
|
154
181
|
});
|
|
155
182
|
|
|
156
183
|
it("uses active history to enable browser back and forward actions", () => {
|
|
@@ -178,10 +205,12 @@ describe("DocBrowser", () => {
|
|
|
178
205
|
docBrowserState.activeHistoryIndex = 1;
|
|
179
206
|
docBrowserState.currentTab = historyTab;
|
|
180
207
|
|
|
181
|
-
render(
|
|
208
|
+
render(
|
|
209
|
+
<DocBrowser customTabRenderers={PANEL_APPS_DOC_BROWSER_RENDERERS} />,
|
|
210
|
+
);
|
|
182
211
|
|
|
183
|
-
fireEvent.click(screen.
|
|
184
|
-
fireEvent.click(screen.
|
|
212
|
+
fireEvent.click(screen.getByRole("button", { name: "Back" }));
|
|
213
|
+
fireEvent.click(screen.getByRole("button", { name: "Forward" }));
|
|
185
214
|
|
|
186
215
|
expect(docBrowserState.goBack).toHaveBeenCalled();
|
|
187
216
|
expect(docBrowserState.goForward).toHaveBeenCalled();
|
|
@@ -212,8 +241,14 @@ describe("DocBrowser", () => {
|
|
|
212
241
|
|
|
213
242
|
const { rerender } = render(<DocBrowser />);
|
|
214
243
|
|
|
215
|
-
expect(screen.
|
|
216
|
-
|
|
244
|
+
expect(screen.getByRole("button", { name: "Back" })).toHaveProperty(
|
|
245
|
+
"disabled",
|
|
246
|
+
false,
|
|
247
|
+
);
|
|
248
|
+
expect(screen.getByRole("button", { name: "Forward" })).toHaveProperty(
|
|
249
|
+
"disabled",
|
|
250
|
+
true,
|
|
251
|
+
);
|
|
217
252
|
|
|
218
253
|
const oldestTab: DocBrowserTab = {
|
|
219
254
|
...latestTab,
|
|
@@ -225,14 +260,20 @@ describe("DocBrowser", () => {
|
|
|
225
260
|
docBrowserState.activeHistoryIndex = 0;
|
|
226
261
|
rerender(<DocBrowser />);
|
|
227
262
|
|
|
228
|
-
expect(screen.
|
|
229
|
-
|
|
263
|
+
expect(screen.getByRole("button", { name: "Back" })).toHaveProperty(
|
|
264
|
+
"disabled",
|
|
265
|
+
true,
|
|
266
|
+
);
|
|
267
|
+
expect(screen.getByRole("button", { name: "Forward" })).toHaveProperty(
|
|
268
|
+
"disabled",
|
|
269
|
+
false,
|
|
270
|
+
);
|
|
230
271
|
});
|
|
231
272
|
|
|
232
273
|
it("opens the start page from the fixed new tab action", () => {
|
|
233
274
|
render(<DocBrowser />);
|
|
234
275
|
|
|
235
|
-
fireEvent.click(screen.
|
|
276
|
+
fireEvent.click(screen.getByRole("button", { name: "New Tab" }));
|
|
236
277
|
|
|
237
278
|
expect(docBrowserState.openNewTab).toHaveBeenCalled();
|
|
238
279
|
});
|
|
@@ -241,13 +282,21 @@ describe("DocBrowser", () => {
|
|
|
241
282
|
const pinTab = vi.fn();
|
|
242
283
|
const unpinTab = vi.fn();
|
|
243
284
|
|
|
244
|
-
render(
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
285
|
+
render(
|
|
286
|
+
<DocBrowser
|
|
287
|
+
dockControls={{
|
|
288
|
+
getDockState: () => ({
|
|
289
|
+
canDock: true,
|
|
290
|
+
isDocked: false,
|
|
291
|
+
removable: false,
|
|
292
|
+
}),
|
|
293
|
+
pinTab,
|
|
294
|
+
unpinTab,
|
|
295
|
+
}}
|
|
296
|
+
/>,
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
fireEvent.click(screen.getByRole("button", { name: "Pin to SideDock" }));
|
|
251
300
|
|
|
252
301
|
expect(pinTab).toHaveBeenCalledWith(docBrowserState.currentTab);
|
|
253
302
|
expect(unpinTab).not.toHaveBeenCalled();
|
|
@@ -257,26 +306,46 @@ describe("DocBrowser", () => {
|
|
|
257
306
|
const pinTab = vi.fn();
|
|
258
307
|
const unpinTab = vi.fn();
|
|
259
308
|
|
|
260
|
-
render(
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
309
|
+
render(
|
|
310
|
+
<DocBrowser
|
|
311
|
+
dockControls={{
|
|
312
|
+
getDockState: () => ({
|
|
313
|
+
canDock: true,
|
|
314
|
+
isDocked: true,
|
|
315
|
+
removable: true,
|
|
316
|
+
}),
|
|
317
|
+
pinTab,
|
|
318
|
+
unpinTab,
|
|
319
|
+
}}
|
|
320
|
+
/>,
|
|
321
|
+
);
|
|
322
|
+
|
|
323
|
+
fireEvent.click(
|
|
324
|
+
screen.getByRole("button", { name: "Remove from SideDock" }),
|
|
325
|
+
);
|
|
267
326
|
|
|
268
327
|
expect(unpinTab).toHaveBeenCalledWith(docBrowserState.currentTab);
|
|
269
328
|
expect(pinTab).not.toHaveBeenCalled();
|
|
270
329
|
});
|
|
271
330
|
|
|
272
331
|
it("disables dock controls for built-in shortcuts", () => {
|
|
273
|
-
render(
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
332
|
+
render(
|
|
333
|
+
<DocBrowser
|
|
334
|
+
dockControls={{
|
|
335
|
+
getDockState: () => ({
|
|
336
|
+
canDock: true,
|
|
337
|
+
isDocked: true,
|
|
338
|
+
removable: false,
|
|
339
|
+
}),
|
|
340
|
+
pinTab: vi.fn(),
|
|
341
|
+
unpinTab: vi.fn(),
|
|
342
|
+
}}
|
|
343
|
+
/>,
|
|
344
|
+
);
|
|
278
345
|
|
|
279
|
-
expect(
|
|
346
|
+
expect(
|
|
347
|
+
screen.getByRole("button", { name: "Built-in shortcut" }),
|
|
348
|
+
).toHaveProperty("disabled", true);
|
|
280
349
|
});
|
|
281
350
|
|
|
282
351
|
it("renders a start page for home tabs", () => {
|
|
@@ -293,7 +362,9 @@ describe("DocBrowser", () => {
|
|
|
293
362
|
docBrowserState.activeTabId = homeTab.id;
|
|
294
363
|
docBrowserState.currentTab = homeTab;
|
|
295
364
|
|
|
296
|
-
render(
|
|
365
|
+
render(
|
|
366
|
+
<DocBrowser customTabRenderers={PANEL_APPS_DOC_BROWSER_RENDERERS} />,
|
|
367
|
+
);
|
|
297
368
|
|
|
298
369
|
expect(screen.getAllByText("Start Page").length).toBeGreaterThan(0);
|
|
299
370
|
expect(screen.getByText("Apps")).toBeTruthy();
|
|
@@ -306,10 +377,14 @@ describe("DocBrowser", () => {
|
|
|
306
377
|
|
|
307
378
|
render(<DocBrowser />);
|
|
308
379
|
|
|
309
|
-
firePointerEvent(
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
380
|
+
firePointerEvent(
|
|
381
|
+
screen.getByTestId("doc-browser-resize-right"),
|
|
382
|
+
"pointerdown",
|
|
383
|
+
{
|
|
384
|
+
clientX: 1160,
|
|
385
|
+
pointerId: 1,
|
|
386
|
+
},
|
|
387
|
+
);
|
|
313
388
|
firePointerEvent(window, "pointermove", { clientX: 1120, pointerId: 1 });
|
|
314
389
|
|
|
315
390
|
const panel = screen.getByTestId("doc-browser-panel");
|
|
@@ -322,10 +397,14 @@ describe("DocBrowser", () => {
|
|
|
322
397
|
|
|
323
398
|
render(<DocBrowser />);
|
|
324
399
|
|
|
325
|
-
firePointerEvent(
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
400
|
+
firePointerEvent(
|
|
401
|
+
screen.getByTestId("doc-browser-resize-left"),
|
|
402
|
+
"pointerdown",
|
|
403
|
+
{
|
|
404
|
+
clientX: 680,
|
|
405
|
+
pointerId: 1,
|
|
406
|
+
},
|
|
407
|
+
);
|
|
329
408
|
firePointerEvent(window, "pointermove", { clientX: 620, pointerId: 1 });
|
|
330
409
|
|
|
331
410
|
const panel = screen.getByTestId("doc-browser-panel");
|
|
@@ -338,11 +417,15 @@ describe("DocBrowser", () => {
|
|
|
338
417
|
|
|
339
418
|
render(<DocBrowser />);
|
|
340
419
|
|
|
341
|
-
firePointerEvent(
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
420
|
+
firePointerEvent(
|
|
421
|
+
screen.getByTestId("doc-browser-resize-top"),
|
|
422
|
+
"pointerdown",
|
|
423
|
+
{
|
|
424
|
+
clientX: 900,
|
|
425
|
+
clientY: 80,
|
|
426
|
+
pointerId: 1,
|
|
427
|
+
},
|
|
428
|
+
);
|
|
346
429
|
firePointerEvent(window, "pointermove", {
|
|
347
430
|
clientX: 900,
|
|
348
431
|
clientY: 120,
|