@yieldfabric/terminal 1.1.0 → 1.1.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/dist/index.cjs +41 -11
- package/dist/index.d.cts +13 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +41 -11
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -201,7 +201,7 @@ var handleSSEStream = async (url, body, onChunk, onComplete, onError, options) =
|
|
|
201
201
|
...method !== "GET" && method !== "HEAD" ? { "Content-Type": "application/json" } : {},
|
|
202
202
|
...options?.headers
|
|
203
203
|
};
|
|
204
|
-
const response = await fetch(url, {
|
|
204
|
+
const response = await (options?.fetcher ?? fetch)(url, {
|
|
205
205
|
method,
|
|
206
206
|
headers: requestHeaders,
|
|
207
207
|
body: method === "GET" || method === "HEAD" ? void 0 : JSON.stringify(body),
|
|
@@ -3296,7 +3296,7 @@ function KGScopeDropdown({ items, onSelect, compact = false }) {
|
|
|
3296
3296
|
}
|
|
3297
3297
|
function ReasoningModeSelector({ current, onChange, compact = false }) {
|
|
3298
3298
|
const t = useTheme();
|
|
3299
|
-
const isLight = t.bgRaised === "#ffffff";
|
|
3299
|
+
const isLight = t.mode ? t.mode === "light" : t.bgRaised === "#ffffff";
|
|
3300
3300
|
const isReason = current === "reason";
|
|
3301
3301
|
const label = isReason ? "Reason" : "Auto";
|
|
3302
3302
|
const title = isReason ? "Force deep reasoning \u2014 click to let the AI decide" : "AI decides \u2014 click to force deep reasoning";
|
|
@@ -3401,7 +3401,8 @@ function TerminalInputBar({
|
|
|
3401
3401
|
}, []);
|
|
3402
3402
|
const [focused, setFocused] = React15__default.default.useState(false);
|
|
3403
3403
|
const chipsVisible = focused || currentCommand.trim().length > 0;
|
|
3404
|
-
const
|
|
3404
|
+
const isTouch = typeof window !== "undefined" && "ontouchstart" in window;
|
|
3405
|
+
const isLight = t.mode ? t.mode === "light" : t.bgRaised === "#ffffff";
|
|
3405
3406
|
const hasCommand = Boolean(currentCommand.trim());
|
|
3406
3407
|
const placeholderColor = isLight ? t.textMuted : "rgba(255,255,255,0.52)";
|
|
3407
3408
|
const panelBorder = isLight ? t.border : "rgba(255,255,255,0.10)";
|
|
@@ -3441,7 +3442,7 @@ function TerminalInputBar({
|
|
|
3441
3442
|
fitToContent(input);
|
|
3442
3443
|
}, [currentCommand, inputRef]);
|
|
3443
3444
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3444
|
-
/* @__PURE__ */ jsxRuntime.jsx("style", { children: `.yf-input-bar::placeholder { color: ${placeholderColor}; }` }),
|
|
3445
|
+
/* @__PURE__ */ jsxRuntime.jsx("style", { children: `.yf-input-bar::placeholder { color: ${placeholderColor}; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }` }),
|
|
3445
3446
|
commandPaletteItems.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3446
3447
|
"div",
|
|
3447
3448
|
{
|
|
@@ -3487,10 +3488,17 @@ function TerminalInputBar({
|
|
|
3487
3488
|
className: `rounded-xl border px-3 transition-[box-shadow,border-color] sm:px-4 ${focused ? "yf-input-focused" : ""}`,
|
|
3488
3489
|
style: {
|
|
3489
3490
|
backgroundColor: panelBg,
|
|
3490
|
-
|
|
3491
|
+
// No backdrop blur on a touch screen. It is the one compositing
|
|
3492
|
+
// layer between the textarea and the page, and on iPhone the
|
|
3493
|
+
// caret was drawn a line below and a character right of where
|
|
3494
|
+
// typing lands — the caret rect resolved against the wrong
|
|
3495
|
+
// layer — while WebKit's own layout (checked in WebKit 26) puts
|
|
3496
|
+
// it on the line. The box keeps its colour; only the frosted
|
|
3497
|
+
// fabric behind it goes, which at this size is barely a change.
|
|
3498
|
+
backdropFilter: isTouch ? void 0 : "blur(18px)",
|
|
3491
3499
|
borderColor: focused ? t.primary : panelBorder,
|
|
3492
3500
|
boxShadow: focused ? `0 0 0 3px ${t.primaryMuted || "rgba(5,78,146,0.18)"}` : isLight ? "0 18px 70px -38px rgba(30,30,58,0.45)" : "0 18px 70px -38px rgba(0,0,0,0.95)",
|
|
3493
|
-
WebkitBackdropFilter: "blur(18px)"
|
|
3501
|
+
WebkitBackdropFilter: isTouch ? void 0 : "blur(18px)"
|
|
3494
3502
|
},
|
|
3495
3503
|
children: [
|
|
3496
3504
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -3594,7 +3602,22 @@ function TerminalInputBar({
|
|
|
3594
3602
|
placeholder: isExecuting ? "Processing..." : placeholder,
|
|
3595
3603
|
rows: 1,
|
|
3596
3604
|
className: "yf-input-bar flex-1 min-w-[100px] bg-transparent focus:outline-none text-[16px] leading-6 resize-none overflow-hidden sm:text-sm sm:leading-5",
|
|
3597
|
-
onFocus: () =>
|
|
3605
|
+
onFocus: () => {
|
|
3606
|
+
setFocused(true);
|
|
3607
|
+
if (!("ontouchstart" in window)) return;
|
|
3608
|
+
window.setTimeout(() => {
|
|
3609
|
+
const ta = inputRef.current;
|
|
3610
|
+
if (!ta || document.activeElement !== ta) return;
|
|
3611
|
+
let scroller = ta.parentElement;
|
|
3612
|
+
while (scroller && !/(auto|scroll)/.test(getComputedStyle(scroller).overflowY)) scroller = scroller.parentElement;
|
|
3613
|
+
window.scrollBy(0, 1);
|
|
3614
|
+
if (scroller) scroller.scrollTop += 1;
|
|
3615
|
+
window.requestAnimationFrame(() => {
|
|
3616
|
+
window.scrollBy(0, -1);
|
|
3617
|
+
if (scroller) scroller.scrollTop -= 1;
|
|
3618
|
+
});
|
|
3619
|
+
}, 400);
|
|
3620
|
+
},
|
|
3598
3621
|
onBlur: () => setFocused(false),
|
|
3599
3622
|
style: { color: t.text, caretColor: t.cursor },
|
|
3600
3623
|
spellCheck: false,
|
|
@@ -3610,12 +3633,19 @@ function TerminalInputBar({
|
|
|
3610
3633
|
disabled: !hasCommand || isExecuting || disabled,
|
|
3611
3634
|
className: "h-10 w-10 rounded-xl disabled:opacity-60 disabled:cursor-not-allowed flex items-center justify-center transition-all duration-200 shrink-0 sm:h-8 sm:w-8",
|
|
3612
3635
|
style: {
|
|
3613
|
-
backgroundColor:
|
|
3636
|
+
backgroundColor: isLight ? t.primary : hasCommand ? t.primary : "rgba(255,255,255,0.08)",
|
|
3614
3637
|
// `textInverse` is the theme's "text on a filled button".
|
|
3615
3638
|
// `bgBase` was used here before and is `transparent` in
|
|
3616
3639
|
// every app theme — so the chevron vanished the moment
|
|
3617
3640
|
// there was something to send.
|
|
3618
|
-
|
|
3641
|
+
// In LIGHT mode this is always a solid accent fill with white
|
|
3642
|
+
// on it (6.1:1). It is the app's primary verb and the single
|
|
3643
|
+
// most-pressed control; a pale disc — grey, or a 10% tint —
|
|
3644
|
+
// read as disabled even when it was not. The empty state is
|
|
3645
|
+
// already expressed by `disabled:opacity-60`, so the fill does
|
|
3646
|
+
// not also have to whisper. Dark keeps its white-alpha rest,
|
|
3647
|
+
// where a solid orange disc would shout on near-black.
|
|
3648
|
+
color: isLight ? t.textInverse : hasCommand ? t.textInverse : "rgba(255,255,255,0.72)"
|
|
3619
3649
|
},
|
|
3620
3650
|
onMouseEnter: (e) => {
|
|
3621
3651
|
if (hasCommand) {
|
|
@@ -3626,7 +3656,7 @@ function TerminalInputBar({
|
|
|
3626
3656
|
},
|
|
3627
3657
|
onMouseLeave: (e) => {
|
|
3628
3658
|
e.currentTarget.style.filter = "";
|
|
3629
|
-
e.currentTarget.style.backgroundColor =
|
|
3659
|
+
e.currentTarget.style.backgroundColor = isLight ? t.primary : hasCommand ? t.primary : "rgba(255,255,255,0.08)";
|
|
3630
3660
|
},
|
|
3631
3661
|
title: submitTitle,
|
|
3632
3662
|
"aria-label": submitTitle,
|
|
@@ -3750,7 +3780,7 @@ var KIND_DOT = {
|
|
|
3750
3780
|
};
|
|
3751
3781
|
function SlashMentionPicker({ candidates, selectedIndex, onPick }) {
|
|
3752
3782
|
const t = useTheme();
|
|
3753
|
-
const isLight = t.bgRaised === "#ffffff";
|
|
3783
|
+
const isLight = t.mode ? t.mode === "light" : t.bgRaised === "#ffffff";
|
|
3754
3784
|
if (candidates.length === 0) return null;
|
|
3755
3785
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3756
3786
|
"div",
|
package/dist/index.d.cts
CHANGED
|
@@ -21,6 +21,16 @@ import { NodeProps, EdgeProps, Node, Edge } from '@xyflow/react';
|
|
|
21
21
|
type ReasoningMode = 'reason';
|
|
22
22
|
|
|
23
23
|
interface TerminalTheme {
|
|
24
|
+
/** Which side of the light/dark divide this theme is.
|
|
25
|
+
*
|
|
26
|
+
* Components that need to branch (a placeholder tint, a panel border, a
|
|
27
|
+
* toolbar fill) used to sniff it with `t.bgRaised === '#ffffff'`. That is an
|
|
28
|
+
* exact-string test against one literal, so it silently reported DARK for
|
|
29
|
+
* any light theme whose surfaces are CSS variables, or simply an off-white
|
|
30
|
+
* — which is what happens the moment a host themes its surfaces properly.
|
|
31
|
+
* Declare it instead. Optional, and the old sniff remains the fallback, so
|
|
32
|
+
* a theme that predates this keeps its current behaviour. */
|
|
33
|
+
mode?: 'light' | 'dark';
|
|
24
34
|
primary: string;
|
|
25
35
|
primaryMuted: string;
|
|
26
36
|
primaryHover: string;
|
|
@@ -1354,6 +1364,9 @@ declare const handleSSEStream: (url: string, body: any, onChunk: (chunk: any) =>
|
|
|
1354
1364
|
method?: string;
|
|
1355
1365
|
headers?: Record<string, string>;
|
|
1356
1366
|
signal?: AbortSignal;
|
|
1367
|
+
/** Host authentication/renewal transport. It must preserve the supplied
|
|
1368
|
+
* abort signal and must not retry a request after its stream has begun. */
|
|
1369
|
+
fetcher?: (url: string, init: RequestInit) => Promise<Response>;
|
|
1357
1370
|
onGone?: (reason: string) => void;
|
|
1358
1371
|
/** Require an explicit answer terminal event. Defaults to true.
|
|
1359
1372
|
* Set false only for non-answer protocols that intentionally end at EOF.
|
package/dist/index.d.ts
CHANGED
|
@@ -21,6 +21,16 @@ import { NodeProps, EdgeProps, Node, Edge } from '@xyflow/react';
|
|
|
21
21
|
type ReasoningMode = 'reason';
|
|
22
22
|
|
|
23
23
|
interface TerminalTheme {
|
|
24
|
+
/** Which side of the light/dark divide this theme is.
|
|
25
|
+
*
|
|
26
|
+
* Components that need to branch (a placeholder tint, a panel border, a
|
|
27
|
+
* toolbar fill) used to sniff it with `t.bgRaised === '#ffffff'`. That is an
|
|
28
|
+
* exact-string test against one literal, so it silently reported DARK for
|
|
29
|
+
* any light theme whose surfaces are CSS variables, or simply an off-white
|
|
30
|
+
* — which is what happens the moment a host themes its surfaces properly.
|
|
31
|
+
* Declare it instead. Optional, and the old sniff remains the fallback, so
|
|
32
|
+
* a theme that predates this keeps its current behaviour. */
|
|
33
|
+
mode?: 'light' | 'dark';
|
|
24
34
|
primary: string;
|
|
25
35
|
primaryMuted: string;
|
|
26
36
|
primaryHover: string;
|
|
@@ -1354,6 +1364,9 @@ declare const handleSSEStream: (url: string, body: any, onChunk: (chunk: any) =>
|
|
|
1354
1364
|
method?: string;
|
|
1355
1365
|
headers?: Record<string, string>;
|
|
1356
1366
|
signal?: AbortSignal;
|
|
1367
|
+
/** Host authentication/renewal transport. It must preserve the supplied
|
|
1368
|
+
* abort signal and must not retry a request after its stream has begun. */
|
|
1369
|
+
fetcher?: (url: string, init: RequestInit) => Promise<Response>;
|
|
1357
1370
|
onGone?: (reason: string) => void;
|
|
1358
1371
|
/** Require an explicit answer terminal event. Defaults to true.
|
|
1359
1372
|
* Set false only for non-answer protocols that intentionally end at EOF.
|
package/dist/index.js
CHANGED
|
@@ -192,7 +192,7 @@ var handleSSEStream = async (url, body, onChunk, onComplete, onError, options) =
|
|
|
192
192
|
...method !== "GET" && method !== "HEAD" ? { "Content-Type": "application/json" } : {},
|
|
193
193
|
...options?.headers
|
|
194
194
|
};
|
|
195
|
-
const response = await fetch(url, {
|
|
195
|
+
const response = await (options?.fetcher ?? fetch)(url, {
|
|
196
196
|
method,
|
|
197
197
|
headers: requestHeaders,
|
|
198
198
|
body: method === "GET" || method === "HEAD" ? void 0 : JSON.stringify(body),
|
|
@@ -3287,7 +3287,7 @@ function KGScopeDropdown({ items, onSelect, compact = false }) {
|
|
|
3287
3287
|
}
|
|
3288
3288
|
function ReasoningModeSelector({ current, onChange, compact = false }) {
|
|
3289
3289
|
const t = useTheme();
|
|
3290
|
-
const isLight = t.bgRaised === "#ffffff";
|
|
3290
|
+
const isLight = t.mode ? t.mode === "light" : t.bgRaised === "#ffffff";
|
|
3291
3291
|
const isReason = current === "reason";
|
|
3292
3292
|
const label = isReason ? "Reason" : "Auto";
|
|
3293
3293
|
const title = isReason ? "Force deep reasoning \u2014 click to let the AI decide" : "AI decides \u2014 click to force deep reasoning";
|
|
@@ -3392,7 +3392,8 @@ function TerminalInputBar({
|
|
|
3392
3392
|
}, []);
|
|
3393
3393
|
const [focused, setFocused] = React15.useState(false);
|
|
3394
3394
|
const chipsVisible = focused || currentCommand.trim().length > 0;
|
|
3395
|
-
const
|
|
3395
|
+
const isTouch = typeof window !== "undefined" && "ontouchstart" in window;
|
|
3396
|
+
const isLight = t.mode ? t.mode === "light" : t.bgRaised === "#ffffff";
|
|
3396
3397
|
const hasCommand = Boolean(currentCommand.trim());
|
|
3397
3398
|
const placeholderColor = isLight ? t.textMuted : "rgba(255,255,255,0.52)";
|
|
3398
3399
|
const panelBorder = isLight ? t.border : "rgba(255,255,255,0.10)";
|
|
@@ -3432,7 +3433,7 @@ function TerminalInputBar({
|
|
|
3432
3433
|
fitToContent(input);
|
|
3433
3434
|
}, [currentCommand, inputRef]);
|
|
3434
3435
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3435
|
-
/* @__PURE__ */ jsx("style", { children: `.yf-input-bar::placeholder { color: ${placeholderColor}; }` }),
|
|
3436
|
+
/* @__PURE__ */ jsx("style", { children: `.yf-input-bar::placeholder { color: ${placeholderColor}; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }` }),
|
|
3436
3437
|
commandPaletteItems.length > 0 && /* @__PURE__ */ jsxs(
|
|
3437
3438
|
"div",
|
|
3438
3439
|
{
|
|
@@ -3478,10 +3479,17 @@ function TerminalInputBar({
|
|
|
3478
3479
|
className: `rounded-xl border px-3 transition-[box-shadow,border-color] sm:px-4 ${focused ? "yf-input-focused" : ""}`,
|
|
3479
3480
|
style: {
|
|
3480
3481
|
backgroundColor: panelBg,
|
|
3481
|
-
|
|
3482
|
+
// No backdrop blur on a touch screen. It is the one compositing
|
|
3483
|
+
// layer between the textarea and the page, and on iPhone the
|
|
3484
|
+
// caret was drawn a line below and a character right of where
|
|
3485
|
+
// typing lands — the caret rect resolved against the wrong
|
|
3486
|
+
// layer — while WebKit's own layout (checked in WebKit 26) puts
|
|
3487
|
+
// it on the line. The box keeps its colour; only the frosted
|
|
3488
|
+
// fabric behind it goes, which at this size is barely a change.
|
|
3489
|
+
backdropFilter: isTouch ? void 0 : "blur(18px)",
|
|
3482
3490
|
borderColor: focused ? t.primary : panelBorder,
|
|
3483
3491
|
boxShadow: focused ? `0 0 0 3px ${t.primaryMuted || "rgba(5,78,146,0.18)"}` : isLight ? "0 18px 70px -38px rgba(30,30,58,0.45)" : "0 18px 70px -38px rgba(0,0,0,0.95)",
|
|
3484
|
-
WebkitBackdropFilter: "blur(18px)"
|
|
3492
|
+
WebkitBackdropFilter: isTouch ? void 0 : "blur(18px)"
|
|
3485
3493
|
},
|
|
3486
3494
|
children: [
|
|
3487
3495
|
/* @__PURE__ */ jsx(
|
|
@@ -3585,7 +3593,22 @@ function TerminalInputBar({
|
|
|
3585
3593
|
placeholder: isExecuting ? "Processing..." : placeholder,
|
|
3586
3594
|
rows: 1,
|
|
3587
3595
|
className: "yf-input-bar flex-1 min-w-[100px] bg-transparent focus:outline-none text-[16px] leading-6 resize-none overflow-hidden sm:text-sm sm:leading-5",
|
|
3588
|
-
onFocus: () =>
|
|
3596
|
+
onFocus: () => {
|
|
3597
|
+
setFocused(true);
|
|
3598
|
+
if (!("ontouchstart" in window)) return;
|
|
3599
|
+
window.setTimeout(() => {
|
|
3600
|
+
const ta = inputRef.current;
|
|
3601
|
+
if (!ta || document.activeElement !== ta) return;
|
|
3602
|
+
let scroller = ta.parentElement;
|
|
3603
|
+
while (scroller && !/(auto|scroll)/.test(getComputedStyle(scroller).overflowY)) scroller = scroller.parentElement;
|
|
3604
|
+
window.scrollBy(0, 1);
|
|
3605
|
+
if (scroller) scroller.scrollTop += 1;
|
|
3606
|
+
window.requestAnimationFrame(() => {
|
|
3607
|
+
window.scrollBy(0, -1);
|
|
3608
|
+
if (scroller) scroller.scrollTop -= 1;
|
|
3609
|
+
});
|
|
3610
|
+
}, 400);
|
|
3611
|
+
},
|
|
3589
3612
|
onBlur: () => setFocused(false),
|
|
3590
3613
|
style: { color: t.text, caretColor: t.cursor },
|
|
3591
3614
|
spellCheck: false,
|
|
@@ -3601,12 +3624,19 @@ function TerminalInputBar({
|
|
|
3601
3624
|
disabled: !hasCommand || isExecuting || disabled,
|
|
3602
3625
|
className: "h-10 w-10 rounded-xl disabled:opacity-60 disabled:cursor-not-allowed flex items-center justify-center transition-all duration-200 shrink-0 sm:h-8 sm:w-8",
|
|
3603
3626
|
style: {
|
|
3604
|
-
backgroundColor:
|
|
3627
|
+
backgroundColor: isLight ? t.primary : hasCommand ? t.primary : "rgba(255,255,255,0.08)",
|
|
3605
3628
|
// `textInverse` is the theme's "text on a filled button".
|
|
3606
3629
|
// `bgBase` was used here before and is `transparent` in
|
|
3607
3630
|
// every app theme — so the chevron vanished the moment
|
|
3608
3631
|
// there was something to send.
|
|
3609
|
-
|
|
3632
|
+
// In LIGHT mode this is always a solid accent fill with white
|
|
3633
|
+
// on it (6.1:1). It is the app's primary verb and the single
|
|
3634
|
+
// most-pressed control; a pale disc — grey, or a 10% tint —
|
|
3635
|
+
// read as disabled even when it was not. The empty state is
|
|
3636
|
+
// already expressed by `disabled:opacity-60`, so the fill does
|
|
3637
|
+
// not also have to whisper. Dark keeps its white-alpha rest,
|
|
3638
|
+
// where a solid orange disc would shout on near-black.
|
|
3639
|
+
color: isLight ? t.textInverse : hasCommand ? t.textInverse : "rgba(255,255,255,0.72)"
|
|
3610
3640
|
},
|
|
3611
3641
|
onMouseEnter: (e) => {
|
|
3612
3642
|
if (hasCommand) {
|
|
@@ -3617,7 +3647,7 @@ function TerminalInputBar({
|
|
|
3617
3647
|
},
|
|
3618
3648
|
onMouseLeave: (e) => {
|
|
3619
3649
|
e.currentTarget.style.filter = "";
|
|
3620
|
-
e.currentTarget.style.backgroundColor =
|
|
3650
|
+
e.currentTarget.style.backgroundColor = isLight ? t.primary : hasCommand ? t.primary : "rgba(255,255,255,0.08)";
|
|
3621
3651
|
},
|
|
3622
3652
|
title: submitTitle,
|
|
3623
3653
|
"aria-label": submitTitle,
|
|
@@ -3741,7 +3771,7 @@ var KIND_DOT = {
|
|
|
3741
3771
|
};
|
|
3742
3772
|
function SlashMentionPicker({ candidates, selectedIndex, onPick }) {
|
|
3743
3773
|
const t = useTheme();
|
|
3744
|
-
const isLight = t.bgRaised === "#ffffff";
|
|
3774
|
+
const isLight = t.mode ? t.mode === "light" : t.bgRaised === "#ffffff";
|
|
3745
3775
|
if (candidates.length === 0) return null;
|
|
3746
3776
|
return /* @__PURE__ */ jsxs(
|
|
3747
3777
|
"div",
|